🧚

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

2020/12/23に公開
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で遊んでみることにする

Discussion

catnosecatnose

Next.js on Firebase以前試したことがあるのですが、パフォーマンス的にSSRが厳しいのですよね。

リージョンを変えると動かない件、ドキュメントにしれっと書かれていますね…。

Firebase Hosting に接続されている関数は us-central1 に配置する必要があります。
Cloud Functions のロケーション

SSRをしなくても良いのであればNext.jsのStatic HTML Exportで生成された静的ファイルをそのままFirebase Hostingにあげて、動的なデータはクライアントでのマウント後にSWRなどで取得するのが良いのかも?🤔

ucworkucwork

コメントありがとうございます!

リージョンを変えると動かない件、ドキュメントにしれっと書かれていますね…。

近い方がいいでしょ。と思い無邪気にリージョン変えてみましたがちゃんとドキュメントに書いてあったんですね😫

SSRをしなくても良いのであればNext.jsのStatic HTML Exportで生成された静的ファイルをそのままFirebase > Hostingにあげて、動的なデータはクライアントでのマウント後にSWRなどで取得するのが良いのかも?🤔

とりあえず公式のexampleを見つけたんでやってみたんですが
かなり重かったのでコメントいただいたとおりnext exportしてFIrebase Hostingにデプロイ
APIはSWRで呼び出す作戦をまさにローカルで試みてました🧔

実験したらまた自分の勉強メモようにまとめようと思います🙇‍♂️