Grep(100만 개 GitHub 레포지토리 인덱싱 코드 검색 엔진)이 deprecated 된 Create React App(CRA)에서 Next.js 로 마이그레이션한 과정을 상세히 다룬다. 즉각적인 검색 반응성을 유지하면서 SSR 이점을 취하는 것이 핵심 과제였다.
핵심 포인트- FCP 3.0초 → 0.9초(70% 단축), Speed Index 4.7초 → 2.9초(38% 단축), 네트워크 요청 완료 1.49초 → 0.4초(73% 단축)
- 이중 검색바(홈 중앙·타 페이지 헤더)를 URL 쿼리 파라미터와 React Context 로 동기화해 하이드레이션 불일치 방지
- TanStack Query HydrationBoundary 로 서버 초기 패치와 클라이언트 증분 업데이트를 중복 요청 없이 이어받음
- 빠른 타이핑의 레이스 컨디션을 useOptimistic + TanStack Query 캐시 키(`["search", filters]`)로 해결
- Partial Prerendering(PPR): 정적 쉘 즉시 로드 후 동적 검색 결과 스트리밍, 기존 코드 리팩토링 불필요
- 동적 라우트 명시적 프리패치(`router.prefetch("/search?q=")`)로 첫 검색 페이지 전환 즉각성 확보
상세 정리- 마이그레이션 동기: CRA deprecated 이후 Next.js 로 전환. 코드 검색 엔진 특성상 타이핑 중 즉각적 반응성 유지가 최우선 제약
- 이중 검색바: 홈(중앙 배치)과 비홈 페이지(헤더 배치) 두 컴포넌트를 pathname 기반으로 조건 렌더링. URL 쿼리 파라미터로 상태 동기화해 레이아웃 이동·하이드레이션 불일치 없음
- 서버-클라이언트 하이브리드 패칭: /search?q=react 방문 시 RSC 가 서버에서 Promise 를 시작하고 HydrationBoundary 로 직렬화 전달. 클라이언트는 useSuspenseQuery 로 이어받아 중복 요청 없이 캐시 재활용. 이후 타이핑은 클라이언트에서 처리
- 레이스 컨디션 해결: 빠른 타이핑 시 늦게 도착한 오래된 응답이 최신 UI 를 덮어쓰는 문제. useOptimistic 으로 최신 입력 반영 고정, TanStack Query 캐시 키로 오래된 결과 버림. useDeferredValue 로 과도한 요청 디바운스
- 동적 라우트 프리패치: Next.js 는 정적 라우트만 자동 프리패치. 홈 검색 컴포넌트에서 명시적으로 router.prefetch("/search?q=") 호출해 검색 레이아웃 캐싱
- PPR: 정적 쉘을 먼저 렌더해 시각 피드백 즉시 제공, 검색 결과는 밀리초 후 스트리밍. 기존 코드 리팩토링 없이 적용 가능한 실험적 기능
- Mobile Safari: 입력 포커스 시 스크롤·줌 이상 동작을 usePreventScroll 훅으로 body overflow 잠금
- 부수 성과: 마이그레이션 기회에 검색 인덱스를 50만 → 100만 레포지토리로 확장, 클라이언트 JavaScript 번들 감소, 다크모드 추가
왜 읽나레거시 CRA 프로젝트를 Next.js 로 옮기는 팀이 실제 성능 수치와 레이스 컨디션·SSR-CSR 혼합·모바일 레이아웃 문제를 한꺼번에 참고할 수 있는 실전 마이그레이션 사례다.