Skip to content

Commit 674643a

Browse files
committed
집가서마저ㅇ
1 parent 4d35094 commit 674643a

File tree

16 files changed

+804
-64
lines changed

16 files changed

+804
-64
lines changed

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- 노드는 ㅂ무조건 22버전으로 실행! nvm use

docs/msw-server-integration.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# MSW 서버 환경 통합 가이드
2+
3+
## 개요
4+
5+
본 프로젝트는 서버사이드 렌더링(SSR) 환경에서도 MSW(Mock Service Worker)를 활용하여 일관된 API 모킹 환경을 구성하고 있습니다. 이 문서는 왜 서버 환경을 고려한 MSW를 구축했는지, 그리고 어떻게 동작하는지 설명합니다.
6+
7+
## 왜 서버용 MSW가 필요한가?
8+
9+
### 1. SSR에서의 API 호출 필요성
10+
11+
SSR 환경에서는 초기 페이지 렌더링 시 서버에서도 데이터를 가져와야 합니다:
12+
13+
```javascript
14+
// server.js
15+
const rendered = await render(url, req.query);
16+
const html = template
17+
.replace(`<!--app-html-->`, rendered.html ?? "")
18+
.replace(`<!--app-data-->`,
19+
`<script>window.__INITIAL_DATA__ = ${JSON.stringify(rendered.data)};</script>`
20+
);
21+
```
22+
23+
### 2. 백엔드 의존성 제거
24+
25+
실제 백엔드 API 서버 없이도 풀스택 개발이 가능하도록:
26+
- 개발 환경에서 즉시 시작 가능
27+
- 백엔드 개발과 독립적으로 프론트엔드 개발 진행
28+
- 테스트 환경에서 안정적인 데이터 제공
29+
30+
### 3. 클라이언트-서버 데이터 일관성
31+
32+
동일한 MSW 핸들러를 클라이언트와 서버에서 공유하여:
33+
- 브라우저에서의 fetch와 서버에서의 fetch가 동일한 응답 받음
34+
- 하이드레이션 과정에서 데이터 불일치 방지
35+
- 개발 환경의 예측 가능성 향상
36+
37+
## 구현 구조
38+
39+
### 1. MSW 핸들러 설정
40+
41+
```javascript
42+
// src/mocks/handlers.js
43+
export const handlers = [
44+
// 와일드카드 패턴으로 서버에서의 절대 URL도 매칭
45+
http.get("*/api/products", async ({ request }) => {
46+
const url = new URL(request.url);
47+
// 쿼리 파라미터 처리 로직
48+
return HttpResponse.json(filteredData);
49+
}),
50+
51+
http.get("*/api/products/:id", ({ params }) => {
52+
// 상품 상세 정보 반환
53+
}),
54+
55+
http.get("*/api/categories", async () => {
56+
// 카테고리 목록 반환
57+
})
58+
];
59+
```
60+
61+
**주요 변경사항:**
62+
- `/api/products``*/api/products`: 서버에서 절대 URL 호출 시에도 매칭
63+
- JSON imports with assertion: `import items from "./items.json" with { type: "json" }`
64+
65+
### 2. 서버용 MSW 설정
66+
67+
```javascript
68+
// src/mocks/node.js
69+
import { setupServer } from "msw/node";
70+
import { handlers } from "./handlers.js";
71+
72+
export const mswServer = setupServer(...handlers);
73+
```
74+
75+
### 3. 서버 통합
76+
77+
```javascript
78+
// server.js
79+
import { mswServer } from "./src/mocks/node.js";
80+
81+
// MSW 서버 시작
82+
mswServer.listen({
83+
onUnhandledRequest: "bypass", // 모킹되지 않은 요청은 그대로 통과
84+
});
85+
86+
// Express 서버 시작
87+
app.listen(port, () => {
88+
console.log(`Vanilla Server started at http://localhost:${port}`);
89+
});
90+
```
91+
92+
## 데이터 플로우
93+
94+
```mermaid
95+
sequenceDiagram
96+
participant Browser
97+
participant SSR Server
98+
participant MSW Server
99+
participant Express App
100+
101+
Browser->>Express App: GET / (페이지 요청)
102+
Express App->>SSR Server: render(url, query)
103+
SSR Server->>MSW Server: fetch("/api/products")
104+
MSW Server->>SSR Server: 모킹된 데이터 응답
105+
SSR Server->>Express App: { html, head, data }
106+
Express App->>Browser: 완성된 HTML + window.__INITIAL_DATA__
107+
108+
Note over Browser: 클라이언트에서 하이드레이션
109+
Browser->>MSW Server: fetch("/api/products") (클라이언트 요청)
110+
MSW Server->>Browser: 동일한 모킹된 데이터
111+
```
112+
113+
## 장점
114+
115+
### 1. 개발 생산성
116+
- 백엔드 API 개발 완료를 기다리지 않고 프론트엔드 개발 진행
117+
- 로컬 환경에서 즉시 실행 가능한 풀스택 애플리케이션
118+
119+
### 2. 테스트 안정성
120+
- 외부 API 의존성 없는 안정적인 테스트 환경
121+
- 일관된 테스트 데이터로 예측 가능한 테스트 결과
122+
123+
### 3. 개발 환경 일관성
124+
- 서버와 클라이언트에서 동일한 API 응답
125+
- SSR과 CSR 간의 데이터 불일치 문제 해결
126+
127+
## 주의사항
128+
129+
### 1. URL 패턴 매칭
130+
서버에서는 절대 URL로 API를 호출하므로 핸들러에서 와일드카드 패턴 사용:
131+
```javascript
132+
// ❌ 서버에서 동작하지 않음
133+
http.get("/api/products", ...)
134+
135+
// ✅ 서버와 클라이언트 모두 동작
136+
http.get("*/api/products", ...)
137+
```
138+
139+
### 2. MSW 서버 생명주기
140+
- Express 서버 시작 전에 MSW 서버를 먼저 시작
141+
- 애플리케이션 종료 시 MSW 서버도 적절히 정리
142+
143+
### 3. 환경 분리
144+
프로덕션 환경에서는 MSW를 비활성화하고 실제 API 사용:
145+
```javascript
146+
if (process.env.NODE_ENV !== "production") {
147+
mswServer.listen({ onUnhandledRequest: "bypass" });
148+
}
149+
```
150+
151+
## 결론
152+
153+
서버용 MSW 통합을 통해 개발 환경에서 완전한 풀스택 애플리케이션을 구축할 수 있으며, SSR과 CSR 간의 데이터 일관성을 보장할 수 있습니다. 이는 특히 백엔드 개발과 독립적으로 프론트엔드 개발을 진행해야 하는 상황에서 매우 유용한 접근 방식입니다.

docs/ssr-hydration-guide.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# SSR Hydration Guide
2+
3+
## window.**INITIAL_DATA**의 역할
4+
5+
`window.__INITIAL_DATA__`는 서버사이드 렌더링(SSR)과 클라이언트사이드 하이드레이션(Hydration) 사이의 데이터 동기화를 담당하는 핵심 메커니즘입니다.
6+
7+
## 동작 원리
8+
9+
### 1. 서버사이드 렌더링
10+
11+
```javascript
12+
// main-server.js
13+
async function renderHomePage(query) {
14+
await loadProductsAndCategories();
15+
const initialData = productStore.getState();
16+
17+
setServerData({
18+
pathname: "/",
19+
query,
20+
params: {},
21+
data: initialData,
22+
});
23+
24+
return {
25+
head: `<title>쇼핑몰</title>`,
26+
html: HomePage(), // 서버에서 HTML 생성
27+
data: initialData, // 클라이언트로 전달될 데이터
28+
};
29+
}
30+
```
31+
32+
### 2. HTML에 데이터 주입
33+
34+
서버 응답에서 HTML에 다음과 같은 스크립트 태그가 포함됩니다:
35+
36+
```html
37+
<script>
38+
window.__INITIAL_DATA__ = {
39+
products: [...],
40+
categories: [...],
41+
loading: false,
42+
error: null
43+
};
44+
</script>
45+
```
46+
47+
### 3. 클라이언트 하이드레이션
48+
49+
```javascript
50+
// withServer.js
51+
export const withServer = (options, page) => {
52+
const pageWithSSR = () => {
53+
const pageParams = isServer()
54+
? currentServerData // 서버: 설정된 데이터 사용
55+
: {
56+
pathname: window.location.pathname,
57+
query: routerInstance.query,
58+
params: routerInstance.params,
59+
data: window.__INITIAL_DATA__, // 클라이언트: 주입된 데이터 사용
60+
};
61+
return page(pageParams);
62+
};
63+
64+
return pageWithSSR;
65+
};
66+
```
67+
68+
## 하이드레이션 과정
69+
70+
1. **서버**: 초기 데이터로 정적 HTML 생성
71+
2. **브라우저**: HTML 로드 및 `window.__INITIAL_DATA__` 파싱
72+
3. **JavaScript**: 같은 초기 데이터로 컴포넌트 활성화
73+
4. **결과**: 서버 HTML과 클라이언트 렌더링이 일치하여 매끄러운 하이드레이션
74+
75+
## 주요 이점
76+
77+
### ✅ 데이터 일관성
78+
79+
- 서버와 클라이언트가 동일한 초기 상태를 가짐
80+
- Hydration mismatch 방지
81+
82+
### ✅ 성능 최적화
83+
84+
- 초기 페이지 로드 시 추가 API 호출 불필요
85+
- 사용자에게 즉시 완전한 페이지 표시
86+
87+
### ✅ SEO 친화적
88+
89+
- 서버에서 완전한 HTML 제공
90+
- 검색 엔진이 모든 콘텐츠 인덱싱 가능
91+
92+
## 코드 예시
93+
94+
### 페이지 컴포넌트에서의 사용
95+
96+
```javascript
97+
const HomePageComponent = ({ query, data }) => {
98+
// 서버: data 파라미터에서 초기 데이터 사용
99+
// 클라이언트: window.__INITIAL_DATA__에서 자동으로 가져온 데이터 사용
100+
const productState = data || productStore.getState();
101+
102+
return PageWrapper({
103+
children: `
104+
${SearchBar({ ...query, categories: productState.categories })}
105+
${ProductList({ products: productState.products })}
106+
`,
107+
});
108+
};
109+
```
110+
111+
### 라이프사이클에서의 활용
112+
113+
```javascript
114+
export const HomePage = withServer(
115+
{},
116+
withLifecycle(
117+
{
118+
onMount: () => {
119+
// 클라이언트에서만 실행
120+
const initialData = window.__INITIAL_DATA__;
121+
console.log("하이드레이션 데이터:", initialData);
122+
123+
// 필요시 추가 데이터 로드
124+
loadProductsAndCategories();
125+
},
126+
},
127+
HomePageComponent,
128+
),
129+
);
130+
```
131+
132+
## 주의사항
133+
134+
- `window.__INITIAL_DATA__`는 클라이언트에서만 사용 가능
135+
- 서버 환경에서는 `setServerData()`로 설정된 데이터 사용
136+
- 초기 데이터 사용 후 `delete window.__INITIAL_DATA__`로 메모리 정리 권장

0 commit comments

Comments
 (0)