← 프로젝트 회고 · etc
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의 제약

  1. 서버가 없으니 redirects(), 요청 시점 렌더링을 쓸 수 없다. 옛 URL 리다이렉트는 호스팅(Firebase) 설정으로 옮겼다.
  2. trailingSlash: true라 usePathname()이 /archive/로 들어와 메뉴 활성 표시가 깨졌다.
  3. opengraph-image.tsx의 결과물이 확장자 없는 파일로 나와서, 정적 호스팅에서 content-type 문제가 생길 수 있어 정적 PNG로 바꿨다.

4. TypeScript 전환

  1. JS로 남긴 공용 컴포넌트는 props 타입이 "전부 필수"로 추론돼서, 먼저 공용 컴포넌트부터 TSX로 바꿨다.
  2. TypeScript 6부터 types 기본값이 비어 있어 @types/jqueryui 같은 전역 타입이 자동으로 잡히지 않는다. 필요한 파일에 /// <reference types="jqueryui" />를 추가했다.
  3. 변환이 끝난 뒤 allowJs: false로 JS가 다시 섞이지 않게 막았다.

5. 옮기면서 드러난 버그들

게임 페이지를 떠나도 계속 재생되던 BGM, 랭킹 이름을 innerHTML로 넣던 저장형 XSS, 닫히는 동안 헤더 뒤로 가려지던 모달 등 오래된 문제를 같이 정리했다. 기반을 바꾸는 작업은 기존 코드를 한 줄씩 다시 읽게 만든다는 점에서, 그 자체로 좋은 코드 리뷰였다.

Gunmo Lee
5년 된 CRA 사이트를 Next.js + TypeScript로 옮기며 배운 것 | Gunmo's Dev Life