과제 체크포인트
배포 링크
바닐라: https://nemobim.github.io/front_6th_chapter4-1/vanilla/ 리액트: https://nemobim.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 (상품 상세 페이지들)
- 빌드 타임 페이지 생성
- 파일 시스템 기반 배포
구현 과정 돌아보기
가장 어려웠던 부분과 해결 과정
전체적으로 다 어려웠습니다..
- HTML을 만든다.
- 서버에서 해당 경로에 접속시 기존에 데이터를 가져와서 같이 넣어주고 HTML을 뿌린다
- CSR/SSR을 사용하는 부분을 조건부 처리해서 서버 환경에서도 돌아가도록 만든다.
라고 처음에 생각했으나 결국 연결고리를 짓는 부분이 부족한지 how 에서 막혔습니다.
왜 SSR이 안 되지?
어찌저찌 가이드를 보고 따라 가봤을때는 어떤식으로 동작하는지 콘솔도 찍어보고 테스트 해봤습니다. 그런데 코드를 구현해도 SSR이 아주 잠깐 나왔다가 사라지고 CSR 코드가 나오더라구요,,
서버: <h1>🛍️ 쇼핑몰</h1>
↓ (JavaScript 로드 후)
클라이언트: <div>기존 쇼핑몰</div>
처음에는 버그인줄 알았으나 코드를 미구현해서 그랬고, 하이드레이션 과정을 빼먹었다는 걸 알게 되었습니다.
1. SSR ≠ 단순 HTML 생성 잘못된 이해: "서버에서 HTML만 만들면 SSR 끝" 올바른 이해: "서버 HTML + 클라이언트 Hydration = 완전한 SSR" 2. Hydration의 진짜 의미 잘못된 이해: "클라이언트에서 다시 렌더링하기" 올바른 이해: "서버 HTML을 살려두고 JavaScript 기능만 붙이기"
어쩔때는 성공하고 어쩔때는 실패하는 e2e
싱글톤 라우터로 인한 동시성 문제의 심각성을 깨달았습니다. 라우터를 하나의 인스턴스로 만들어놨더니 여러 요청이 동시에 들어올 때 서로 다른 요청의 URL이 뒤섞이는 문제가 발생했습니다...클라이언트에서는 한 사용자당 하나의 라우터로 충분하지만 서버에서는 각 요청마다 별도의 라우터 인스턴스를 생성해야 상태가 섞이지 않는다는 걸 배웠습니다.
구현하면서 새롭게 알게 된 개념
-
SSR과 SSG의 차이점
- SSR은 요청할 때마다 서버에서 실시간으로 HTML을 생성
- SSG는 빌드 타임에 미리 모든 페이지를 정적 파일로 만들어두는 방식
-
Universal JavaScript 패턴
- 같은 코드가 서버와 클라이언트에서 다르게 동작해야 한다.
- typeof window === "undefined" 체크가 코드 곳곳에 필요함
- localStorage나 DOM API 같은 브라우저 전용 기능들을 서버에서 대체 필요
-
** useSyncExternalStore의 getServerSnapshot**
- React 18에서 서버-클라이언트 상태 동기화의 핵심
- 세번째 인자로 서버 환경에서의 초기 상태를 제공해야 Hydration 불일치를 방지
성능 최적화 관점에서의 인사이트
SSG로 생성된 정적 파일들을 서버 처리 과정 없이 바로 HTML 파일을 전송할 수 있어서 응답 시간이 빠르다! SSR로 초기 데이터를 미리 받으니까 사용자 경험에 좋다!
학습 갈무리
Q1. 현재 구현한 SSR/SSG 아키텍처에서 확장성을 고려할 때 어떤 부분을 개선하시겠습니까?
- 요청별 상태 격리 시스템 구축
- 현재 싱글톤 라우터로 인한 동시성 문제를 해결하기 위해 요청별로 독립적인 컨텍스트를 만드는 시스템이 필요
- 각 요청마다 격리된 라우터, 스토어 인스턴스를 생성하도록 개선
Q2. Express 서버 대신 다른 런타임(Cloudflare Workers, Vercel Edge Functions 등)을 사용한다면 어떤 점을 수정해야 할까요?
모르겠어요...AI 돌렸습니다.
- 파일 시스템 접근 제거: 현재 템플릿 파일을 fs.readFile로 읽어오는데, 엣지 환경에서는 파일 시스템이 없으므로 템플릿을 환경변수나 KV 스토리지에 저장하거나, 번들에 인라인으로 포함시켜야 합니다.
- Cold Start 최적화: 번들 크기를 최소화하기 위해 dynamic import를 적극 활용하고, 자주 사용되는 코드만 메인 번들에 포함시키겠습니다. 또한 워커 인스턴스 간 상태 공유가 불가능하므로 모든 상태를 요청 스코프로 관리해야 합니다.
- Web API 호환성: Node.js 전용 모듈들(fs, path 등)을 Web API 기반으로 대체해야 합니다. Buffer 대신 Uint8Array, require 대신 import를 사용하고, 서버 환경 감지도 typeof process !== 'undefined' 방식으로 변경해야 합니다.
Q3. 현재 구현에서 성능 병목이 될 수 있는 지점은 어디이고, 어떻게 개선하시겠습니까?
- renderToString CPU 병목:
- 현재 동기적 렌더링으로 인해 CPU 사용량이 높은데 Worker Thread를 활용한 병렬 렌더링이나 렌더링 결과 캐싱을 도입 -자주 요청되는 페이지는 Redis에 렌더링 결과를 저장하여 CPU 부하를 줄이기
- 메모리 누수 방지:
- 싱글톤 패턴으로 인한 메모리 누적 문제를 해결하기 위해 요청 완료 후 명시적으로 인스턴스를 정리하는 가비지 컬렉션 트리거를 추가
- 대용량 데이터 처리 시 스트리밍 방식을 도입하여 메모리 사용량을 제어
- 정규식 라우터 최적화
Q4. 1000개 이상의 상품 페이지를 SSG로 생성할 때 고려해야 할 사항은 무엇입니까?
- 메모리 사용량 관리
- 한 번에 1000개를 다 만들려고 하면 컴퓨터 메모리가 부족하다.
- 100개씩 나누어서 만들고, 100개 만들 때마다 메모리를 비워주는 방식으로 진행
- 순차 처리 방식을 Worker Thread 기반 병렬 배치 처리로 개선
- 증분 빌드 시스템
- 변경된 상품만 재생성하는 시스템을 구축
- 상품 데이터의 해시값을 저장해두고 변경 감지 시에만 해당 페이지를 재빌드하여 전체 빌드 시간을 대폭 단축
- CDN 캐시 무효화 전략
- 상품별로 태그를 부여하여 특정 상품 변경 시 해당 페이지만 선별적으로 캐시를 무효화
Q5. Hydration 과정에서 사용자가 느낄 수 있는 UX 이슈는 무엇이고, 어떻게 개선할 수 있을까요?
- 인터랙션 차단 문제
- 전체 앱이 Hydration 완료될 때까지 모든 버튼이 비활성화
- React 18의 Selective Hydration을 도입하여 우선순위가 높은 컴포넌트(장바구니 버튼, 검색 등)부터 순차적으로 활성화 필요
- 로딩 상태 시각화
- Hydration 진행 상태를 사용자가 알 수 있도록 스켈레톤 UI와 프로그레시브 로딩 인디케이터를 추가
Q6. 이번 과제에서 학습한 내용을 실제 프로덕션 환경에 적용할 때 추가로 고려해야 할 사항은?
- 얼마나 빠른지, 에러가 얼마나 나는지 계속 지켜보기.. 모니터링
- window.__INITIAL_DATA__에 사용자 입력 데이터가 포함될 때 XSS 공격을 방지하기 위한 HTML 이스케이핑과 데이터 sanitization이 필수
- Content Security Policy(CSP) 설정으로 외부 스크립트 실행을 제한하고 서버 에러 시 스택 트레이스나 민감한 정보가 클라이언트에 노출되지 않도록 에러 응답을 필터링
Q7. Next.js 같은 프레임워크 대신 직접 구현한 SSR/SSG의 장단점은 무엇인가요?
장점 - 내 마음대로 할 수 있음: 원하는 대로 정확히 만들 수 있습니다. 그리고 문제 생겼을 때 어디가 잘못됐는지 알 수 있어요.
단점 - 너무 힘들고 시간 많이 걸림: 개발 및 유지보수 비용이 매우 높습니다. Next.js가 자동으로 처리해주는 코드 스플리팅, 이미지 최적화, 자동 Static 최적화 등을 모두 직접 구현해야 하고 새로운 React 버전이나 웹 표준 변화에 대응하는 부담도 큼...
결론: 학습 목적이나 매우 특수한 요구사항이 있는 경우가 아니라면 Next.js 같은 검증된 프레임워크를 사용하는 것이 좋다. 다만 이번 구현 경험을 통해 Next.js를 사용할 때도 내부 동작을 이해하고 더 효과적으로 활용할 수 있게 된거같다(?)
Q8. Next.js 를 이용하여 SSG 방식으로 배포하려면 어떻게 해야 좋을까요?
- 인기 상품만 미리 만들고 나머지는 사용자가 처음 방문할 때 만들도록 설정
- 나머지는 fallback: 'blocking'으로 설정하여 사용자가 처음 방문할 때 만들도록 한다.
- ISR(Incremental Static Regeneration) 활용
- 옵션을 설정하여 페이지를 주기적으로 업데이트할 수 있다. 상품 정보는 하루에 한 번, 가격 정보는 1시간마다 갱신
코드 품질 향상
자랑하고 싶은 구현
솔직히 자랑할 만한 코드는 없었습니다... 과제 완성에 급급해서 "일단 돌아가게만 만들자"는 마음으로 구현했습니다..
개선하고 싶은 부분
-
싱글톤 패턴 라우터의 동시성 문제: -가장 큰 문제였던 싱글톤 라우터를 요청별 인스턴스로 변경
- 현재는 여러 요청이 들어올 때 라우터 상태가 뒤섞여서 A 사용자가 B 사용자의 페이지를 보는 심각한 버그가 발생합니다.
-
any 타입 남발:
- 시간에 쫓겨서 타입 정의를 제대로 하지 못하고 any로 때려박은 부분들이 많다
-
중복된 Mock 코드:
-
기존 API와 동일한 인터페이스를 유지하기 위해 serverMock을 따로 만들었는데 코드 중복이 심하고 유지보수가 어려움
-
추상화를 통해 공통 로직 뽑아내기
리팩토링 계획
- 과제 하느라 촉박해서 타입을 any 로 때려박은 부분이있음
- mock을 사용하기 위해 기존코드와 동일한 serverMock을 따로 만듦..
학습 연계
다음 학습 목표
있는거나 잘하겠습니다..!
실무 적용 계획
CSR며이며 데이터가 잘 바뀌지 않는 프로젝트를 찾아 SSR로 처리해보자..1
리뷰 받고 싶은 내용
- 서버와 클라이언트에서 같은 코드를 실행하면서 환경별 분기 처리가 너무 많아졌는데, 더 깔끔하게 추상화할 수 있는 패턴이 있는지 궁금합니다.
- 학습연계로 뭘하면 좋을까요
과제 피드백
안녕하세요 도은님!! 우겨곡절 끝에 결국 해내셨군요 ㅎㅎ 고생하셨습니다!!
싱글톤 라우터로 인한 동시성 문제의 심각성을 깨달았습니다. 라우터를 하나의 인스턴스로 만들어놨더니 여러 요청이 동시에 들어올 때 서로 다른 요청의 URL이 뒤섞이는 문제가 발생했습니다...클라이언트에서는 한 사용자당 하나의 라우터로 충분하지만 서버에서는 각 요청마다 별도의 라우터 인스턴스를 생성해야 상태가 섞이지 않는다는 걸 배웠습니다.
맞아요.. 이게 과제여서 다행이라고 생각합니다 ㅎㅎ 실제 서비스에서 동일한 문제가 발생한다면 정말 심각한 문제가 될 수 있어요 ㅠㅠ 다른 사용자의 개인정보를 갑자기 렌더링 한다거나, 의도치않은 계정 탈취를 한다거나...? 이런 류의 문제가 발생할 수 있답니다.
서버와 클라이언트에서 같은 코드를 실행하면서 환경별 분기 처리가 너무 많아졌는데, 더 깔끔하게 추상화할 수 있는 패턴이 있는지 궁금합니다.
이 부분은 과제 솔루션을 참고해보시면 좋을 것 같아요! 환경별 분기처리는 아마 window와 연관된 부분 (router, storage 등) 일텐데, 별도의 구현체를 만들어서 사용하면 될 것 같아요. 보통 "전략 패턴" 이라고 하는데요, 인터페이스는 동일하고 각 인터페이스에 대한 구현체가 여러개 있어서 바꿔낄 수 있도록 하는거죠!
학습연계로 뭘하면 좋을까요
NextJS 만들어보기 시도해보는거 추천드립니다 ㅋㅋ 아니면 저의 항해 블로그에 기여해본다거나!? SEO 관련 이슈가 추가되어있는 상태랍니다 ㅎㅎ