과제 체크포인트
배포 링크
기본 배포: https://amelia-shin.github.io/front_6th_chapter4-1/vanilla/ 심화 배포: https://amelia-shin.github.io/front_6th_chapter4-1/react/
기본과제 (Vanilla SSR & SSG)
Express SSR 서버
- Express 미들웨어 기반 서버 구현
- 개발/프로덕션 환경 분기 처리
- HTML 템플릿 치환 (
<!--app-html-->,<!--app-head-->)
서버 사이드 렌더링
- 서버에서 동작하는 Router 구현
- 서버 데이터 프리페칭 (상품 목록, 상품 상세)
- 서버 상태관리 초기화
클라이언트 Hydration
-
window.__INITIAL_DATA__스크립트 주입 - 클라이언트 상태 복원
- 서버-클라이언트 데이터 일치
Static Site Generation
- 동적 라우트 SSG (상품 상세 페이지들)
- 빌드 타임 페이지 생성
- 파일 시스템 기반 배포
심화과제 (React SSR & SSG)
React SSR
-
renderToString서버 렌더링 - TypeScript SSR 모듈 빌드
- Universal React Router (서버/클라이언트 분기)
- React 상태관리 서버 초기화
React Hydration
- Hydration 불일치 방지
- 클라이언트 상태 복원
Static Site Generation
- 동적 라우트 SSG (상품 상세 페이지들)
- 빌드 타임 페이지 생성
- 파일 시스템 기반 배포
구현 과정 돌아보기
가장 어려웠던 부분과 해결 과정
구현하면서 새롭게 알게 된 개념
ssrLoadModule을 왜 사용하는가?
SSR과 SSG 차이점
데이터 하이드레이션(Hydration) 왜 사용하는가?
-
서버사이드 렌더링(SSR) 아키텍처 설계 CSR에서 SSR로의 전환: 초기 클라이언트 렌더링 방식에서 서버사이드 렌더링으로 점진적 발전 하이브리드 라우팅: CSR과 SSR을 상황에 따라 분리하여 사용하는 방법 학습 서버 환경과 클라이언트 환경의 차이점: window 객체, DOM API 등 브라우저 전용 기능 처리 방법
-
정적 사이트 생성(SSG) 구현
// static-site-generate.js에서 구현한 SSG
const { products } = await getProducts();
generateStaticSite("/", {});
generateStaticSite("/404", {});
for (let i = 0; i < products.length; i++) {
generateStaticSite(`/product/${products[i].productId}`, {});
}
빌드 타임 사전 렌더링: 모든 상품 페이지를 빌드 시점에 미리 생성 성능 최적화: 런타임 계산 없이 정적 파일로 서빙
- 데이터 하이드레이션(Hydration) 패턴
window.__INITIAL_DATA__ = ${JSON.stringify(rendered.initialData || {})};
서버-클라이언트 데이터 동기화: 서버에서 렌더링된 데이터를 클라이언트에서 재사용 FOUC(Flash of Unstyled Content) 방지: 초기 데이터 주입으로 깜빡임 현상 제거
- MSW(Mock Service Worker) 활용 개발 환경과 프로덕션 환경 분리: 개발시에만 MSW 서버 데이터 사용 API 모킹: 실제 백엔드 없이도 완전한 기능 개발 가능
성능 최적화 관점에서의 인사이트
학습 갈무리
Q1. 현재 구현한 SSR/SSG 아키텍처에서 확장성을 고려할 때 어떤 부분을 개선하시겠습니까?
Q2. Express 서버 대신 다른 런타임(Cloudflare Workers, Vercel Edge Functions 등)을 사용한다면 어떤 점을 수정해야 할까요?
Q3. 현재 구현에서 성능 병목이 될 수 있는 지점은 어디이고, 어떻게 개선하시겠습니까?
Q4. 1000개 이상의 상품 페이지를 SSG로 생성할 때 고려해야 할 사항은 무엇입니까?
Q5. Hydration 과정에서 사용자가 느낄 수 있는 UX 이슈는 무엇이고, 어떻게 개선할 수 있을까요?
Q6. 이번 과제에서 학습한 내용을 실제 프로덕션 환경에 적용할 때 추가로 고려해야 할 사항은?
Q7. Next.js 같은 프레임워크 대신 직접 구현한 SSR/SSG의 장단점은 무엇인가요?
Q8. Next.js 를 이용하여 SSG 방식으로 배포하려면 어떻게 해야 좋을까요?
코드 품질 향상
자랑하고 싶은 구현
핵심 구현 기술
- Universal JavaScript 패턴
// 서버/클라이언트 환경 분기 처리
if (typeof window === "undefined") {
const { getProductsSSR } = await import("../lib/serverRouter.js");
return getProductsSSR(params);
}
- 하이드레이션(Hydration) 구현
// 서버 데이터를 클라이언트 스토어에 복원
if (typeof window !== "undefined" && window.__INITIAL_DATA__) {
productStore.dispatch({ type: PRODUCT_ACTIONS.SETUP, ...data });
}
- 서버사이드 스토리지 추상화
// localStorage 대체용 서버 스토리지
const createServerStorage = () => {
const storage = new Map();
return { getItem, setItem, removeItem };
};
개선하고 싶은 부분
리팩토링 계획
학습 연계
다음 학습 목표
실무 적용 계획
리뷰 받고 싶은 내용
과제 피드백
수고했습니다. 이번 과제는 SSR과 SSG를 직접 구현해보면서 렌더링 전략의 차이점과 성능 최적화 방법을 체험하는데 목적이 있었습니다.
Universal JavaScript 패턴을 구현해서 서버/클라이언트 환경 분기 처리한 부분 잘했어요. typeof window === "undefined"로 환경을 구분하고 각각에 맞는 모듈을 동적으로 import하는 방식으로 구현하는 방식 잘했습니다. window.__INITIAL_DATA__를 통해 서버 데이터를 클라이언트 스토어에 복원하는 패턴으로 SSR과 하이드레이션 구현도 잘했네요.
서버사이드 스토리지를 Map으로 추상화한 아이디어도 괜찮네요. localStorage가 없는 서버 환경에서 대체재로 쓸 수 있겠어요.
그래도 SSR/SSG의 핵심 개념들을 직접 구현해보면서 왜 이런 패턴들이 필요한지 이해하셨을 거라 생각해요. 이런 경험이 나중에 프레임워크 활용할 때도 도움이 될 거예요.
다음 과제도 화이팅입니다!