Next.jsをFirebase Hostingにデプロイする

5 min read読了の目安(約4600字 2

概要

Next.jsを雰囲気で書いてみたのでデプロイしてみたい

公式を見るとVercelにあげるのがレコメンされてますが
会社ではGCPよく使うので、Firebase Hostingにあげたい!と思いやってみたメモ

参考にしたサイト

公式のExampleによるとSSR(getServerSideProps)させるためか
Firebase HostingへのリクエストをCloud Function For Firebase に流して
Node.jsに実行させてるみたい...
とりあえず素直に公式に従ってやってみる

手順

ディレクトリ構成

こんな感じのディレクトリ構成
Next.jsの公式とかみて適当なpages用意してやってください
※今回と直接関係ないディレクトリもあります🙇‍♂️

directory
.
├── LICENSE
├── README.md
├── components
├── docker
│   └── nextjs
│       └── Dockerfile
├── docker-compose.yml
├── firebase.json
├── firebaseFunctions.js
├── hooks
├── next.config.js
├── node_modules
├── package-lock.json
├── package.json
├── pages
├── public
├── styles
└── tsconfig.json

docker-composeでとりあえずローカル環境準備

docker-compose.yml
version: '3.8'
services:
  nextjs:
    build: ./docker/nextjs
    container_name: nextjs
    volumes:
      - ./:/usr/src/app
    command: "npm run dev"
    ports:
      - "3000:3000"
./docker/nextjs/Dockerfile
FROM node:15.4.0-alpine3.10

WORKDIR /usr/src/app

上記ファイル準備してビルド&起動

docker-compose up -d --build

Cloud Functionsで実行するファイル用意

Firebase HostingへのリクエストがこっちにきてNext.jsを捌いてくれる
distDirで.next指定してるので、next buildで出力された.nextディレクトリを指定

firebaseFunctions.js
const { https } = require("firebase-functions");
const { default: next } = require("next");

const isDev = process.env.NODE_ENV !== "production";

const nextjsServer = next({
  dev: isDev,
  conf: {
    distDir: ".next",
  },
});
const nextjsHandle = nextjsServer.getRequestHandler();

exports.nextjsFunc = https.onRequest((req, res) => {
  return nextjsServer.prepare().then(() => nextjsHandle(req, res));
});

package準備

こんな感じで準備してnpm installすれば依存入ってきます
(ポイントとしては★でコメントした部分

package.json
{
  "name": "ucwork-frontend",
  "version": "0.1.0",
  "private": true,
  "main": "firebaseFunctions.js", // ★1 Cloud Function For Firebaseにデプロイするjsファイル
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "deploy": "firebase deploy --only functions,hosting", // ★2 こいつでFirebaseのHostingとFunctionsにデプロイ
    "logs": "firebase functions:log",  // ★3 なんかエラーあった時これで詳細見れるので便利
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },
  "dependencies": {
    "@material-ui/core": "^4.11.1",
    "@material-ui/icons": "^4.9.1",
    "firebase-admin": "^9.4.2",  // ★4 この辺はmustで必要
    "firebase-functions": "^3.13.0", // ★4 
    "i18next": "^19.8.4",
    "next": "10.0.3",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-i18next": "^11.8.3",
    "recharts": "^1.8.5"
  },
  "devDependencies": {
    "@babel/core": "^7.12.10",
    "@storybook/addon-actions": "^6.1.10",
    "@storybook/addon-essentials": "^6.1.10",
    "@storybook/addon-links": "^6.1.10",
    "@storybook/react": "^6.1.10",
    "@types/node": "^14.14.10",
    "@types/react": "^17.0.0",
    "babel-loader": "^8.2.2",
    "firebase-tools": "^9.0.1",
    "prettier": "^2.2.1",
    "typescript": "^4.1.2"
  }
}

firebase関連の設定

Firebase Functionsへのデプロイ前に
npm run buildnext build実行してる感じ

firebase.json
{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "**/**",
        "function": "nextjsFunc"
      }
    ]
  },
  "functions": {
    "source": ".",
    "predeploy": [
      "npm --prefix \"$PROJECT_DIR\" install",
      "npm --prefix \"$PROJECT_DIR\" run build"
    ],
    "runtime": "nodejs12"
  }
}

デプロイ

あとはnpm run deploy実行すると、こんな感じでdeploy成功する

npx firebase login --no-localhostでログインが必要かも

deploy
/usr/src/app # npm run deploy

> ucwork-frontend@0.1.0 deploy
> firebase deploy --only functions,hosting


=== Deploying to ...

...
✔  functions[nextjsFunc(us-central1)]: Successful update operation.
i  hosting[xxx]: finalizing version...
✔  hosting[xxx]: version finalized
i  hosting[xxx]: releasing new version...
✔  hosting[xxx]: release complete

✔  Deploy complete!

気になったポイント

コールドスタート重い

初回アクセス(しばらくアクセスしてない)時はCloud Functionsの起動に時間かかるのでかなり重め

https://github.com/vercel/next.js/discussions/11848

リージョン変えると動かない

Cloud Functionsデプロイリージョンをasia-northeast1(東京)
に変えたらブラウザでうまく表示されなくなった
Cloud Functionsのoriginはus-central1である必要があるみたい

https://github.com/firebase/firebase-tools/issues/842

まとめ

初回(結構時間経った時)アクセスするとかなり重いので
中途半端にSSRせずに、SWRでAPI連携することで、Firebase Hostingだけで動かないのかしら...🤔

ということでもう一声Next.jsで遊んでみることにする