무색
기술블로그
에세이
연구
소개

무색

소프트웨어로 비즈니스의 가능성을 만듭니다. 웹·앱 개발, 음성 AI, 자동화 콘텐츠 제작까지 — 기술이 필요한 곳에 무색이 있습니다.

연락처

contact@museck.com

사업자 정보

상호: 무색

대표: 배성재

사업자등록번호: 577-58-00836

인천광역시 연수구 인천타워대로 323, 에이동 8층 801-802호 AB-132 (송도동, 송도 센트로드)

© 2026 무색. All rights reserved.
개인정보처리방침·이용약관·연락처
INCHEON, KR
Next.js 16 + Tailwind v4 + React Compiler — sumi-e 키 비주얼
museck 만들기
2026. 2. 16.

Next.js 16 + Tailwind v4 + React Compiler 메가 업그레이드: 한 번에 세 개 올리기

next.jstailwind-cssreactupgrade

PayloadCMS 3.77이 Next.js 16.2.0-canary.10 이상을 요구한다는 공지가 떴다. 어차피 올려야 하는 거, Tailwind CSS v3에서 v4로, React Compiler까지 한 번에 올리기로 했다. 메이저 의존성 3개를 하루에 동시 업그레이드한 기록이다.

왜 한 번에 올렸나

하나씩 올리면 안전하지만, 이 세 가지는 서로 영향을 준다.

  • PayloadCMS 3.77은 React 19.2와 Next.js 16을 요구한다.
  • Next.js 16은 Turbopack이 기본이고, Tailwind v4와 궁합이 좋다.
  • React Compiler는 React 19 이상에서만 동작한다.

결국 하나를 올리면 나머지도 같이 올라가야 한다. 차라리 한 번에 끝내는 게 중간 상태에서 삽질하는 것보다 낫다고 판단했다.

업그레이드 순서

의존 관계를 따라 순서를 잡았다. 하위 의존성부터 올려야 상위에서 깨지지 않는다.

  1. PayloadCMS 3.77 + React 19.2 + TypeScript 5.9 + Sharp 0.34.5
  2. Next.js 16.2.0-canary.51
  3. Tailwind CSS v4 (CSS-first 전환)
  4. React Compiler 활성화
  5. ESLint flat config 전환

1단계: PayloadCMS + React + TypeScript

가장 먼저 PayloadCMS를 3.77로 올렸다. 이때 React 19.2.4, TypeScript 5.9.3, Sharp 0.34.5도 함께 업데이트했다. PayloadCMS가 의존하는 버전이 정해져 있어서 선택의 여지가 없다.

2단계: Next.js 16 — async API 마이그레이션

Next.js 16의 가장 큰 변화는 params와 searchParams가 Promise로 바뀐 것이다. 기존에는 동기적으로 접근했지만 이제는 await로 풀어야 한다.

모든 동적 라우트 페이지를 수정해야 했다. params를 사용하는 곳이 15군데 정도 있었는데, 타입스크립트 에러가 전부 잡아줬다.

Turbopack도 이제 기본 번들러다. 별도 설정 없이 pnpm dev하면 Turbopack으로 돌아간다. webpack을 쓰려면 --webpack 플래그를 명시해야 한다.

3단계: Tailwind v4 — CSS-first로 전환

Tailwind v4는 근본적으로 달라졌다. tailwind.config.ts를 삭제하고 CSS에서 모든 걸 정의한다.

JS 설정 파일 대신 CSS @theme 블록에서 디자인 토큰을 정의한다. @plugin으로 플러그인을 불러오고, @import "tailwindcss"가 base, components, utilities를 모두 포함한다.

기존에 tailwind.config.ts에 정의했던 커스텀 색상, 폰트, 애니메이션을 전부 CSS 변수로 옮겼다. 파일 하나가 통째로 사라지는 건 기분이 좋다.

4단계: React Compiler

React Compiler는 useMemo, useCallback을 자동으로 처리해준다. 직접 메모이제이션 코드를 쓸 필요가 없어진다.

설정 한 줄이다. 기존 코드의 useMemo, useCallback을 당장 제거할 필요는 없다. 컴파일러가 자동으로 더 나은 최적화를 적용하기 때문에 기존 코드와 공존한다.

5단계: ESLint flat config

Next.js 16은 ESLint flat config을 네이티브로 지원한다. 기존의 FlatCompat 래퍼를 제거하고 깔끔하게 전환했다.

다만 eslint-plugin-react가 아직 ESLint 10을 지원하지 않아서 ESLint 9에 머물러야 했다. 이건 시간이 해결해줄 문제다.

삽질 모음

ESM 프로젝트의 .cjs 확장자

프로젝트가 "type": "module"이라서 CJS 형식의 설정 파일은 .cjs 확장자를 써야 한다. postcss.config.cjs가 대표적이다. .js로 두면 ESM으로 해석하려다 실패한다.

params가 Promise라니

Next.js 16에서 params가 Promise가 된 건 파급력이 크다. 모든 동적 라우트의 페이지 컴포넌트와 generateMetadata, generateStaticParams를 수정해야 한다. 타입스크립트가 없었으면 런타임에서야 발견했을 버그다.

tailwind.config.ts 완전 삭제

v4에서는 JS 설정 파일이 아예 없다. 처음에 tailwind.config.ts를 남겨뒀더니 v3 모드로 동작해서 신규 기능이 안 먹었다. 깨끗하게 삭제해야 한다.

next lint 명령어 변경

Next.js 16에서 next lint 대신 직접 eslint src/ 명령어를 사용해야 한다. next.config.mjs에서 eslint 키가 더 이상 인식되지 않는다.

canary 버전의 리스크

솔직히 말하면 Next.js 16.2.0-canary.51은 정식 릴리스가 아니다. PayloadCMS가 요구해서 쓰는 거지만, canary 버전은 언제든 breaking change가 올 수 있다. 실제로 개발하면서 canary 버전 간에도 동작이 달라지는 경우를 겪었다.

그래도 PayloadCMS 팀이 이 버전을 기준으로 테스트하고 있으니 어느 정도의 안정성은 보장된다. 다만 프로덕션에 쓸 때는 canary 버전을 고정하고 무분별한 업데이트를 피하는 게 좋다.

마무리

메이저 업그레이드 3개를 하루에 끝냈다. 5개 커밋으로. 의존성 순서를 따라 차분하게 진행하면 생각보다 무섭지 않다.

핵심 교훈: 의존 관계가 엮인 메이저 업그레이드는 한 번에 끝내는 게 낫다. 중간 상태에서 머무르면 양쪽 버전의 버그를 동시에 상대해야 한다. 그게 더 고통스럽다.

자주 묻는 질문

Next.js 16에서 params가 Promise로 바뀌면 기존 코드를 전부 수정해야 하나요?
동적 라우트([slug] 등)의 params를 사용하는 모든 페이지 컴포넌트, generateMetadata, generateStaticParams를 수정해야 합니다. TypeScript가 타입 에러로 모두 잡아줍니다.
Tailwind CSS v3에서 v4로 전환할 때 가장 주의할 점은 무엇인가요?
tailwind.config.ts를 완전히 삭제해야 합니다. 파일이 남아 있으면 v3 호환 모드로 동작합니다. CSS @theme 블록에서 모든 디자인 토큰을 재정의해야 합니다.
React Compiler를 활성화하면 기존 useMemo, useCallback을 제거해야 하나요?
바로 제거할 필요 없습니다. 컴파일러가 더 나은 최적화를 자동 적용하므로 기존 코드와 공존할 수 있습니다. 점진적으로 정리하면 됩니다.
museck 만들기(13/22)
Prev

Next.js App Router SEO 구현: robots.ts부터 JSON-LD까지 하루 만에

Next

Next.js 셀프호스팅 강화: tini, Build ID, Version Skew 방지까지