筆者の制作物、執筆記事、業務経歴をまとめた Web サイトです。
本番運用はフロントエンドのみで、React.js を基盤とし MUI でインターフェイスを構築しています。
システムの展開と運用は GitHub で行っており、バックエンドに相当する処理は Google 内のサービスで代用しています。
Note
開発環境用のモック (バックエンド) については README.md をご参照ください。
| 種別 | 使用技術 |
|---|---|
| 開発言語 | TypeScript |
| JavaScript フレームワーク | React.js |
| CSS フレームワーク | MUI (Material UI) |
| テストフレームワーク | Jest |
| 多言語対応 | i18next |
| HTTP クライアント | Axios |
| バリデーター | Yup |
| リンター | ESLint , Secretlint |
| フォーマッター | Prettier |
| パッケージマネージャー | Yarn |
| ビルドツール | Vite |
| ER 図生成ツール | tbls |
| API 仕様書生成ツール | ReDoc |
| API テストツール | Newman |
| 性能テストツール | K6 |
| CI/CD | GitHub Actions |
| ホスティング | GitHub Pages |
| ライセンススキャン | FOSSA |
| 脆弱性スキャン | Snyk |
| カバレッジ計測 | Codecov |
| エラー解析 | Sentry |
| アクセス解析 | Google Analytics |
| バッチ処理 | Google Apps Script |
| データ永続化 | Google Spread Sheets |
| メール配信 | EmailJS , SendGrid |
Web サイトは GitHub Pages によってホスティングされています。
アクセス情報は Google Analytics で、エラー情報は Sentry で解析されます。
ページ内に表示される Qiita の記事は、Apps Script により自動取得されたものです。日次で収集されるこれらの情報は、DB の代替として永続化を担う Spread Sheets に蓄積されており、クライアントは Google Sheets API を介してレコードを抽出します。
また、問い合わせの送信はメールによって通知される方式で、この処理は EmailJS を介してフロントエンドから直接実行され SendGrid によりメール配信が行われます。
CI/CD は GitHub Actions によって実現されており、パイプラインは master ブランチへの merge をトリガーにスタートします。
静的解析 (ESLint)、UT (Jest)、IT (Newman)、性能テスト (K6)、ライセンススキャン (FOSSA)、脆弱性スキャン (Snyk) を順次行い、これらの検証をパスすればアプリをビルドします。
ビルドされたアプリは、ER 図 (tbls), API 仕様書 (ReDoc)、検証レポート類 (Codecov, Newman Reporter, K6 Reporter) と併せてデプロイされ、Web サイトが GitHub Pages にリリースされます。
連携された Slack からは、パイプラインのステータスを確認できます。
Warning
バックエンドはモックであるため本番運用には使用しません。運用を想定したコードの作成のみになります。
サーバでは CORS ポリシーや CSRF トークンによる検証が行われており、下記に処理の流れを示す。
sequenceDiagram
participant FE as Front-end<br/>[React.js]
participant BE as Back-end<br/>[Express.js]
%% 初期化処理
par
activate BE
BE->>BE: initialize<br/>Express.js
deactivate BE
and
activate FE
FE->>FE: initialize<br/>Axios (withCredentials)
deactivate FE
end
activate FE
%% CSRF トークン取得
rect rgba(0, 255, 255, 0.1)
note left of FE: get token
FE->>BE: GET /csrf-token<br/>[Axios]
activate BE
BE->>BE: create CSRF token<br/>[csurf]
BE->>FE: 200 OK<br/>Body: { csrf_token: "abc123..." }<br/>Set-Cookie: "csrf_secret=xyz789..."<br/>(httpOnly: true, secure: true, sameSite: lax)<br/>Access-Control-Allow-Credentials: true<br/>[Express.js]
deactivate BE
FE->>FE: save CSRF Secret from Cookie<br/>[Browser]
FE->>FE: save CSRF Token from Body<br/>[React.js]
end
%% CSRF トークン送信
rect rgba(255, 255, 0, 0.1)
note left of FE: send token
FE->>FE: set CSRF Token to Header<br/>[React.js]
FE->>FE: set CSRF Secret to Cookie<br/>[Browser]
%% プリフライトリクエスト
rect rgba(255, 255, 255, 0.1)
note left of FE: preflight request
FE->>BE: OPTIONS /send<br/>Access-Control-Request-Method: "POST"<br/>[Browser]
activate BE
BE->>FE: 204 NO CONTENT<br/>Access-Control-Allow-Origin: "https://example.com"<br/>Access-Control-Allow-Methods: "GET,POST,PUT,DELETE"<br/>Access-Control-Allow-Headers: "Content-Type,x-csrf-token"<br/>[cors]
deactivate BE
FE->>FE: check Access-Control<br/>[Browser]
opt not allowed
FE->>FE: Network Error<br/>[Browser]
end
end
FE->>BE: POST /send<br/>Content-Type: "application/json"<br>x-csrf-token: "abc123..."<br/>Cookie: "_csrf=xyz789..."<br/>[Axios]
activate BE
BE->>BE: verify CSRF Token<br/>with CSRF Secret<br/>[csurf]
opt invalid
BE->>FE: 403 Forbidden<br/>[csurf]
end
BE->>BE: run API process<br/>[Express.js]
BE->>FE: 200 OK<br/>Body: { success: true }<br/>[Express.js]
deactivate BE
end
deactivate FE
Warning
バックエンドはモックであるため本番運用には使用しません。運用を想定したコードの作成のみになります。
IaC は Terraform による AWS デプロイを想定し構築しています。
Express.js で開発された Node.js サーバを ECS で運用し、データベースに RDS を用いた場合のクラウド環境です。
開発環境でアプリケーションを動かす際は、GCP への疎通は行わず、API 処理をモックで代用します。モックは Express.js と MySQL を組み合わせた API サーバが Docker 上に展開されたもので、アプリケーションはこのサーバに対して疎通を試みます。
VS Code でのコーディングとテストのほか、Sourcetree でのバージョン管理、Docker での環境構築、Chrome での動作確認からなります。API サーバはローカルで実行するためのモックであり、これに対するテストやパフォーマンス測定に意義はありませんが、実際の開発現場に近い環境を用意したく必要なツール類を選定しています。
具体的なアプリは Brewfile を、ランタイムバージョンは .tool-versions をご参照ください。
| 種別 | 使用ツール |
|---|---|
| OS | macOS |
| パッケージ管理 | Homebrew |
| ランタイム管理 | asdf |
| JS パッケージ管理 | Yarn |
| Web ブラウザ | Google Chrome |
| Docker コンテナ管理 | Docker Desktop |
| IaC リソース管理 | Terraform |
| IaaS エミュレータ | LocalStack |
| コードエディタ | Visual Studio Code |
| OpenAPI エディタ | Stoplight Studio |
| Git クライアント | Sourcetree |
| DB クライアント | TablePlus |
| API クライアント | Postman |
| 性能テストツール | K6 |
| 作図ツール | draw.io |
GitHub 等のクラウドリソースにアクセスするための既存の MCP サーバの他、ソースコード内の特定のアノテーションコメントが付与された記載をナレッジとして生成 AI の情報源にさせる自作サーバも構築しています。
収集されたコメントはベクトルデータに変換され、 Python オブジェクト形式でシリアライズしキャッシュされます。
Note
MCP ナレッジサーバについては README.md をご参照ください。
プロダクトのディレクトリ構成を下記に示します。
app 配下がフロントエンド (React.js) 、mock 配下がバックエンドのモック (Express.js および MySQL) になります。
+--.github/ # GitHub 関連ファイル
| |
| +--instructions/ # GitHub Copilot カスタムインストラクション (用途別)
| |
| +--workflows/ # GitHub Actions ワークフロー
| |
| +--copilot-instructions.md # GitHub Copilot カスタムインストラクション (共通)
|
|
+--.vscode/ # VSCode 設定ファイル
|
|
+--app/ # React.js アプリケーション
| |
| +--public/ # 公開ファイル
| |
| +--src/ # ソースファイル
| | |
| | +--@types/ # 型定義ファイル
| | |
| | +--assets/ # 静的ファイル
| | |
| | +--components/ # ページで使用するコンポーネント
| | | |
| | | +--dialogs/ # ページに内包されるダイアログ
| | | |
| | | +--elements/ # ページに配置する部品
| | | |
| | | +--layouts/ # ページを構成するレイアウト
| | |
| | +--constants/ # 定数グループ
| | |
| | +--hooks/ # カスタムフック
| | |
| | +--lib/ # ライブラリラッパー
| | |
| | +--locales/ # 多言語情報
| | |
| | +--pages/ # ページ定義
| | |
| | +--schemas/ # バリデーション定義
| | |
| | +--utils/ # 汎用処理
|
|
+--docs/ # ドキュメント類
| |
| +--images/ # 画像ファイル
|
|
+--mcp/ # MCP サーバ
| |
| +--knowledge/ # ナレッジサーバ
|
|
+--mock/ # モック
| |
| +--aws/ # AWS 関連ファイル
| | |
| | +--localstack/ # LocalStack 関連ファイル
| | | |
| | | +--docker-compose.yml # LocalStack 用 Docker Compose 設定ファイル
| | |
| | +--terraform/ # Terraform 関連ファイル
| | | |
| | | +--environments/ # 環境別 Terraform 管理ファイル
| | | | |
| | | | +--dev/ # 開発環境
| | | | |
| | | | +--prd/ # 商用環境
| | | | |
| | | | +--stg/ # ステージング環境
| | | |
| | | +--modules/ # インフラモジュール
| | | | |
| | | | +--container/ # コンテナ関連 (ECS, ECR)
| | | | |
| | | | +--database/ # データベース関連 (RDS, bastion)
| | | | |
| | | | +--load_balancer/ # ロードバランサー関連 (ALB, WAF, DNS)
| | | | |
| | | | +--monitor/ # モニタリング関連 (CloudWatch, ChatOps)
| | | | |
| | | | +--network/ # ネットワーク関連 (VPC, Subnet, Endpoint)
| |
| +--docker/ # Docker 関連ファイル
| | |
| | +--mysql/ # MySQL 設定ファイル
| | |
| | +--sonarqube/ # SonarQube 関連ファイル
| |
| +--server/ # Express.js アプリケーション
| | |
| | +--prisma/ # Prisma スキーマ定義
| | |
| | +--src/ # ソースファイル
| | | |
| | | +--@types/ # 型定義ファイル
| | | |
| | | +--api/ # API 処理
| | | |
| | | +--lib/ # ライブラリラッパー
| | | |
| | | +--middlewares/ # ミドルウェア
| | | |
| | | +--responses/ # レスポンス型定義
| | | |
| | | +--routes/ # ルーティング
| | | |
| | | +--schemas/ # バリデーション定義
| |
| +--docker-compose.yml # モックサーバ用 Docker Compose 設定ファイル
| |
| +--Makefile # モックサーバ用コマンド
|
|
+--tools/ # ツール類
| |
| +--gas/ # GAS ソースファイル
| |
| +--k6/ # 負荷試験用テストコード
| |
| +--postman/ # API テストコード
| |
| +--swagger/ # OpenAPI 定義書生成仕様
| |
| +--tbls/ # DB 定義書生成仕様
|
下記は開発時に使用する主要な Yarn コマンドの一覧です。
| コマンド | 概要 | |
|---|---|---|
| パッケージのインストール | yarn install |
依存パッケージをインストールする |
| サービスのローカル起動 | yarn dev |
サービスをローカル環境で起動する |
| +-- フロントエンドのみ起動 | yarn dev:app |
フロントエンドの Web サーバのみ起動する (この場合、一部のコンテンツは表示されない) |
| +-- バックエンドのみ起動 | yarn dev:mock |
バックエンドのモックサーバのみ起動する |
| 静的解析の実施 | yarn lint |
静的解析を実施する |
| 単体テストの実施 | yarn test:ut |
UT を実施し、./app/coverage/ に Jest の実施結果を出力するUT を個別に実行したい場合は yarn test:ut --testPathPattern 'path/to/test/\[id\].test.tsx' |
| 外部結合テストの実施 | yarn test:itb |
IT を実施し、./tools/dist/ に Newman の実施結果を出力する(モックサーバが起動している必要がある) |
| 性能テストの実施 | yarn test:pt |
性能テストを実施し、./tools/dist/ に K6 の実施結果を出力する(モックサーバが起動している必要がある) |
| 全資材をビルド | yarn build |
実行可能な形式のアプリケーションと関連するドキュメントを生成する |
| +-- アプリケーションのビルド | yarn build:app |
React.js アプリケーションをトランスパイルする |
| +-- ライセンス情報の生成 | yarn build:licenses |
React.js アプリケーションが利用するサードパーティーのライセンス情報を ./app/dist/ に出力する |
| +-- API 仕様書の生成 | yarn build:openapi |
OpenAPI 仕様書を HTML 形式で ./tools/dist/ に出力する |
| +-- ER 図の生成 | yarn build:er |
起動中の MySQL から ER 図を生成し ./tools/dist/ に出力する |
プロダクトを本番環境にデプロイし、全機能を動作させるためには、下記の環境変数が必要になります。
※ 開発時およびテスト時に必要な環境変数は、対応する .env にすべて定義されます。
| Name | Value | Summary |
|---|---|---|
ACTIONS_RUNNER_DEBUG |
true |
ジョブ実行ホストの詳細ログの出力 (GitHub Actions 障害解析用) |
ACTIONS_STEP_DEBUG |
true |
ジョブの詳細ログの出力 (GitHub Actions 障害解析用) |
GENERATE_SOURCEMAP |
false |
マッピングファイルの生成を無効化 |
REACT_APP_GOOGLEAPIS_URL |
https://sheets.googleapis.com/v4 |
API 処理のリクエスト先 URL |
| Name | Value | Summary |
|---|---|---|
REACT_APP_GOOGLE_SHEETS_API_KEY (§1) |
*** | Google Sheets の API キー |
REACT_APP_GOOGLE_SHEETS_ID |
*** | Google Sheets のシート ID |
REACT_APP_GOOGLE_ANALYTICS_ID (§2) |
*** | Google Analytics ID |
REACT_APP_EMAILJS_PUBLIC_KEY (§3) |
*** | EmailJS の API キー |
REACT_APP_EMAILJS_SERVICE_ID (§4) |
*** | EmailJS のサービス ID |
REACT_APP_EMAILJS_TEMPLATE_ID |
*** | EmailJS のテンプレート ID |
REACT_APP_SENTRY_DSN (§5) |
*** | Sentry のデータソース名 |
FOSSA_TOKEN (§6) |
*** | FOSSA の API トークン |
SNYK_TOKEN (§7) |
*** | SNYK の API トークン |
CODECOV_TOKEN (§8) |
*** | Codecov の API トークン |
SLACK_WEBHOOK_URL (§9) |
*** | Slack の Web フック URL |
- §1: Google Cloud: API キーを使用して認証する > API キーの制限を適用する
※ 本番環境では HTTP リファラーと API が制限されたキーを使用すること - §2: Google: アナリティクスを設定 > トラッキング ID への対応
- §3: EmailJS: emailjs.init
- §4: EmailJS: emailjs.send
- §5: Sentry: Data Source Name (DSN) > Where to Find Your DSN
- §6: FOSSA: API & Custom Integrations > API Tokens
- §7: Snyk: Revoking and regenerating Snyk API tokens
- §8: Codecov: How to Set Up Codecov with C and GitHub Actions in 2022 > GitHub and Codecov
- §9: Slack: Incoming Webhook
プロダクト開発や品質評価に関する資料を下記に列挙します。
- ER 図 (tbls)
- API 仕様書 (ReDoc)
- ライセンス情報 (Yarn)
- ライセンススキャン結果 (FOSSA)
- 脆弱性スキャン結果 (Snyk)
- カバレッジレポート (Codecov)
- カバレッジレポート (lcov)
- 結合試験レポート (Newman)
- 性能試験レポート (K6)
Note
なお、本プロダクトは自学用のプログラミングノートも兼ねており、ソース内には NOTE: で始まるコメントが多く残されています。
コメントの内容は不正確な可能性もあるため、ご容赦ください。
-
ブランチ戦略
本来であれば、ブランチを main, develop, feature などに分け、レビューを経て製品版にマージするのが常套ですが、本開発ではこれを実施しません。
大きな要因としては、比較的頻繁に変更が入る上、実験的な内容も多く、コミット履歴が荒れることを嫌ったためです。また、ポートフォリオサイトという特性上、開発者が筆者一人に限られるためレビューに有効性がありません。 -
本番環境分離
通常、Web サイトのリリースでは、資材をステージング環境に展開しテストを経てから本番環境に適用します。
本開発では、アプリケーションを更新する際、即座に本番環境の資材が置き換えられます。CI/CD パイプラインで静的解析、テスト、スキャンが済んでおり、仮にリリースミスやデグレードが発生した場合も影響範囲は限られ、容易に切り戻し可能なためです。
以上




