🚀

【Next.js】チーム開発までの環境整備手順

2023/11/27に公開

はじめに

初めまして、現在エンジニアをしながら起業をしてサービスを開発中の橋田至です。

https://twitter.com/dall_develop

私は今Swappyという同人誌のフリマサイトを開発中です。
初めは一人で開発をしていたのですが、開発すべき機能が多すぎることに気づき、共同開発者を募集しました。

その結果、現在約10名ほどのメンバーがアクティブに開発しています。

チーム開発の難しさ

一人で開発するなら自分だけがコードを読めて理解できたら良いです。
しかし、チーム開発となると統一されたコーディング規約やルールが必要になります。

今回は、個人開発レベルをチームで行う際の環境整備手順を記事にしました。
参考になれば幸いです。

技術スタック

  • Next14(App Router)
  • TypeScript
  • Prisma
  • tailwindcss
  • daisy UI

他:MongoDB・Vercel・git,github・NextAuthなど

pull_request_templateの作成

PR作成時のテンプレートです。
以下例

<!-- この記号(<!-- と --‌>)で囲まれた部分はコメントです。この記号より外に書いてください。 -->
# What's changed

<!--
このプルリクで何をしたのかを記入してください。
画像とテキストを使って説明するのがおすすめです。
CodeRabbitがある程度作ってくれるので、人間がわかりづらい部分の補足を書きます -->

<!-- Fix #IssueNumber -->
<!-- 上記のコメントアウトを外してIssueNumberを入れると、PRがマージされたら自動的にそれに紐づくissueがクローズされるようになります -->

## Todo List

<!-- 今回のプルリクでまだやっていないことや、将来やる予定の事項を記入してください -->

<!-- 以下のうち今回のPRにあてはまるものをコメントの外に出してください
- [ ] デザイナーに確認してもらいます
- [ ] iOS 16のデバイスにテストします
- [ ] テストケースを書きます
-->

## Check List

- [ ] developブランチから派生しています
- [ ] テストが全て通りました
- [ ] ESLintはエラーや警告は出ませんでした
- [ ] `pnpm run build`を使ってエラーは出ませんでした
- [ ] CodeRabbitのレビューを対応し、対応済みのものはResolveしました
- [ ] UI系の変更を伴う場合はPCとスマホの画面のスクショを貼りました

## Remark

<!-- 補足事項 -->

ブランチ保護ルール

githubのリポジトリのsettingからブランチ保護ルールを設定できます。
公式のドキュメントを見ても位置が変わっていたりしたので注意が必要です。

.github/workflows/branch_restrictions.yml

name: Branch Restrictions

on:
  pull_request:
    branches:
      - main

jobs:
  check_base_branch:
    runs-on: ubuntu-latest
    steps:
      - name: Fail if not from develop branch
        run: |
          if [[ "${{ github.event.pull_request.head.ref }}" != "develop" ]]; then
            echo "Error: PRs to main must come from the develop branch."
            exit 1
          fi

READMEやwikiの作成

READMEには環境構築手順を記載し、wikiにその他情報をまとめました。

ESLintの設定

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: ["tsconfig.json"],
    tsconfigRootDir: __dirname,
    sourceType: "module",
  },
  plugins: ["@typescript-eslint/eslint-plugin", "tailwindcss", "jsdoc"],
  extends: [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "plugin:tailwindcss/recommended",
    "prettier",
    "plugin:jsdoc/recommended-typescript-error",
  ],
  root: true,
  env: {
    node: true,
    jest: true,
  },
  ignorePatterns: ["./next*", "**/*.js", "**/*.cjs", "pnpm-lock.yaml"],
  rules: {
    "no-restricted-syntax": [
      "error",
      "TSEnumDeclaration",
      "TSInterfaceDeclaration",
      "ForInStatement",
      "ForOfStatement",
      "LabeledStatement",
      "WithStatement",
      "VariableDeclaration[kind='let']",
    ],
    "@typescript-eslint/interface-name-prefix": "off",
    "@typescript-eslint/explicit-function-return-type": "off",
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "@typescript-eslint/no-explicit-any": "warn",
    "jsdoc/tag-lines": "off",
    "jsdoc/no-types": "off",
    "jsdoc/require-param": [
      "error",
      {
        checkDestructuredRoots: false,
      },
    ],
    "jsdoc/check-param-names": [
      "error",
      {
        checkDestructured: false,
      },
    ],
    "jsdoc/require-jsdoc": [
      "error",
      {
        publicOnly: true,
        require: {
          FunctionDeclaration: true,
          MethodDefinition: true,
          ClassDeclaration: true,
          ArrowFunctionExpression: true,
          FunctionExpression: true,
        },
      },
    ],
    "jsdoc/require-returns": "off",
    "jsdoc/require-returns-description": "off",
    "jsdoc/require-description": [
      "error",
      {
        descriptionStyle: "body",
      },
    ],
  },
};

Prettierの設定

tailwindcssを使用する場合は以下の記述を推奨

// prettier.config.js
module.exports = {
  plugins: ['prettier-plugin-tailwindcss'],
}

CodeRabbitのレビュー

AIがコードレビューしてくれるサービスです。
使用している感じ神ですが、たまに変な指摘をしてくるので、プロンプトは適宜修正することを推奨します。

.github/workflows/ai-review.yaml

name: ai-pr-reviewer

permissions:
  contents: read
  pull-requests: write

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches-ignore:
      - master
      - main
  pull_request_review_comment:
    types: [created]
  issue_comment:
    types: [created]

concurrency:
  group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}-${{ github.event_name == 'pull_request_review_comment' && 'pr_comment' || 'pr' }}
  cancel-in-progress: ${{ github.event_name != 'pull_request_review_comment' }}

jobs:
  review:
    runs-on: ubuntu-latest
    if: (github.event_name == 'issue_comment' && contains(github.event.comment.body, '[run review]') && github.event.issue.pull_request) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '[run review]')) || (github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'release') && !contains(github.event.pull_request.title, 'Release'))
    timeout-minutes: 15
    steps:
      - uses: coderabbitai/openai-pr-reviewer@latest
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        with:
          debug: false
          review_simple_changes: false
          review_comment_lgtm: false
          openai_light_model: gpt-4-1106-preview # 好みで変更
          openai_heavy_model: gpt-4-1106-preview # 好みで変更
          openai_timeout_ms: 900000 # 15分.
          language: ja-JP
          path_filters: |
            !db/**
            !**/*.lock
          system_message: |
            回答は日本語で書いてください。
            あなたは @coderabbitai(別名 github-actions[bot])で、OpenAIによって訓練された言語モデルです。
            あなたの目的は、非常に経験豊富なソフトウェアエンジニアとして機能し、コードの一部を徹底的にレビューし、
            以下のようなキーエリアを改善するためのコードスニペットを提案することです:
              - ロジック
              - セキュリティ
              - パフォーマンス
              - データ競合
              - 一貫性
              - エラー処理
              - 保守性
              - モジュール性
              - 複雑性
              - 最適化
              - ベストプラクティス: DRY, SOLID, KISS

            些細なコードスタイルの問題や、コメント・ドキュメントの欠落についてはコメントしないでください。
            重要な問題を特定し、解決して全体的なコード品質を向上させることを目指し、細かい問題は意図的に無視してください。
            以下は指摘しない:
              - use client,use server directive
              - function recieved by form action attribute
              - async component
              - Link > a
              - hooks export static component
              - async function throws error
              - React.cache
              - next/navigation
              - FaSquareXTwitter

            回答は日本語で書いてください。

          summarize: |
            次の内容でmarkdownフォーマットを使用して、最終的な回答を提供してください。

              - *ウォークスルー*: 全体の変更についての80語以内の高レベル要約。
              - *変更点*: ファイルと要約のテーブル。同様の変更を持つファイルを1行にまとめてスペースを節約。

            GitHubのプルリクエストにコメントとして追加されるこの要約には、追加のコメントを避けてください。
          summarize_release_notes: |
            このプルリクエストの目的とユーザーストーリーに焦点を当て、markdownフォーマットで簡潔なリリースノートを提供。変更は次のように分類し箇条書きにすること:
              "New Feature", "Bug fix", "Documentation", "Refactor", "Style",
              "Test", "Chore", "Revert"
            例えば:
            ```
            - New Feature: UIに統合ページが追加されました
            ```
            回答は50-100語以内にしてください。この回答はそのままリリースノートに使用されるので、追加のコメントは避けてください。

Slack(Discord)ルールの作成

私の場合は以下のようなチャンネルを作成しています。



VSCodeの設定

拡張機能のインストールを推奨します。

.vscode/extensions.json

{
  // 拡張機能がインストールされていない場合、インストールの推奨を表示
  "recommendations": [
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "github.vscode-pull-request-github",
    "vscode-icons-team.vscode-icons",
    "bradlc.vscode-tailwindcss"
  ]
}

.vscode/settings.json

{
  "editor.codeActionsOnSave": {
    "source.fixAll": true,
    "source.organizeImports": true
  },
  // デフォルトのフォーマッターに prettier を指定
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  // 保存時に prettier によって整形
  "editor.formatOnSave": true,
  // インポートを絶対パス優先に
  "typescript.preferences.importModuleSpecifier": "non-relative",
  "[prisma]": {
    "editor.defaultFormatter": "Prisma.prisma"
  }
}

最後に

その他にも「こうすれば良いよ!」などの設定などありましたらお声かけいただけると嬉しいです!

また、Swappyの開発者は随時募集しておりますので、気軽にDMください!!

https://twitter.com/dall_develop

https://ring-brand-8a1.notion.site/5948c0f2bd5e44548fd6e1f814b137d0?pvs=4

参考

https://zenn.dev/ukigumo_shiina3/articles/a862dc23b36cf0

https://zenn.dev/minedia/articles/7928ef7545b393

Discussion