Next.jsとSpringのモノレポ構築メモ(お蔵入り)
1. 環境
- maxOS Venture 13.7.2
2. Node.jsをインストール
brewでnodebrewをインストール
brew install nodebrew
/usr/local/opt/nodebrew/bin/nodebrew setup_dirs
echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zshrc
nodebrewでNode.jsをインストール
nodebrew install v22.12.0
nodebrew use v22.12.0
3. 新規プロジェクト作成
リポジトリの初期化
まず、新しいGitリポジトリを作成します。
PROJECT_HOME=`pwd`/my-project
mkdir -p $PROJECT_HOME
cd $PROJECT_HOME
git init
パッケージマネージャーの選定と初期化
ここでは pnpm を使用する前提で進めます。
pnpmのインストールと初期化
# pnpmのインストール(まだインストールしていない場合)
npm install -g pnpm
# pnpmワークスペースの初期化
pnpm init
package.jsonを編集
package.json
{
"name": "my-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"packageManager": "pnpm@9.15.1",
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"deploy": "turbo run deploy"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"turbo": "^2.3.3"
}
}
Turborepoのセットアップ
Turborepoをインストールし、設定ファイルを作成します。
pnpm add -D turbo
turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"lint": {
"outputs": []
},
"typecheck": {
"dependsOn": ["lint"],
"outputs": []
},
"test": {
"dependsOn": ["typecheck"],
"outputs": ["**/coverage/**"]
},
"build": {
"dependsOn": ["test", "^build"],
"outputs": [".next/**", "out/**", "dist/**", "build/**", "target/**"]
},
"export": {
"dependsOn": ["build"],
"outputs": ["out/**"]
},
"deploy": {
"dependsOn": ["export"],
"outputs": [],
"cache": false
}
}
}
ワークスペース設定
ほわっつ ワークスペース
pnpm-workspace.yaml
を作成し、以下を追加します。
packages:
- "apps/*"
- "packages/*"
- "infrastructure/*"
- "config/*"
ディレクトリ構成の作成
ディレクトリ構造を作成します。
mkdir -p apps/web apps/api apps/batch packages/openapi packages/shared-ts-utils packages/shared-java-utils infrastructure/docker infrastructure/k8s infrastructure/terraform config scripts
ここまでこんな感じ
.
├── apps
│ ├── api
│ ├── batch
│ └── web
├── config
├── infrastructure
│ ├── docker
│ ├── k8s
│ └── terraform
├── node_modules
│ └── turbo -> .pnpm/turbo@2.3.3/node_modules/turbo
├── package.json
├── packages
│ ├── openapi
│ ├── shared-java-utils
│ └── shared-ts-utils
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
└── turbo.json
17 directories, 4 files
各アプリケーションの初期設定
フロントエンド(Next.js/TypeScript)のセットアップ
apps/web ディレクトリに移動し、Next.jsアプリを作成します。ここでは create-next-app を使用します。
cd $PROJECT_HOME/apps/web
pnpm create next-app@latest . --typescript --no-tailwind --eslint --app --src-dir --import-alias "@/*" --use-pnpm --no-turbopack
必要なパッケージを追加します。
pnpm add -D eslint prettier eslint-config-prettier eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y @typescript-eslint/parser @typescript-eslint/eslint-plugin
pnpm add redux @reduxjs/toolkit react-redux
pnpm add -D jest @testing-library/react @testing-library/jest-dom
pnpm add next-pwa
next.config.js の設定
PWA機能を有効にするために next-pwa を設定します。
apps/web/next.config.js
// apps/web/next.config.js
import withPWA from 'next-pwa';
export default withPWA({
dest: 'public',
disable: process.env.NODE_ENV === 'development',
// 他のNext.js設定
exportTrailingSlash: true,
trailingSlash: true,
reactStrictMode: true,
// 必要に応じて他の設定を追加
});
package.json のモジュールタイプ設定
ES6モジュールを使用するために、package.json に "type": "module" を追加します。
// apps/web/package.json
{
"name": "web",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"export": "next export",
"build-and-export": "next build && next export",
"start": "next start",
"test": "jest",
"deploy": "pnpm run build-and-export && pnpm run deploy"
},
// 他の設定
}
### サーバーサイド(Java/Spring)のセットアップ
apps/api ディレクトリに移動し、Spring Bootプロジェクトをセットアップします。ここでは Spring Initializr を使用します。
```zsh
cd $PROJECT_HOME/apps/api
curl https://start.spring.io/starter.zip \
-d dependencies=web,data-jpa,security,actuator \
-d javaVersion=21 \
-d type=gradle-project-kotlin \
-d groupId=com.example \
-d artifactId=api \
-o api.zip
unzip api.zip -d .
rm api.zip
必要な依存関係を追加します。build.gradle を編集し、springdoc-openapi や micrometer などのライブラリを追加します。
build.gradle.kts
ファイルの implements
の配下に以下を追加
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
implementation(platform("io.micrometer:micrometer-bom:1.14.2"))
implementation("implementation 'io.micrometer:micrometer-registry-datadog")
共有パッケージのセットアップ
OpenAPIスキーマ管理
packages/openapi にOpenAPIスキーマとコード生成設定を追加します。
cd $PROJECT_HOME/packages/openapi
mkdir codegen generated
# スキーマファイルを `schema.yaml` として追加
touch schema.yaml
共有TypeScriptユーティリティ
packages/shared-ts-utils を初期化します。
cd $PROJECT_HOME/packages/shared-ts-utils
pnpm init
pnpm add -D typescript
touch tsconfig.json # 設定はあとで
tsconfig.json の設定
`packages/shared-ts-utils/tsconfig.json
{
"compilerOptions": {
"target": "ES6",
"module": "ES6",
"declaration": true,
"outDir": "dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"]
}
共有Javaユーティリティ
packages/shared-java-utils を初期化します。
cd $PROJECT_HOME/packages/shared-java-utils
curl https://start.spring.io/starter.zip \
-d javaVersion=21 \
-d type=gradle-project-kotlin \
-d groupId=com.example \
-d artifactId=shared-java-utils \
-o shared-java-utils.zip
unzip shared-java-utils.zip -d .
rm shared-java-utils.zip
インフラ設定
Docker設定
infrastructure/docker に各アプリケーションの Dockerfile を作成します。
フロントエンドのDockerfile
Next.js用のDockerfileを作成します。PWA機能を有効にするための設定も含めます。
ここ、本番ステージのところ後で確認
infrastructure/docker/apps/web/Dockerfile
# apps/web/Dockerfile
# ビルドステージ
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install
COPY . .
RUN pnpm run build-and-export
# 本番ステージ
FROM nginx:alpine
COPY /app/out /usr/share/nginx/html
COPY /app/public /usr/share/nginx/html/public
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
サーバーサイドのDockerfile
infrastructure/docker/apps/api/Dockerfile
FROM openjdk:21-jdk-alpine
VOLUME /tmp
COPY build/libs/api-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Docker Composeの設定
infrastructure/docker/docker-compose.yml を作成します。
ymlのバージョンが古そうなので確認
name: my-web
services:
api:
build:
context: ../../apps/api
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: dev
AWS_COGNITO_CLIENT_ID: your_client_id
AWS_COGNITO_REGION: your_region
depends_on:
- db
web:
build:
context: ../../apps/web
dockerfile: Dockerfile
ports:
- "80:80" # Nginxがポート80をリッスン
environment:
NODE_ENV: production
NEXT_PUBLIC_API_URL: http://localhost:8080/api
depends_on:
- api
db:
image: postgres:13
environment:
POSTGRES_USER: your_user
POSTGRES_PASSWORD: your_password
POSTGRES_DB: your_db
ports:
- "5432:5432"
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
CI/CD設定(参考)
共通設定ファイルの追加
ESLintとPrettier設定 (config/ ディレクトリ)
config/eslint.config.js を作成します。
フォーマットが古いっぽいんであとでかくにん
// config/eslint.config.js
export default {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'prettier'
],
plugins: ['react', '@typescript-eslint'],
env: {
browser: true,
es2021: true,
node: true
},
settings: {
react: {
version: 'detect'
}
},
rules: {
// カスタムルールを追加
}
};
config/prettier.config.js を作成します。
Turborepoスクリプト (scripts/ ディレクトリ)
必要に応じて、共通のビルドやデプロイスクリプトを scripts/ ディレクトリに追加します。
内容はあとで精査
# scripts/build-all.sh
#!/bin/bash
pnpm turbo run build
# scripts/deploy-web.sh
#!/bin/bash
# 必要な環境変数の確認
if [ -z "$CMS_API_ENDPOINT" ] || [ -z "$CMS_API_KEY" ]; then
echo "必要な環境変数が設定されていません。"
exit 1
fi
# 静的ファイルのビルドとエクスポート
pnpm --filter web run build-and-export
# CMSへのアップロード(例: Headless CMSのAPIを使用)
# ここでは、一般的なHTTP POSTリクエストを使用した例を示します。具体的なCMSに応じて適宜調整してください。
curl -X POST "$CMS_API_ENDPOINT" \
-H "Authorization: Bearer $CMS_API_KEY" \
-F "file=@apps/web/out/*" \
-F "path=/desired/path/on/cms"
echo "フロントエンドのデプロイが完了しました。"
スクリプトに実行権限を付与します。
chmod +x scripts/*.sh
リポジトリの運用方法
モノレポ構成の維持
- ディレクトリ構成の遵守: apps/ 配下にアプリケーション、packages/ 配下に共有ライブラリ、infrastructure/ にインフラ関連の設定を配置します。
- 共有コードの再利用: packages/shared-ts-utils や packages/shared-java-utils などの共有ライブラリを活用し、コードの重複を避けます。
Turborepoによるタスク管理
turbo.json に定義されたタスクを活用して、ビルド、テスト、エクスポート、デプロイなどを効率的に実行します。
主なタスクの実行例
- Lintの実行
pnpm turbo run lint
- ビルドとエクスポートの実行
pnpm turbo run build
pnpm turbo run export
- デプロイの実行
pnpm turbo run deploy --filter web
Discussion