Upgrading to Version 9

버전 9로 업그레이드하려면 아래의 명령어를 실행하세요:

Terminal
npm i next@9
Terminal
yarn add next@9
Terminal
pnpm up next@9
Terminal
bun add next@9

알아두면 좋은 사항: TypeScript를 사용하는 경우, @types/react@types/react-dom도 해당 버전으로 업그레이드해야 합니다.

Production Deployment on Vercel

과거에 vercel.json 파일에서 동적 경로를 위해 routes를 구성했다면, Next.js 9의 새로운 기능인 Dynamic Routing feature을 활용해 이러한 규칙을 제거할 수 있습니다.

Next.js 9의 동적 경로는 Vercel (opens in a new tab)에서 자동으로 구성되며, vercel.json 추가 설정이 필요하지 않습니다.

자세한 내용은 Dynamic Routing here에서 확인할 수 있습니다.

Check your Custom App File (pages/_app.js)

과거에 Custom <App> 예제를 복사한 경험이 있는 경우, getInitialProps를 제거할 수 있습니다.

새로운 Next.js 기능을 활용하기 위해 pages/_app.js에서 getInitialProps을 제거해 주세요!

getInitialProps는 아무 작업도 하지 않으므로 제거해도 됩니다:

class MyApp extends App {
  // 삭제해 주세요, 아무것도 수행하지 않습니다!
  static async getInitialProps({ Component, ctx }) {
    let pageProps = {}
 
    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx)
    }
 
    return { pageProps }
  }
 
  render() {
    // ... etc
  }
}

Breaking Changes

@zeit/next-typescript is no longer necessary

Next.js는 이제 @zeit/next-typescript 사용을 무시하고 이를 제거하라는 경고를 표시할 겁니다. 이 플러그인을 next.config.js에서 제거하세요.

커스텀 .babelrc@zeit/next-typescript/babel 참조가 있는 경우 이를 제거하세요.

fork-ts-checker-webpack-plugin (opens in a new tab) 사용도 next.config.js에서 제거해야 합니다.

TypeScript 정의 파일은 next 패키지에 포함되어 있으므로, 충돌을 방지하기 위해 @types/next를 제거해야 합니다.

다음 유형은 다릅니다:

이 목록은 업그레이드를 돕기 위해 커뮤니티가 작성한 것입니다. 차이점을 발견한다면, 이 목록에 pull-request를 보내어 다른 사용자들을 도와주세요.

From:

import { NextContext } from 'next'
import { NextAppContext, DefaultAppIProps } from 'next/app'
import { NextDocumentContext, DefaultDocumentIProps } from 'next/document'

to

import { NextPageContext } from 'next'
import { AppContext, AppInitialProps } from 'next/app'
import { DocumentContext, DocumentInitialProps } from 'next/document'

The config key is now an export on a page

페이지에서 config라는 이름의 사용자 지정 변수를 더 이상 내보낼 수 없습니다. (예. export { config } / export const config ...) 내보낸 변수는 이제 Opt-in AMP 및 API Route 기능과 같은 페이지 수준의 Next.js 구성을 지정하는 데 사용됩니다.

Next.js와 관련 없는 config 내보내기 변수는 다른 이름으로 변경해야 합니다.

next/dynamic no longer renders "loading..." by default while loading

동적 컴포넌트는 로딩 중에 기본적으로 아무것도 렌더링하지 않습니다. loading 속성을 설정하여 이 동작을 계속 사용자 지정할 수 있습니다:

import dynamic from 'next/dynamic'
 
const DynamicComponentWithCustomLoading = dynamic(
  () => import('../components/hello2'),
  {
    loading: () => <p>Loading</p>,
  },
)

withAmp has been removed in favor of an exported configuration object

Next.js에서 page-level configuration 개념을 도입했기 때문에 일관성을 위해 withAmp 고차 컴포넌트를 제거했습니다.

해당 변경 사항은 아래 명령어를 Next.js 프로젝트의 루트에서 실행하여 자동으로 마이그레이션할 수 있습니다:

Terminal
curl -L https://github.com/vercel/next-codemod/archive/master.tar.gz | tar -xz --strip=2 next-codemod-master/transforms/withamp-to-config.js npx jscodeshift -t ./withamp-to-config.js pages/**/*.js

마이그레이션을 수동으로 수행하거나, codemod의 생성 결과를 보려면 아래를 참조하세요:

Before

import { withAmp } from 'next/amp'
 
function Home() {
  return <h1>My AMP Page</h1>
}
 
export default withAmp(Home)
// or
export default withAmp(Home, { hybrid: true })

After

export default function Home() {
  return <h1>My AMP Page</h1>
}
 
export const config = {
  amp: true,
  // or
  amp: 'hybrid',
}

next export no longer exports pages as index.html

과거, pages/about.js를 내보내면 out/about/index.html이 생성되었습니다. 이 행위는 out/about.html이 생성되는 결과로 바뀌었습니다.

동작을 되돌리려면 다음 내용을 포함하는 next.config.js 파일을 생성하세요:

next.config.js
module.exports = {
  trailingSlash: true,
}

pages/api/ is treated differently

pages/api/의 페이지는 이제 API Routes (opens in a new tab)로 간주됩니다. 해당 경로에 있는 페이지는 더 이상 클라이언트 측 번들을 포함하지 않습니다.

Deprecated Features

next/dynamic has deprecated loading multiple modules at once

next/dynamic에서 여러 모듈을 한 번에 로드하는 기능을 사용하지 않게 되어 React(React.lazy and Suspense) 구현에 더 가까워졌습니다.

동작에 의존하는 코드를 업데이트하는 것은 비교적 간단합니다! 애플리케이션을 마이그레이션하는 데 도움이 되는 예제를 참고해 주세요:

Before

import dynamic from 'next/dynamic'
 
const HelloBundle = dynamic({
  modules: () => {
    const components = {
      Hello1: () => import('../components/hello1').then((m) => m.default),
      Hello2: () => import('../components/hello2').then((m) => m.default),
    }
 
    return components
  },
  render: (props, { Hello1, Hello2 }) => (
    <div>
      <h1>{props.title}</h1>
      <Hello1 />
      <Hello2 />
    </div>
  ),
})
 
function DynamicBundle() {
  return <HelloBundle title="Dynamic Bundle" />
}
 
export default DynamicBundle

After

import dynamic from 'next/dynamic'
 
const Hello1 = dynamic(() => import('../components/hello1'))
const Hello2 = dynamic(() => import('../components/hello2'))
 
function HelloBundle({ title }) {
  return (
    <div>
      <h1>{title}</h1>
      <Hello1 />
      <Hello2 />
    </div>
  )
}
 
function DynamicBundle() {
  return <HelloBundle title="Dynamic Bundle" />
}
 
export default DynamicBundle