Next.js 15 Breaking Changes 완벽 정리

HyeonE·2026년 1월 29일

NextJS

목록 보기
3/4
post-thumbnail

Next.js 16을 프로젝트에 적용하면서 Next.js 15의 주요 변경사항들을 공부하게 되었습니다.
해당 포스팅에서는 Breaking Changes를 중심으로 정리해보겠습니다.

🚨 1. params와 searchParams가 이제 Promise입니다

가장 큰 변경사항 중 하나입니다. Next.js 14까지는 동기적으로 접근 가능했던 paramssearchParams가 이제 비동기로 변경되었습니다.

❌ Before (Next.js 14)

// app/users/[id]/page.tsx
export default function UserPage({ 
  params 
}: { 
  params: { id: string } 
}) {
  const userId = params.id  // 바로 접근 가능
  
  return <div>User ID: {userId}</div>
}

✅ After (Next.js 15)

// app/users/[id]/page.tsx
export default async function UserPage({ 
  params 
}: { 
  params: Promise<{ id: string }> 
}) {
  const { id } = await params  // await 필요!
  
  return <div>User ID: {id}</div>
}

searchParams도 마찬가지

// app/search/page.tsx
export default async function SearchPage({
  searchParams
}: {
  searchParams: Promise<{ q: string }>
}) {
  const { q } = await searchParams
  
  return <div>검색어: {q}</div>
}

왜 이렇게 변경되었나요?

  • 서버 컴포넌트의 성능 최적화
  • 스트리밍과 Suspense의 더 나은 통합
  • 동적 데이터 처리의 일관성

🎯 2. Parallel Routes에 default.tsx 필수

Parallel Routes를 사용할 때 각 슬롯마다 default.tsx 파일이 반드시 필요합니다.

에러 메시지

Missing required default.js file for parallel route at /@modal

해결 방법

// app/@modal/default.tsx
export default function Default() {
  return null
}

폴더 구조 예시

app/
├── layout.tsx
├── @modal/
│   ├── default.tsx     ← 필수!
│   └── login/
│       └── page.tsx
└── page.tsx

Layout에서 사용

// app/layout.tsx
export default function RootLayout({
  children,
  modal,
}: {
  children: React.ReactNode
  modal: React.ReactNode
}) {
  return (
    <html>
      <body>
        {children}
        {modal}
      </body>
    </html>
  )
}

왜 필요한가요?

  • Parallel Route가 활성화되지 않았을 때의 fallback
  • 소프트 네비게이션 시 예기치 않은 에러 방지

⚡ 3. fetch 캐싱 기본값 변경

Next.js 15부터 fetch의 기본 캐싱 동작이 변경되었습니다.

❌ Before (Next.js 14)

// 기본적으로 캐시됨
const res = await fetch('https://api.example.com/data')

✅ After (Next.js 15)

// 기본적으로 캐시 안 됨 (no-store)
const res = await fetch('https://api.example.com/data')

// 캐시하려면 명시적으로 지정
const res = await fetch('https://api.example.com/data', {
  cache: 'force-cache'
})

// 또는 revalidate 사용
const res = await fetch('https://api.example.com/data', {
  next: { revalidate: 3600 }  // 1시간
})

마이그레이션 팁:
캐시가 필요한 fetch 호출에는 명시적으로 캐싱 옵션을 추가하세요.

🔄 4. React 19 지원

Next.js 15는 React 19를 정식 지원합니다.

주요 변경사항

  1. useFormStatus, useFormState 안정화
'use client'
import { useFormStatus } from 'react-dom'

export function SubmitButton() {
  const { pending } = useFormStatus()
  
  return (
    <button disabled={pending}>
      {pending ? '제출 중...' : '제출'}
    </button>
  )
}
  1. ref가 이제 prop으로
// React 19에서는 forwardRef 불필요
export default function Input({ ref, ...props }: Props) {
  return <input ref={ref} {...props} />
}

📝 5. 비동기 Server Component가 표준

Server Component에서 async/await을 직접 사용하는 것이 표준 패턴이 되었습니다.

export default async function Home() {
  // 직접 데이터 fetch
  const posts = await fetch('https://api.example.com/posts')
    .then(res => res.json())
  
  // DB 직접 접근도 가능
  const users = await db.user.findMany()
  
  return (
    <div>
      {posts.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

장점:

  • useEffect나 별도의 data fetching 로직 불필요
  • 서버에서 직접 데이터 처리
  • 자동 에러 처리 및 로딩 상태 관리

🎉 결론

Next.js 15의 변경사항들은 처음에는 번거로워 보이지만, 더 나은 성능과 개발 경험을 위한 것입니다. 특히 params를 Promise로 변경한 것은 서버 컴포넌트의 스트리밍과 Suspense를 더 효과적으로 활용하기 위한 선택으로 생각됩니다.

이번 Next.js 15 학습을 통해 프레임워크의 진화 방향을 이해할 수 있었고, 앞으로 Next.js 16의 변경점들도 차근차근 공부해봐야겠습니다! 🚀


참고 자료:

profile
기억보다 기록을

0개의 댓글