PromleeBlog
sitemap
aboutMe

posting thumbnail
Next.js 16 마이그레이션 가이드
Next.js 16 Complete Migration Guide

📅

🚀

들어가기 전에 🔗

안녕하세요.
앞선 포스팅들에서 Next.js 16의 강력한 기능들을 살펴봤다면, 이제는 직접 실전에 적용해 볼 차례입니다.
여러분의 소중한 프로젝트를 안전하게 Next.js 16으로 업그레이드하는 과정을 단계별로 안내해 드리겠습니다.
단순히 패키지 버전만 올리는 것이 아니라, 코드 수정이 필요한
Breaking Changes
들이 꽤 있으므로 꼼꼼히 따라오셔야 합니다.

🚀

1. 자동 업그레이드 도구 사용 (Codemod) 🔗

Next.js 팀은 업그레이드를 돕기 위해 강력한 자동화 도구를 제공합니다.
수동으로 버전을 올리기보다 이 도구를 사용하는 것을 강력히 권장합니다.

터미널을 열고 프로젝트 폴더에서 아래 명령어를 실행하세요.
Terminal
npx @next/codemod@canary upgrade latest
이 명령어는 다음과 같은 작업들을 수행합니다.

하지만 이 명령어가 비즈니스 로직까지 완벽하게 이해하고 고쳐줄 수는 없습니다.
이제부터는 개발자가 직접 손봐야 할
필수 수정 사항
들입니다.

🚀

2. 필수 수정: 비동기(Async) API 적용 🔗

가장 먼저, 그리고 가장 많이 마주칠 에러는 동기 API 접근에 관한 것입니다.
2부에서 설명했듯, 서버 컴포넌트의 런타임 데이터 접근은 이제
비동기
로 처리해야 합니다.

params와 searchParams 수정 🔗

페이지 컴포넌트의 props 타입과 사용법을 변경해야 합니다.
app/blog/[slug]/page.tsx
// [변경 전]
// export default function Page({ params }: { params: { slug: string } }) { ... }
 
// [변경 후]
// 1. Props 타입: params를 Promise로 감싸줍니다.
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  
  // 2. 데이터 접근: 반드시 await를 사용하여 값을 추출해야 합니다.
  // await 없이 params.slug에 접근하면 런타임 에러가 발생합니다.
  const { slug } = await params;
 
  return <div>Blog Post: {slug}</div>;
}

cookies와 headers 수정 🔗

next/headers 에서 가져오는 함수들도 모두 await 가 필요합니다.
app/layout.tsx
import { cookies } from 'next/headers';
 
export default async function RootLayout({ children }: { children: React.ReactNode }) {
  // [변경] cookies() 호출 앞에 await를 붙여야 합니다.
  // 이전 버전(v15)에서는 경고만 떴지만, v16에서는 에러가 발생합니다.
  const cookieStore = await cookies();
  const theme = cookieStore.get('theme');
  
  return (
    <html lang="en" className={theme?.value}>
      <body>{children}</body>
    </html>
  );
}

🚀

3. next/image 보안 설정 강화 🔗

이미지 최적화 컴포넌트의 기본 설정이 변경되었습니다.
보안과 성능을 위해 조금 더 엄격해졌다고 보시면 됩니다.

로컬 이미지 쿼리 스트링 허용 🔗

로컬 이미지 경로에 ?v=1 같은 쿼리 파라미터를 사용하고 있었다면, 이제 next.config.js 에 명시적으로 허용 설정을 추가해야 합니다.
이는 무분별한 이미지 생성을 막기 위한 조치입니다.

사설망 로컬 IP 허용 🔗

개발 환경(localhost)이 아닌, 사설 네트워크 등에서 로컬 IP로 이미지를 로드해야 한다면 dangerouslyAllowLocalIP 옵션을 켜야 합니다.
next.config.js
module.exports = {
  images: {
    // 1. 로컬 이미지에 쿼리 스트링 사용 시 패턴 명시 필요
    localPatterns: [
      {
        pathname: '/assets/**',
        search: '?v=1',
      },
    ],
 
    // 2. 사설망 등에서 로컬 IP로 이미지 로드 시 필요 (보안 주의)
    // 기본값은 false로 변경되었습니다.
    dangerouslyAllowLocalIP: true, 
  },
};

🚀

4. Parallel Routes: default.js 강제 🔗

병렬 라우트(Parallel Routes)
, 즉 @folder 형식을 사용 중이라면 주의하세요.
이제 각 슬롯에 대응하는 default.js 파일이 없으면
빌드 자체가 실패
합니다.

이전에는 Next.js가 알아서 404 처리를 하거나 무시했지만, 이제는 개발자가 명시적으로 "보여줄 내용 없음"을 선언해야 합니다.
app/@analytics/default.tsx
// 해당 슬롯에 보여줄 기본 컨텐츠가 없더라도 파일은 생성해야 합니다.
export default function Default() {
  // 아무것도 렌더링하지 않으려면 null을 반환합니다.
  return null;
}

🚀

5. Middleware 파일명 변경 🔗

프로젝트 루트에 있는 middleware.ts (또는 .js) 파일의 이름을
proxy.ts
로 변경해 주세요.
그리고 내부에서 내보내는 함수 이름도 middleware 에서 proxy 로 변경해야 합니다.
Terminal
# 파일명 변경
mv middleware.ts proxy.ts
proxy.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
 
// [변경] 함수 이름을 proxy로 수정
export function proxy(request: NextRequest) {
  return NextResponse.next();
}

🚀

6. 제거된 기능 정리 (Cleanup) 🔗

마지막으로, 더 이상 지원하지 않는 기능들을 코드에서 삭제해야 합니다.

🚀

결론 🔗

Next.js 16으로의 마이그레이션은 단순히 버전을 올리는 것 이상의 의미가 있습니다.
비동기 API
명시적 캐싱
을 통해 애플리케이션의 체질을 개선하고, 장기적인 유지보수성을 확보하는 과정입니다.

이 글 뿐 아니라 아래 참고 링크를 차근차근 따라 하시면 큰 문제 없이 마이그레이션을 완료하실 수 있을 겁니다.
감사합니다.

참고 🔗