RETROSPECTIVE · 회고
5년 된 CRA 사이트를 Next.js + TypeScript로 옮기며 배운 것
etc
2021년 Create React App으로 만든 이 사이트를 2026년에 Next.js App Router + TypeScript로 옮겼다. 기능은 그대로 두고 기반만 바꾸는 작업이었는데, 생각보다 걸리는 지점이 많았다.
1. 의존성은 한 번에 하나씩
Firebase SDK v8(namespaced)을 v9+ 모듈러 API로 바꾸려 했더니 CRA의 webpack 4가 새 SDK 코드를 파싱하지 못했다. react-scripts 5로 먼저 올리고, Firebase를 옮기고, 그다음 Next.js로 넘어갔다. 한 단계씩 빌드가 되는 상태를 유지한 게 결국 가장 빨랐다.
2. 라우터 state는 URL로
프로젝트 상세 페이지에 인덱스를 history.state로 넘기고 있어서 새로고침하면 빈 페이지가 됐다. /infoPage/[idx] 동적 라우트로 바꾸고 generateStaticParams로 정적 생성하니, 공유 가능한 URL과 SEO가 같이 해결됐다.
3. 정적 export의 제약
- 서버가 없으니
redirects(), 요청 시점 렌더링을 쓸 수 없다. 옛 URL 리다이렉트는 호스팅(Firebase) 설정으로 옮겼다. trailingSlash: true라usePathname()이/archive/로 들어와 메뉴 활성 표시가 깨졌다.opengraph-image.tsx의 결과물이 확장자 없는 파일로 나와서, 정적 호스팅에서 content-type 문제가 생길 수 있어 정적 PNG로 바꿨다.
4. TypeScript 전환
- JS로 남긴 공용 컴포넌트는 props 타입이 "전부 필수"로 추론돼서, 먼저 공용 컴포넌트부터 TSX로 바꿨다.
- TypeScript 6부터
types기본값이 비어 있어@types/jqueryui같은 전역 타입이 자동으로 잡히지 않는다. 필요한 파일에/// <reference types="jqueryui" />를 추가했다. - 변환이 끝난 뒤
allowJs: false로 JS가 다시 섞이지 않게 막았다.
5. 옮기면서 드러난 버그들
게임 페이지를 떠나도 계속 재생되던 BGM, 랭킹 이름을 innerHTML로 넣던 저장형 XSS, 닫히는 동안 헤더 뒤로 가려지던 모달 등 오래된 문제를 같이 정리했다. 기반을 바꾸는 작업은 기존 코드를 한 줄씩 다시 읽게 만든다는 점에서, 그 자체로 좋은 코드 리뷰였다.