技術選定の勉強になるBulletproof Reactの起動と使い方の解説

Bulletproof Reactとは

Bulletproof ReactはReact (Next.js)のアプリケーションを構築するための、ベストプラクティス集+ディレクトリ構成テンプレートです。

ディレクトリ構成が整理されており、例として、react-vite内のpackage.jsonを見ると、vitest, playwright, eslintなどが含まれており、かなりしっかりとしたチェックやテストができる環境になっていることがわかります。

package.json
{
  "name": "bulletproof-react-vite",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "tsc && vite",
    "build": "tsc && vite build --base=/",
    "preview": "vite preview",
    "test": "vitest",
    "test-e2e": "pm2 start \"yarn run-mock-server\" --name server && yarn playwright test",
    "prepare": "husky",
    "lint": "eslint src --ignore-path .gitignore",
    "check-types": "tsc --project tsconfig.json --pretty --noEmit",
    "generate": "plop",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build",
    "run-mock-server": "vite-node mock-server.ts | pino-pretty -c"
  },
  // 以下略
}

非常に参考になる構成ですが、初心者の方だとこれらの構成の意図や使い方がわからず、起動すらできないこともあります。

そのため、この記事ではBulletproof Reactの起動方法や使用されている便利なツールなどについて解説しています。

Bulletproof Reactのダウンロード

まず、git cloneコマンドで任意の場所にダウンロードします。

git clone https://github.com/alan2207/bulletproof-react.git

ダウンロードしたらcdコマンドでbulletproof-react/appsに移動して、lsコマンドを実行すると3つのディレクトリが確認できます。

cd apps
ls
nextjs-app   nextjs-pages react-vite

この3つのディレクトリはそれぞれ以下のように分けられています。

nextjs-appNext.js 13以降のApp Router
nextjs-pages旧来のNext.jsのPages Router
react-viteReact + Viteを使ったSPA

旧来のNext.jsのPages Routerで新規プロジェクトを作成することはほぼないので、Next.jsの技術選定の勉強にはnextjs-app、Reactの場合はreact-viteを参考にします。

この記事は初心者向けのため、react-viteでの起動方法などについてのみ解説します。

最初にcdコマンドでreact-viteに移動してください。

cd react-vite
pwd
/Users/iwb/Desktop/bulletproof-react/apps/react-vite

Bulletproof Reactのインストール

Bulletproof Reactではyarnコマンドを使用するため、yarn --versionを実行してバージョンが表示されない場合は以下のコマンドで事前にインストールしてください。

yarn --version
1.22.22
npm install --global yarn

最近はnpm, pnpm, deno, bunなどが使われることが多いので、yarnを使ったことがない方が多いと思います。

使い方はnpmと大体同じで、パッケージのインストールなどに利用します。

インストールはcdでreact-viteに移動していれば、yarnを入力して実行するだけです。

yarn
yarn install v1.22.22
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning " > @testing-library/user-event@14.5.2" has unmet peer dependency "@testing-library/dom@>=7.21.4".
warning Workspaces can only be enabled in private projects.
warning Workspaces can only be enabled in private projects.
[4/4] 🔨  Building fresh packages...
$ husky
  Done in 9.52s.

yarnはnpmよりも処理が早く、「yarn run dev」は「yarn dev」のように「run」を省略して実行できるということだけ知っておけば、この記事の範疇では問題ありません。

Bulletproof Reactの起動

Bulletproof Reactを起動するにはreact-vite内の.env.exampleファイルを.envにリネームする必要があります。

.envファイルはアプリケーションの環境変数を定義するためのファイルです。

.envファイルが用意できたら、yarn devコマンドで起動してLocalのURLでブラウザでアクセスできるようになります。

yarn dev
yarn run v1.22.22
$ vite

  VITE v5.2.11  ready in 245 ms

    Local:   http://localhost:3000/
    Network: use --host to expose
    press h + enter to show help
Browserslist: caniuse-lite is outdated. Please run:
  npx update-browserslist-db@latest
  Why you should do it regularly: https://github.com/browserslist/update-db#readme

「Browserslist: caniuse-lite is outdated.」と表示される場合は「npx update-browserslist-db@latest」でアップデートすると表示されなくなります。

npx update-browserslist-db@latest

起動してlocalhost:3000にアクセスすると、下図のような画面が表示されます。

技術選定の勉強になるBulletproof Reactの使い方の解説

右下に表示される🏝️アイコンはTanStack React Queryのデベロッパーツールを開くためのボタンです。

TanStack React Queryとはサーバー状態管理に特化したライブラリです。

ログインした状態でボタンを押すと、ログイン中のユーザーのデータなどを確認できます。

TanStack React Queryとはサーバー状態管理に特化したライブラリ

Get startedを押してログイン画面を開く

トップページに表示されている「Get started」のボタンを押すと未ログインであればログインページに遷移します。

Bulletproof React ログイン画面

ログイン画面のRegisterのリンクをクリックすると登録画面に遷移します。

Bulletproof React 登録画面

この画面で登録しても、登録情報は使用しているデバイスのローカルストレージに保存されるので情報は外部に送信されないです。

試しに必要事項を入力して登録してみてください。登録するとログイン状態となり、ダッシュボード画面に遷移します。

ログイン中はログイン画面のURLにアクセスしようとするとダッシュボード画面にリダイレクトされます。

ログイン画面URL
http://localhost:3000/auth/login

ダッシュボード画面URL
http://localhost:3000/app

ダッシュボード画面の左メニューからはディスカッション画面とユーザー画面に遷移できます。

Bulletproof React ダッシュボード画面
Bulletproof React ディスカッション画面
Bulletproof React ユーザー画面

これらの画面のファイルは react-vite/src/app 内にあるので、どのようなコードで作られているのか編集しながら学習できます。

例えば、トップページのファイルは react-vite/src/app/routes/landing.tsx なので、コード内の「Showcasing Best Practices For Building React Applications」を「Reactの勉強中」に変更して保存すれば画面に反映されます。

landing.tsx
<img src={logo} alt="react" />
<p>Reactの勉強中</p>

ビルドしてサーバーにアップロードする

「yarn build」コマンドを実行するとdistディレクトリにファイルがビルドされ、アップロードできるようになります。

試しにNetlifyにログインして、ビルドされたdistディレクトリをドラッグ&ドロップするとBulletproof Reactが下記のようなURLにアクセスして確認可能になります。

https://taupe-salmiakki-31edfb.netlify.app

ちなみにビルドしたものをローカルで確認する場合は「yarn preview」コマンドを使用します。

ビルド時のtscについて

package.jsonのscriptsのbuildを確認すると「vite build」の前に「tsc」が実行されていることが確認できます。

tscとはTypeScript Compilerという型チェックと.jsファイルの変換コマンドです。

package.json
{
  "name": "bulletproof-react-vite",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build --base=/",
    // 以下略
  }
}

tsc CLI Options

ビルドだけならvite buildのコマンドだけでもできますが、vite buildコマンドはTypeScriptの型エラーが発生していても無視してビルドできてしまいます。

そのため、ビルドの前にtscで型チェックを行い、型エラーがある場合はビルドされないようになっています。

例えば、landing.tsx内の「user」に「user: string」という間違った型にした状態でビルドを実行すると、型が間違っているためtscの型エラーとなるのでビルドできません。

yarn build
yarn run v1.22.22
$ tsc && vite build --base=/
src/app/routes/landing.tsx:11:9 - error TS2322: Type 'UseQueryResult<User, unknown>' is not assignable to type 'string'.
  Type 'QueryObserverRefetchErrorResult<User, unknown>' is not assignable to type 'string'.

11   const user: string = useUser();

これを見ることでビルド前にはtscで型チェックするということが必要なことがわかります。

企業によってはビルド前にtscによる型チェックをせずにビルドしてしまっているケースがあるため、TypeScriptを使用している場合はビルドに型チェックが必要だということを覚えておくと良いです。

また、yarn buildコマンドは本番用ビルドなので、事前に.d.tsの型定義ファイルを生成する役割もあります。

Vitestについて

「yarn test」を実行するとVitestが実行されます。

yarn test

VitestとはViteプロジェクト向けに作られたテストフレームワークです。

Vitest | Next Generation testing framework

vitestのテストファイルは「__tests__」というディレクトリに入れられており、例えばseo内のhead.tsxのテストには src/components/seo/tests/head.test.tsx のファイルが使用されています。

head.test.tsx内のコードは以下のようになっており、titleおよびdescriptionを入れた際に想定通りの値が返ってくるかテストされています。

head.test.tsx
import { render, waitFor } from '@/testing/test-utils';

import { Head } from '../head';

test('should add proper page title and meta description', async () => {
  const title = 'Hello World';
  const titleSuffix = ' | Bulletproof React';
  const description = 'This is a description';

  render(<Head title={title} description={description} />);
  await waitFor(() => expect(document.title).toEqual(title + titleSuffix));

  const metaDescription = document.querySelector("meta[name='description']");

  expect(metaDescription?.getAttribute('content')).toEqual(description);
});

もし、head.tsx内のコードが以下のようにdescriptionのmetaタグが抜けているコードになっている場合は、前述のテストの結果はエラーになります。

head.test.tsx
import { Helmet, HelmetData } from 'react-helmet-async';

type HeadProps = {
  title?: string;
  description?: string;
};

const helmetData = new HelmetData({});

export const Head = ({ title = '' }: HeadProps = {}) => {
  return (
    <Helmet
      helmetData={helmetData}
      title={title ? `${title} | Bulletproof React` : undefined}
      defaultTitle="Bulletproof React"
    ></Helmet>
  );
};
yarn test
yarn run v1.22.22
$ vitest

 DEV  v2.1.4 /Users/iwb/Desktop/bulletproof-react/apps/react-vite

  src/hooks/__tests__/use-disclosure.test.ts (4)
  src/lib/__tests__/authorization.test.tsx (4) 894ms
  src/components/seo/__tests__/head.test.tsx (1)
   × should add proper page title and meta description

pm2とPlaywrightについて

「yarn test-e2e」を実行するとpm2とPlaywrightが実行されます。

yarn test-e2e

Bulletproof ReactのPlaywrightのバージョンが古くて、そのままだとE2Eテストがエラーになるので、初回は実行前に以下のコマンドでPlaywrightを最新化する必要があります。

yarn playwright install

pm2はモックサーバーをバックグラウンドで起動するプロセス管理を行うためのツールです。

「yarn dev & yarn playwright test」だとE2Eテストを安定して回せないため、pm2を使用してモックサーバーをバックグラウンドで起動するほうが安定して起動できて管理もしやすいです。

試しにinput.tsxの<input>にdisabledを追加してから「yarn test-e2e」を実行すると、入力不可になるため以下のようにE2Eテストが失敗します。

  14 |   // registration:
> 15 |   await page.getByLabel('First Name').click();
     |                                       ^
  16 |   await page.getByLabel('First Name').fill(user.firstName);
  17 |   await page.getByLabel('Last Name').click();
  18 |   await page.getByLabel('Last Name').fill(user.lastName);

package.jsonのprepareについて

package.jsonには「"prepare": "husky",」という記述があります。

package.json
{
  "name": "bulletproof-react-vite",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build --base=/",
    "preview": "vite preview",
    "test": "vitest",
    "test-e2e": "pm2 start \"yarn run-mock-server\" --name server && yarn playwright test",
    "prepare": "husky",
    // 略
  }
}

これを見て、「yarn prepare」を実行して「husky」を実行するのだろうと考える方がいると思いますが、それは間違いです。

prepareスクリプトはyarn installを実行したあとに、自動的に呼び出されるフックです。

よって、「yarn install」実行後に自動的に実行させるものなので、開発者が「yarn prepare」を直接実行するのは正しい使い方ではないです。

試しに「"prepare": "echo Hello",」に書き換えて「yarn」コマンドを実行すると、yarn installの処理後に「Hello」が出力されます。

yarn
yarn install v1.22.22
[1/4] 🔍  Resolving packages...
success Already up-to-date.
$ echo Hello
Hello
  Done in 0.18s.

Huskyについて

HuskyとはGitフック(コミット前やプッシュ前に特定の処理を走らせる仕組み)を簡単に設定できるツールです。

現在のBulletproof ReactではHuskyがコミット時に実行されないので、使用する場合は公式サイトのドキュメントを参照してください。

Husky

ESLintについて

「yarn lint」を実行するとESLintが実行されます。

head.tsxのコードのconstをvarに変えて、「yarn lint」を実行するとESLintのエラーとして検出されます。

yarn lint
yarn run v1.3.2
$ eslint src --ignore-path .gitignore

/Users/iwb/Desktop/bulletproof-react/apps/react-vite/src/components/seo/head.tsx
  8:1  error  Unexpected var, use let or const instead  no-var

 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

ESLintの対象は「src」配下のディレクトリとなっており、「--ignore-path .gitignore」により、.gitignoreに記載されているファイルは対象外になっています。

yarn check-typesコマンドについて

「yarn check-types」コマンドでtscを以下のオプションで実行できます。

"check-types": "tsc --project tsconfig.json --pretty --noEmit",

各オプションは以下の目的で付けられています。

  • --project tsconfig.json: 使用するtsconfig.jsonファイルの指定
  • --pretty: エラーメッセージを色付きの見やすいフォーマットで出力
  • --noEmit: コンパイルしてもJavaScript の出力ファイルを作らない

上記のオプションからもわかるように、check-typesはTypeScriptのコンパイルではなく「型チェック専用」として使われています。

check-typesなら型チェックのみ(コンパイルなし)なので高速で処理できるため、ビルド前の方チェックはこの方法がベストプラクティスになっています。

plopについて

「yarn generate」コマンドでplopを実行できます。

plopとは簡単にコードやファイルのテンプレートを自動生成できるツールです。

「yarn generate」コマンドを実行するとコンポーネント名と保存場所を聞かれるので、それぞれを入力すると指定した場所にディレクトリとファイルが作成されます。

yarn generate
yarn run v1.22.22
$ plop
? component name modal
? Which feature does this component belong to? components
? folder in components ui
  ++ /src/components/ui/modal/index.ts
  ++ /src/components/ui/modal/modal.tsx
  ++ /src/components/ui/modal/modal.stories.tsx
  Done in 17.80s.

react-vite/generators/component/index.cjs に設定ファイルがあり、以下のような設定によりディレクトリとファイルが出力されるようになっています。

index.cjs
const path = require('path');
const fs = require('fs');

const featuresDir = path.join(process.cwd(), 'src/features');
const features = fs.readdirSync(featuresDir);

/**
 *
 * @type {import('plop').PlopGenerator}
 */
module.exports = {
  description: 'Component Generator',
  prompts: [
    {
      type: 'input',
      name: 'name',
      message: 'component name',
    },
    {
      type: 'list',
      name: 'feature',
      message: 'Which feature does this component belong to?',
      choices: ['components', ...features],
      when: () => features.length > 0,
    },
    {
      type: 'input',
      name: 'folder',
      message: 'folder in components',
      when: ({ feature }) => !feature || feature === 'components',
    },
  ],
  actions: (answers) => {
    const componentGeneratePath =
      !answers.feature || answers.feature === 'components'
        ? 'src/components/{{folder}}'
        : 'src/features/{{feature}}/components';
    return [
      {
        type: 'add',
        path: componentGeneratePath + '/{{kebabCase name}}/index.ts',
        templateFile: 'generators/component/index.ts.hbs',
      },
      {
        type: 'add',
        path: componentGeneratePath + '/{{kebabCase name}}/{{kebabCase name}}.tsx',
        templateFile: 'generators/component/component.tsx.hbs',
      },
      {
        type: 'add',
        path: componentGeneratePath + '/{{kebabCase name}}/{{kebabCase name}}.stories.tsx',
        templateFile: 'generators/component/component.stories.tsx.hbs',
      },
    ];
  },
};

Storybookについて

「yarn storybook」コマンドでStorybookを起動できます。

StorybookとはUIコンポーネントのカタログツールです。

起動すると http://localhost:6006/ にアクセスすることで、各コンポーネントがドキュメント化された状態で確認できます。

StorybookとはUIコンポーネントのカタログツール

前述のplop実行時にmodal.stories.tsxというファイルが生成されていましたが、Storybookのコンポーネントはxxxx.stories.tsxのファイルでコンポーネントを読み込んで設定することで画面上に表示できます。

storybook buildについて

「yarn build-storybook」コマンドでStorybookをビルドできます。

ビルドしたStorybookはアップロードすればブラウザでアクセスできるようになります。

https://comforting-sorbet-d2968f.netlify.app

run-mock-serverについて

「yarn run-mock-server」コマンドでAPIのモックサーバーを起動できます。

APIのURLは.envにありますが、デフォルトだと「https://api.bulletproofapp.com」になっているので、「http://localhost:8080/api」に変更してください。

.env
VITE_APP_API_URL=http://localhost:8080/api
VITE_APP_ENABLE_API_MOCKING=true

この状態で「yarn run-mock-server」を実行して、http://localhost:8080/api/healthcheck にアクセスすれば、{ "ok": true } が返ってきます。

react-vite/src/testing/mocks/handlers/index.ts にモックAPIのコードが書かれているので、例えば /api/date を追加したい場合は以下のようになります。

index.ts
export const handlers = [
  ...authHandlers,
  ...commentsHandlers,
  ...discussionsHandlers,
  ...teamsHandlers,
  ...usersHandlers,
  http.get(`${env.API_URL}/healthcheck`, async () => {
    await networkDelay();
    return HttpResponse.json({ ok: true });
  }),
  http.get(`${env.API_URL}/date`, async () => {
    return HttpResponse.json({ date: new Date().toISOString() });
  }),
];

まとめ

Bulletproof Reactのpackage.jsonのscriptsは以下のようになっています。

はじめは「yarn dev」で開発環境を起動するなど、各scriptsを実行して試してみると学びが多いです。

package.json
{
  "name": "bulletproof-react-vite",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build --base=/",
    "preview": "vite preview",
    "test": "vitest",
    "test-e2e": "pm2 start \"yarn run-mock-server\" --name server && yarn playwright test",
    "prepare": "husky",
    "lint": "eslint src --ignore-path .gitignore",
    "check-types": "tsc --project tsconfig.json --pretty --noEmit",
    "generate": "plop",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build",
    "run-mock-server": "vite-node mock-server.ts | pino-pretty -c"
  },
  // 中略
}

scriptsを一通り確認したら、次はpackage.jsonのdevDependenciesから使用されているパッケージを確認したり、ファイル構造や使用されているコードがどうなっているか調べながら学ぶと良いと思います。(すべての内容は書ききれないので割愛します)