Open9

Next.js 12→13(ついでに React 17→18)へ移行する際にやったこと

トラハック@toraco株式会社トラハック@toraco株式会社

前置き

1年ほどReact/Nextの技術的キャッチアップができていなかったのでバージョンアップしてリファクタリングしながら理解を深めようと思った。
React 18 で追加された主機能もまだちゃんと試せていなかったからちゃんとやる。

移行前の関連パッケージバージョン

  • next@12.1.3
  • react@17.0.2
  • react-dom@17.0.2

移行後の関連パッケージバージョン

  • next@^13.1.0
  • react@^18.2.0
  • react-dom@^18.2.0

やること/試すこと

  • packageのバージョンアップ
  • next-transpile-moduleの削除
  • turbopack
  • next/link の変更に対応
  • コンポーネントの型修正
  • app directory
  • Layouts
  • Cloud Run へのデプロイ
  • componentのディレクトリ構成を変える

体系的に理解するために読んだ記事たち

https://nextjs.org/blog/next-13
https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html
https://zenn.dev/jtakahashi64/articles/a9d2ae3285ceb6
https://zenn.dev/g4rds/articles/287c53498d17a1
https://zenn.dev/tfutada/articles/36ad71ab598019

トラハック@toraco株式会社トラハック@toraco株式会社

next-transpile-moduleの削除

yarn workspaceを用いたmonorepo構成をとっており、共通処理をcommonというworkspaceをライブラリとして使えるようにnext-transpile-modulenext-component-pluginsを使っていた。

このツイートを見かけて、Next.js 13 の transpilePackages オプションを使えば不要になるということでやった。
https://twitter.com/martpie_/status/1605951856928096256?s=20&t=BPJKhsbudEjY-oLHSFOSSA

next.config.js は以下のように変わった(一部省略している)

before

next.config.js
const withPlugins = require('next-compose-plugins')
const withTM = require('next-transpile-modules')(['common'])

module.exports = withPlugins([withTM], {
  distDir: '.next',
  sassOptions: {
    includePaths: [path.join(__dirname, 'styles')],
  },
  swcMinify: true
})

after

next.config.js
const nextConfig = {
  distDir: '.next',
  reactStrictMode: true,
  sassOptions: {
    includePaths: [path.join(__dirname, 'styles')],
  },
  swcMinify: true,
  transpilePackages: ['common'] // これだけで良くなった!
}

module.exports = nextConfig
トラハック@toraco株式会社トラハック@toraco株式会社

turbopack

webpackに代わる爆速バンドラーとして話題になっていたので試してみようと思った。
結論、対応しているoptionsが少なくてproduction buildにも対応しておらず導入を見送った。

Reference

https://nextjs.org/blog/next-13#introducing-turbopack-alpha

Note: Turbopack in Next.js currently only supports next dev. View the supported features. We are also working to add support for next build through Turbopack.

トラハック@toraco株式会社トラハック@toraco株式会社

今までnext/linkからimportして使えるLinkコンポーネントはaタグを使用する必要があったが、aタグが不要になったことがBlog - Next.js 13で紹介されていた。これは地味に嬉しい。

before

<Link href="/about">
  <a>About</a>
</Link>

after

<Link href="/about">
  About
</Link>

毎度aタグを追加するのが面倒だったので、今までは以下のようなラッパーコンポーネントを用意していたが、不要になったので削除できた🎉

EnhancedLink.tsx
const EnhancedLink = (props) => {
  const { children, className = '', pathname, query, target } = props

  return (
    <Link
      href={{
        pathname,
        query,
      }}
    >
      <a {...{ className }} target={target}>
        {children}
      </a>
    </Link>
  )
}
トラハック@toraco株式会社トラハック@toraco株式会社

app Directory

Next.js 13の目玉機能だけどまだbetaなのでproductionでは使わない。
とはいえ近々alphaになるやろと思って今のうちにキャッチアップしておく。

https://nextjs.org/blog/next-13#new-app-directory-beta

pages Directory を app Directory に移行

今まで file based routing を担っていた pages Directory を app Directory に変更。
app Directory 内のファイルは全て Server Component として扱われるとのこと。

出たな、Server Component。(以下、RSC)
RSCは今まで触っていなかったので各種ドキュメントを読んでみた。
最初は Server Side Rendering (SSR) との区別がつかなかったが別物だった。

Next.jsにおいて、Server Components では colocated data fetching が強みらしい。
https://beta.nextjs.org/docs/data-fetching/fundamentals

ちなみに app Directory ではgetServerSidePropsgetStaticProps, getInitialPropsなどの既存のデータ取得方法は使えない。

Previous Next.js data fetching methods such as getServerSideProps, getStaticProps, and getInitialProps are not supported in the new app directory.

もしそれらを使いたければ pages Directory を使う必要があるようだ。
※ app Directory と pages Directory は共存できる。

Server Components を使うと何が嬉しいのか

気力があればまとめる

Fetch API

気力があればまとめる

トラハック@toraco株式会社トラハック@toraco株式会社

Cloud Run へのデプロイ

Next.jsの実行環境として Cloud Run を採用しているため、影響があったら嫌だなーと思っていたけどやっぱりあった。
前提として、Dockerのマルチステージングビルドをしている。

ポート番号のエラー

ビルドは正常終了したが、container imageをdeployするときにエラーとなった。

ERROR: (gcloud.run.deploy) The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable. Logs for this revision might contain more information.

デフォルトの8080番ポートに繋ごうとしているが接続できねーぞとのこと。
docker関連ファイルを見直す。

docker-compose.yaml
   runner:
     container_name: nextjs-app-multistage-runner
     profiles:
       - donotstart
     stdin_open: true
     ports:
       - target: 3000
-        published: 3000
+        published: 8080
         protocol: tcp
Dockerfile
-EXPOSE ${NEXTJS_APP_PORT:-3000}
+EXPOSE 3000
 
+ENV PORT 3000
 
 WORKDIR /workspace/packages/app

-CMD ["yarn", "start", "-p", "${NEXTJS_APP_PORT:-3000}"]
+CMD ["yarn", "start"]

portsの指定については以下を参照した。
https://docs.docker.jp/compose/compose-file/compose-file-v3.html#ports

yarn install と build の時間が延びた

cacheが効いていない?
-> cacheだった。

初回

  • yarn install: 10m 36s
  • build: 5m くらい

2回目

  • yarn install: 7m 29s
  • build: 4m くらい