콘텐츠로 이동

업그레이드: Codemods

출처 URL: https://nextjs.org/docs/pages/guides/upgrading/codemods

마지막 업데이트 2026년 2월 20일

Codemod은 코드베이스에서 프로그램적으로 실행되는 변환으로, 모든 파일을 수동으로 확인하지 않아도 대규모 변경을 자동으로 적용할 수 있습니다.

Next.js는 API가 업데이트되거나 사용 중단될 때 Next.js 코드베이스 업그레이드를 돕기 위한 Codemod 변환을 제공합니다.

터미널에서 프로젝트 폴더로 이동(cd)한 다음 다음을 실행합니다:

터미널

npx @next/codemod <transform> <path>

<transform><path>를 알맞은 값으로 바꿉니다.

  • transform - 변환 이름
  • path - 변환할 파일 또는 디렉터리
  • --dry 실제 코드를 수정하지 않는 드라이런 수행
  • --print 비교용으로 변경된 출력 내용을 표시

Next.js, React, React DOM을 업데이트하고 codemod를 자동으로 실행해 Next.js 애플리케이션을 업그레이드합니다.

터미널

npx @next/codemod upgrade [revision]
  • revision(선택 사항): 업그레이드 유형(patch, minor, major), NPM dist 태그(예: latest, canary, rc), 또는 정확한 버전(예: 15.0.0)을 지정합니다. 안정 버전의 기본값은 minor입니다.
  • --verbose: 업그레이드 과정에서 더 자세한 출력을 표시합니다.

예:

터미널

# 최신 패치로 업그레이드 (예: 16.0.7 -> 16.0.8)
npx @next/codemod upgrade patch
# 최신 마이너로 업그레이드 (예: 15.3.7 -> 15.4.8). 기본값입니다.
npx @next/codemod upgrade minor
# 최신 메이저로 업그레이드 (예: 15.5.7 -> 16.0.7)
npx @next/codemod upgrade major
# 특정 버전으로 업그레이드
npx @next/codemod upgrade 16
# 카나리 릴리스로 업그레이드
npx @next/codemod upgrade canary

알아두면 좋아요 :

  • 대상 버전이 현재 버전과 같거나 낮으면 명령은 아무 변경 없이 종료됩니다.
  • 업그레이드 중 Next.js codemod 적용 여부나 React 업그레이드 시 React 19 codemod 실행 여부를 묻는 프롬프트가 나타날 수 있습니다.

App Router 페이지와 레이아웃에서 experimental_ppr 라우트 세그먼트 구성 제거

섹션 제목: “App Router 페이지와 레이아웃에서 experimental_ppr 라우트 세그먼트 구성 제거”

터미널

npx @next/codemod@latest remove-experimental-ppr .

이 codemod는 App Router 페이지와 레이아웃에서 experimental_ppr 라우트 세그먼트 구성을 제거합니다.

app/page.tsx

- export const experimental_ppr = true;

안정화된 API에서 unstable_ 접두사 제거

섹션 제목: “안정화된 API에서 unstable_ 접두사 제거”

터미널

npx @next/codemod@latest remove-unstable-prefix .

이 codemod는 안정화된 API에서 unstable_ 접두사를 제거합니다.

예:

import { unstable_cacheTag as cacheTag } from 'next/cache'
cacheTag()

다음으로 변환됩니다:

import { cacheTag } from 'next/cache'
cacheTag()

사용 중단된 middleware 규약에서 proxy로 마이그레이션

섹션 제목: “사용 중단된 middleware 규약에서 proxy로 마이그레이션”

터미널

npx @next/codemod@latest middleware-to-proxy .

이 codemod는 사용 중단된 middleware 규약을 사용하는 프로젝트를 proxy 규약으로 마이그레이션합니다. 수행 작업:

  • middleware.<extension>proxy.<extension>으로 이름 변경(예: middleware.tsproxy.ts)
  • 이름이 지정된 export middlewareproxy로 변경
  • Next.js 구성 속성 experimental.middlewarePrefetchexperimental.proxyPrefetch로 변경
  • Next.js 구성 속성 experimental.middlewareClientMaxBodySizeexperimental.proxyClientMaxBodySize로 변경
  • Next.js 구성 속성 experimental.externalMiddlewareRewritesResolveexperimental.externalProxyRewritesResolve로 변경
  • Next.js 구성 속성 skipMiddlewareUrlNormalizeskipProxyUrlNormalize로 변경

예:

middleware.ts

import { NextResponse } from 'next/server'
export function middleware() {
return NextResponse.next()
}

다음으로 변환됩니다:

proxy.ts

import { NextResponse } from 'next/server'
export function proxy() {
return NextResponse.next()
}

next lint에서 ESLint CLI로 마이그레이션

섹션 제목: “next lint에서 ESLint CLI로 마이그레이션”

터미널

npx @next/codemod@canary next-lint-to-eslint-cli .

이 codemod는 next lint를 사용하는 프로젝트를 로컬 ESLint 설정과 함께 ESLint CLI를 사용하도록 마이그레이션합니다. 수행 작업:

  • Next.js 권장 구성이 포함된 eslint.config.mjs 파일 생성
  • package.json 스크립트를 next lint 대신 eslint .으로 업데이트
  • 필요한 ESLint 의존성을 package.json에 추가
  • 기존 ESLint 구성이 있으면 그대로 유지

예:

package.json

{
"scripts": {
"lint": "next lint"
}
}

다음으로 변경됩니다:

package.json

{
"scripts": {
"lint": "eslint ."
}
}

그리고 다음 파일을 생성합니다:

eslint.config.mjs

import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { FlatCompat } from '@eslint/eslintrc'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
})
const eslintConfig = [
...compat.extends('next/core-web-vitals', 'next/typescript'),
{
ignores: [
'node_modules/**',
'.next/**',
'out/**',
'build/**',
'next-env.d.ts',
],
},
]
export default eslintConfig

App Router 라우트 세그먼트 구성 runtime 값을 experimental-edge에서 edge로 변환

섹션 제목: “App Router 라우트 세그먼트 구성 runtime 값을 experimental-edge에서 edge로 변환”

참고 : 이 codemod는 App Router 전용입니다.

터미널

npx @next/codemod@latest app-dir-runtime-config-experimental-edge .

이 codemod는 Route Segment Config runtimeexperimental-edgeedge로 변환합니다.

예:

export const runtime = 'experimental-edge'

다음으로 변환됩니다:

export const runtime = 'edge'

비동기 Dynamic API로 마이그레이션

섹션 제목: “비동기 Dynamic API로 마이그레이션”

기존에 동적 렌더링을 선택해 동기 접근을 지원하던 API가 이제 비동기 방식이 되었습니다. 이 파괴적 변경 사항에 대한 자세한 내용은 업그레이드 가이드에서 확인하세요.

터미널

npx @next/codemod@latest next-async-request-api .

이 codemod는 이제 비동기로 전환된 동적 API(next/headerscookies(), headers(), draftMode())를 적절하게 await하거나 필요 시 React.use()로 래핑합니다. 자동 마이그레이션이 불가능한 경우 TypeScript 파일이면 타입 캐스트를, 그 외에는 수동 검토 및 업데이트가 필요하다는 주석을 추가합니다.

예:

import { cookies, headers } from 'next/headers'
const token = cookies().get('token')
function useToken() {
const token = cookies().get('token')
return token
}
export default function Page() {
const name = cookies().get('name')
}
function getHeader() {
return headers().get('x-foo')
}

다음으로 변환됩니다:

import { use } from 'react'
import {
cookies,
headers,
type UnsafeUnwrappedCookies,
type UnsafeUnwrappedHeaders,
} from 'next/headers'
const token = (cookies() as unknown as UnsafeUnwrappedCookies).get('token')
function useToken() {
const token = use(cookies()).get('token')
return token
}
export default async function Page() {
const name = (await cookies()).get('name')
}
function getHeader() {
return (headers() as unknown as UnsafeUnwrappedHeaders).get('x-foo')
}

page.js, layout.js, route.js, default.js와 같은 페이지/라우트 항목 또는 generateMetadata / generateViewport API에서 paramssearchParams props에 대한 프로퍼티 접근을 감지하면 동기 호출부를 비동기 함수로 변환하고 프로퍼티 접근을 await하려고 시도합니다. 클라이언트 컴포넌트처럼 비동기로 만들 수 없는 경우 React.use를 사용해 프로미스를 언래핑합니다.

예:

page.tsx
export default function Page({
params,
searchParams,
}: {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}) {
const { value } = searchParams
if (value === 'foo') {
// ...
}
}
export function generateMetadata({ params }: { params: { slug: string } }) {
const { slug } = params
return {
title: `My Page - ${slug}`,
}
}

다음으로 변환됩니다:

page.tsx
export default async function Page(props: {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const searchParams = await props.searchParams
const { value } = searchParams
if (value === 'foo') {
// ...
}
}
export async function generateMetadata(props: {
params: Promise<{ slug: string }>
}) {
const params = await props.params
const { slug } = params
return {
title: `My Page - ${slug}`,
}
}

알아두면 좋아요: 이 codemod가 수동 개입이 필요할 가능성을 감지했지만 정확한 수정 방법을 결정할 수 없는 경우, 코드에 주석이나 타입 캐스트를 추가해 수동 업데이트가 필요함을 알립니다. 이 주석에는 @next/codemod 접두사가 붙고, 타입 캐스트에는 UnsafeUnwrapped 접두사가 붙습니다. 이러한 주석을 명시적으로 제거하기 전까지는 빌드가 실패합니다. 자세히 보기.

NextRequestgeoip 프로퍼티를 @vercel/functions로 대체

섹션 제목: “NextRequest의 geo 및 ip 프로퍼티를 @vercel/functions로 대체”

터미널

npx @next/codemod@latest next-request-geo-ip .

이 코드를 사용하면 @vercel/functions가 설치되고 NextRequestgeoip 속성이 해당 @vercel/functions 기능으로 변환됩니다.

예시:

import type { NextRequest } from 'next/server'
export function GET(req: NextRequest) {
const { geo, ip } = req
}

다음과 같이 변환됩니다:

import type { NextRequest } from 'next/server'
import { geolocation, ipAddress } from '@vercel/functions'
export function GET(req: NextRequest) {
const geo = geolocation(req)
const ip = ipAddress(req)
}

ImageResponse 가져오기 마이그레이션

섹션 제목: “ImageResponse 가져오기 마이그레이션”

터미널

npx @next/codemod@latest next-og-import .

이 코드는 동적 OG 이미지 생성 사용을 위해 next/server에서 next/og로 가져오기를 이동하고 변환합니다.

예시:

import { ImageResponse } from 'next/server'

다음과 같이 변환됩니다:

import { ImageResponse } from 'next/og'

터미널

npx @next/codemod@latest metadata-to-viewport-export .

이 코드는 특정 뷰포트 메타데이터를 viewport 내보내기로 마이그레이션합니다.

예시:

export const metadata = {
title: 'My App',
themeColor: 'dark',
viewport: {
width: 1,
},
}

다음과 같이 변환됩니다:

export const metadata = {
title: 'My App',
}
export const viewport = {
width: 1,
themeColor: 'dark',
}

터미널

npx @next/codemod@latest built-in-next-font .

이 코드는 @next/font 패키지를 제거하고 @next/font 가져오기를 기본 제공 next/font로 변환합니다.

예시:

import { Inter } from '@next/font/google'

다음과 같이 변환됩니다:

import { Inter } from 'next/font/google'

터미널

npx @next/codemod@latest next-image-to-legacy-image .

기존 Next.js 10, 11, 12 애플리케이션의 next/image 가져오기를 Next.js 13의 next/legacy/image로 안전하게 이름을 변경합니다. 또한 next/future/imagenext/image로 이름을 바꿉니다.

예시:

pages/index.js

import Image1 from 'next/image'
import Image2 from 'next/future/image'
export default function Home() {
return (
<div>
<Image1 src="/test.jpg" width="200" height="300" />
<Image2 src="/test.png" width="500" height="400" />
</div>
)
}

다음과 같이 변환됩니다:

pages/index.js

// 'next/image' becomes 'next/legacy/image'
import Image1 from 'next/legacy/image'
// 'next/future/image' becomes 'next/image'
import Image2 from 'next/image'
export default function Home() {
return (
<div>
<Image1 src="/test.jpg" width="200" height="300" />
<Image2 src="/test.png" width="500" height="400" />
</div>
)
}

새로운 Image 컴포넌트로 마이그레이션

섹션 제목: “새로운 Image 컴포넌트로 마이그레이션”

터미널

npx @next/codemod@latest next-image-experimental .

next/legacy/image에서 새로운 next/image로 위험도가 높은 마이그레이션을 수행하며, 인라인 스타일을 추가하고 사용되지 않는 props를 제거합니다.

  • layout prop을 제거하고 style을 추가합니다.
  • objectFit prop을 제거하고 style을 추가합니다.
  • objectPosition prop을 제거하고 style을 추가합니다.
  • lazyBoundary prop을 제거합니다.
  • lazyRoot prop을 제거합니다.

터미널

npx @next/codemod@latest new-link .

Link 컴포넌트 안의 <a> 태그를 제거합니다.

예시:

<Link href="/about">
<a>About</a>
</Link>
// transforms into
<Link href="/about">
About
</Link>
<Link href="/about">
<a onClick={() => console.log('clicked')}>About</a>
</Link>
// transforms into
<Link href="/about" onClick={() => console.log('clicked')}>
About
</Link>

터미널

npx @next/codemod cra-to-next

Create React App 프로젝트를 Next.js로 마이그레이션하면서 Pages Router와 필요한 구성을 생성해 동작을 맞춥니다. 초기에는 SSR 중 window 사용으로 인한 호환성 깨짐을 막기 위해 클라이언트 사이드 전용 렌더링을 활용하며, 이후 Next.js 고유 기능을 점진적으로 도입할 수 있습니다.

이 변환에 대한 피드백은 이 토론에 공유해 주세요.

터미널

npx @next/codemod add-missing-react-import

React를 가져오지 않는 파일에 가져오기를 추가하여 새로운 React JSX 변환이 동작하도록 합니다.

예시:

my-component.js

export default class Home extends React.Component {
render() {
return <div>Hello World</div>
}
}

다음과 같이 변환됩니다:

my-component.js

import React from 'react'
export default class Home extends React.Component {
render() {
return <div>Hello World</div>
}
}

익명 컴포넌트를 명명된 컴포넌트로 변환

섹션 제목: “익명 컴포넌트를 명명된 컴포넌트로 변환”

터미널

npx @next/codemod name-default-component

버전 9 이상.

익명 컴포넌트를 명명된 컴포넌트로 변환하여 Fast Refresh와 함께 동작하도록 합니다.

예시:

my-component.js

export default function () {
return <div>Hello World</div>
}

다음과 같이 변환됩니다:

my-component.js

export default function MyComponent() {
return <div>Hello World</div>
}

컴포넌트 이름은 파일 이름을 기반으로 카멜 케이스로 지정되며, 화살표 함수에도 동일하게 작동합니다.

참고: 기본 제공 AMP 지원과 이 코드는 Next.js 16에서 제거되었습니다.

터미널

npx @next/codemod withamp-to-config

withAmp HOC를 Next.js 9 페이지 구성으로 변환합니다.

예시:

// Before
import { withAmp } from 'next/amp'
function Home() {
return <h1>My AMP Page</h1>
}
export default withAmp(Home)
// After
export default function Home() {
return <h1>My AMP Page</h1>
}
export const config = {
amp: true,
}

터미널

npx @next/codemod url-to-withrouter

더 이상 사용되지 않는 최상위 페이지의 자동 주입 url 속성을 withRouter와 그가 주입하는 router 속성으로 변환합니다. 자세한 내용: https://nextjs.org/docs/messages/url-deprecated

예시:

변환 전

import React from 'react'
export default class extends React.Component {
render() {
const { pathname } = this.props.url
return <div>Current pathname: {pathname}</div>
}
}

변환 후

import React from 'react'
import { withRouter } from 'next/router'
export default withRouter(
class extends React.Component {
render() {
const { pathname } = this.props.router
return <div>Current pathname: {pathname}</div>
}
}
)

이 사례를 포함한 모든 변환(및 테스트)은 __testfixtures__ 디렉터리에 있습니다.

보내기