Next.js 16으로 블로그 마이그레이션하며 배운 것들
Next.js 14에서 16으로 올리면서 겪은 삽질과 깨달음을 기록한다
"주말이면 되겠지"
잘 돌아가는 블로그를 굳이 마이그레이션한 이유는 솔직히 두 가지다. Next.js 14 보안 패치 지원이 끝나가고 있었고, React 19 새 기능들을 쓰고 싶었다. 근데 진짜 이유는 새 버전 나오면 써보고 싶은 개발자 본능이다. 인정한다.
"주말이면 되겠지"라고 시작했다. 5일 걸렸다. (주말 + 평일 3일을 추가로 썼다.)
next.config부터 막혔다
제일 먼저 마주치는 변화가 설정 파일이다. next.config.js에서 next.config.ts로 전환이 공식 권장이 됐다. TypeScript로 설정 쓸 수 있다는 건 좋은데, 기존 플러그인들이 타입 정의를 안 주는 경우가 있어서 @ts-expect-error를 붙여야 하는 게 좀 찝찝했다.
Turbopack이 기본 번들러로 자리 잡으면서 webpack 관련 설정은 별도 옵트인으로 바뀌었다. 내 블로그는 MDX 플러그인 때문에 webpack이 필요했는데, 이 부분에서 하루를 날렸다.
params가 Promise가 된 건 좀 짜증났다
이전에는 params.slug로 바로 접근했는데, 이제 params가 Promise다. const { slug } = await params;로 써야 한다. 타입도 Promise<{ slug: string }>으로. 사소해 보이지만 동적 라우트가 12개 있었고 전부 수정해야 했다.
정규식으로 일괄 치환하려다가 엣지 케이스에서 터져서, 결국 하나하나 손으로 고쳤다. 마이그레이션에서 제일 시간 잡아먹는 건 큰 변경이 아니라, 사소한 변경이 열두 군데 흩어져 있는 경우다.
Turbopack은 빠르긴 한데
개발 서버 시작이 8초에서 1.2초로 줄었다. 이건 진짜 좋다. HMR도 체감될 정도로 빨라졌다.
근데 ESM 호환성 문제가 발목을 잡았다. rehype-pretty-code 같은 MDX 라이브러리들이 Turbopack이랑 안 맞았다. 빌드는 되는데 런타임에서 코드 하이라이팅이 안 먹는 거다. 결국 개발 환경에서는 Turbopack, 프로덕션 빌드는 webpack으로 하는 하이브리드를 택했다. 완벽하진 않지만 현실적인 타협이었다. (이걸 타협이라고 해야 하나 패배라고 해야 하나.)
이미지도 미묘하게 바뀌었다
next/image에서 fill 쓸 때 부모에 position: relative 없으면 경고가 강화됐고, sizes 안 넣으면 콘솔 워닝이 뜬다. 이미지 스무 개 전부 수정했다. 귀찮았지만 Lighthouse 점수가 3점 올라갔으니 의미는 있었다.
숫자로 보면 확실히 나아졌다
개발 서버 콜드 스타트 8초에서 1.2초. 프로덕션 빌드 45초에서 38초. LCP 1.8초에서 1.4초. 특히 개발 서버 속도 향상은 DX에서 체감이 크다. "npm run dev 치고 커피 탈 시간"이 사라졌다.
솔직히 후회되는 부분
하나 있다. 공식 마이그레이션 가이드를 대충 훑고 바로 작업에 들어간 거다. 가이드를 처음부터 끝까지 꼼꼼히 읽었으면 하루는 줄었을 거다. 그리고 의존성을 한 번에 다 올린 것도 실수였다. 하나씩 올리면서 각 단계에서 빌드 확인했어야 했다. 한 번에 올리면 어디서 터졌는지 모른다.
5일 걸렸지만 올리길 잘했다고 생각한다. 근데 "주말이면 된다"는 낙관론은 무조건 버려야 한다. 마이그레이션 예상 시간에 2.5 곱하면 현실에 가깝다.