[1팀 천진아] Chapter 4-1 성능최적화: SSR, SSG, Infra#21
Open
totter15 wants to merge 16 commits intohanghae-plus:mainfrom
Open
[1팀 천진아] Chapter 4-1 성능최적화: SSR, SSG, Infra#21totter15 wants to merge 16 commits intohanghae-plus:mainfrom
totter15 wants to merge 16 commits intohanghae-plus:mainfrom
Conversation
added 15 commits
December 16, 2025 00:30
- Express 서버에 Vite SSR 미들웨어 통합 - 홈, 상품 상세, 404 페이지 SSR 렌더링 구현
- ServerRouter 클래스 추가: SSR 환경에서 라우팅 지원 - createStorage에 더미 스토리지 추가: localStorage 없는 환경 대응 - router.js에서 환경 감지하여 적절한 Router 인스턴스 생성 - withLifecycle에서 SSR 환경 마운트 방지 - main-server.js 리팩토링: 하드코딩된 URL 처리를 라우팅 시스템으로 전환
- MSW server 인스턴스 추가 (mocks/server.js) - API 핸들러를 와일드카드 패턴으로 변경 (*/api/...) - SSR과 CSR 환경 모두 지원
- getBaseUrl 유틸리티 추가 - CSR: 상대 경로, SSR: 절대 경로(localhost:5173) - productApi에 환경별 baseUrl 적용
- HomePage에 loader 함수 추가 - main-server에서 MSW 서버 시작 및 loader 호출 - 서버에서 페이지 렌더링 전 데이터 미리 로드 - serversideProps로 데이터 전달
- __INITIAL_DATA__에서 서버 데이터 복원 - 서버 데이터가 있으면 불필요한 재요청 방지 - productStore에 초기 상태 주입
- nodemon 추가 (자동 재시작) - 의존성 버전 업데이트 (vitest, playwright, react 등)
- ProductDetailPage에 loader 함수 추가 - 상품 상세 및 관련 상품 데이터 서버에서 프리페칭 - getRelatedProducts 함수 분리 (서버/클라이언트 공용) - currentProduct, relatedProducts hydration 지원
SSR 라우터 개선: - 각 요청마다 독립적인 ServerRouter 인스턴스 생성 - loader 함수에 serverRouter 전달하여 의존성 제거 - 페이지 컴포넌트에 serverRouter/clientRouter 명확히 구분 데이터 구조 개선: - loader 반환값 표준화 (data, title) - head에 페이지별 title 동적 생성 - 환경변수 PORT 지원 버그 수정: - HomePage API 응답 매핑 수정 (count → total) - ProductDetailPage 안전한 옵셔널 체이닝 적용
SSG 구현:
- static-site-generate.js에 실제 SSG 로직 추가
- MSW 서버를 활용한 데이터 프리페칭
- 홈, 404, 상품 상세 페이지 자동 생성
- __INITIAL_DATA__ 주입으로 hydration 지원
ESM 호환성:
- 모든 import 경로에 .js 확장자 명시
- JSON import에 with { type: json } 사용
- MemoryRouter로 SSR 라우팅 지원 - React Server-Side Rendering 구현 - 서버 데이터 클라이언트 hydration - 페이지별 데이터 프리페칭 (loader) - MSW를 활용한 SSR API 모킹
라우터 전달: - main-server에서 serverRouter를 컴포넌트에 전달 - HomePage, ProductDetailPage에 serverRouter prop 추가 - SearchBar에서 serverRouter의 쿼리 파라미터 직접 사용 ProductDetailPage 개선: - loader 함수 추가 (데이터 프리페칭) - serversideProps로 서버 데이터 전달 - ProductDetail 컴포넌트에 serversideProps 전달 쿼리 파라미터 처리: - SSR 환경: serverRouter.query 직접 사용 - CSR 환경: useProductFilter hook 사용 - 조건부로 적절한 소스 선택
서버 개선: - 불필요한 import 제거 (renderToString, createElement) - 주석 처리된 코드 제거 - 템플릿 경로 수정 (dist/client -> dist/react) 라우터 개선: - SSR 환경에서 base URL을 빈 문자열로 설정 - server.js에서 이미 base 제거되므로 순수 경로만 사용 - URL 정규화 추가 (빈 문자열 처리)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
과제 체크포인트
배포 링크
https://totter15.github.io/front_7th_chapter4-1/vanilla/
https://totter15.github.io/front_7th_chapter4-1/react/
기본과제 (Vanilla SSR & SSG)
Express SSR 서버
<!--app-html-->,<!--app-head-->)서버 사이드 렌더링
클라이언트 Hydration
window.__INITIAL_DATA__스크립트 주입Static Site Generation
심화과제 (React SSR & SSG)
React SSR
renderToString서버 렌더링React Hydration
Static Site Generation
아하! 모먼트 (A-ha! Moment)
SSR/SSG
SSR이란?
클라이언트요청 -> 서버에서 HTML생성 -> 클라이언트에응답
SSG란?
빌드시점에 HTML을 만들어서 배포. 이후 클라이언트에 해당 빌드 응답.
SSR동작 과정
1. 클라이언트 요청
↓
2. Express 서버 (server.js)
-comporession: HTTP응답 압축으로 전송데이터 크기감소.
-app.use(compression()): 모든 응답에 압축적용
-sirv: 정적 파일 서빙 미들웨어.
-app.use(base, sirv("./dist/vanilla", { extensions: [] })): base-기본경로, ./dist/vanilla-사용할 정적 파일 디렉토리, { extensions: [] }-확장자 자동추가 비활성화. ['html']로 설정시 /about요청시 /about.html을 찾음. SSR에서 HTML은 동적으로 생성되므로 정적 HTML파일을 자동으로 찾지 않도록함.
3. URL 파싱 및 base 제거
↓
4. main-server.js의 render() 호출
↓
5. ServerRouter로 라우트 매칭
↓
6. PageComponent.loader() 실행 (데이터 프리패칭)
↓
7. PageComponent() 호출 (HTML 생성)
↓
8. 템플릿에 주입 및 응답
↓
9. 클라이언트 Hydration
자유롭게 회고하기
진행중 마주친 몇가진 문제 혹은 고민들
server에서 사용될 router 문제
일단 Router함수를 그대로 복사해서 ServerRouter로 만들어주었고, server에서 사용되지 않는 부분들만 수정해주었습니다.
router.push관련 삭제 -> SSR에서는 불필요.
query관련 수정 -> SSR에서 동적으로 query를 받을 필요는 없기에 setUrl로 요청된 url의 query값만 parsing해서 관리해주었습니다.
main-server.js에서 render시 어떤걸 render하면 좋을까?
=> 중복코딩을 막기위해서 2번대로 page컴포넌트를 그대로 가져와주었습니다. serverSide 환경에서 해당 컴포넌트들이 rendering될때 storage나 router에서 client에서만 사용가능한 값들에 대한 예외처리를 추가로 해주었습니다.
아래는 이 결정으로 인해 발생된 또다른 문제들...
withLifecycle에서 mount처리 추가
vanill환경에서 mount는 react의 useEffect와 동일하다 생각해서 server환경에서는 mount가 되지 않게 처리해주었습니다.
prefetching은 어떻게 하지?
prefetching을 위한 함수가 각 페이지별로 있는데 이를 위해 각페이지 컴포넌트에 loader함수를 추가해주었습니다.
loader에서 title,과 data를 return해서 main-server.js에서 컴포넌트 렌더링시 serversideProps로 데이터를 주입받을 수 있게 만들어주었습니다.
hydration은 어떻게 하지?
prefeching한 데이터를
<header><script>window__INITIAL_DATA__=DATA</script></header>에 넣습니다.client가 실행되는 main에서 window.__INITIAL_DATA__가 있을경우, store에 저장하는 로직을 추가해줍니다.
SSG와 SSR을 서버에서 구분을 어떻게 하지?
서버가 SSR/SSG를 판단하는게 아니라 실행하는 서버 프로그램이 다르다.
SSR: Express(server.js)
SSG: Vite preivew
리뷰 받고 싶은 내용
server와 client에서 render될 컴포넌트를 만들때 저는 client에서 사용되고있던 HomePage, ProductDetailPage를 그대로 와서 server용으로 사용하기 위해 예외처리를 추가했습니다. 이런방식보다 HomePage에서 사용되고있는 html부분만 따로 빼서 컴포넌트를 만들고, 이를 server, client두곳에서 사용하도록 하는게 더 좋은 방법 같기도 합니다. 하지만 react에서는 순수하게 html부분만 컴포넌트로 빼기에는 컴포넌트 내부에서 동작하는 store함수등이 있어서 이를 수정하려면 기존코드를 많이 고쳐야 할것 같습니다. 어떤 방법이 좋은 방식일까요?