GitHubスター
1
ユーザー評価
未評価
お気に入り
0
閲覧数
12
フォーク
2
イシュー
0
@kawasawa
筆者の制作物、執筆記事、業務経歴をまとめた 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 |
コードエディタ | Visual Studio Code |
OpenAPI エディタ | Stoplight Studio |
Git クライアント | Sourcetree |
DB クライアント | TablePlus |
API クライアント | Postman |
性能テストツール | K6 |
作図ツール | draw.io |
MCP サーバ
GitHub 等のクラウドリソースにアクセスするための既存の MCP サーバの他、ソースコード内の特定のアノテーションコメントが付与された記載をナレッジとして生成 AI の情報源にさせる自作サーバも構築しています。
収集されたコメントはベクトルデータに変換され、 Python オブジェクト形式でシリアライズしキャッシュされます。
[!NOTE]
MCP ナレッジサーバについては README.md をご参照ください。
ディレクトリ構成
プロダクトのディレクトリ構成を下記に示します。
app 配下がフロントエンド (React.js) 、mock 配下がバックエンドのモック (Express.js および MySQL) になります。
+--.github/ # GitHub 関連ファイル
| |
| +--instructions/ # GitHub Copilot カスタムインストラクション
| |
| +--prompts/ # GitHub Copilot プロンプトファイル
| |
| +--workflows/ # GitHub Actions ワークフロー
|
|
+--.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/ # モック
| |
| +--docker/ # Docker 関連ファイル
| | |
| | +--mysql/ # MySQL 設定ファイル
| | |
| | +--sonarqube/ # SonarQube 関連ファイル
| |
| +--server/ # Express.js アプリケーション
| | |
| | +--prisma/ # Prisma スキーマ定義
| | |
| | +--src/ # ソースファイル
| | | |
| | | +--@types/ # 型定義ファイル
| | | |
| | | +--api/ # API 処理
| | | |
| | | +--lib/ # ライブラリラッパー
| | | |
| | | +--middlewares/ # ミドルウェア
| | | |
| | | +--responses/ # レスポンス型定義
| | | |
| | | +--routes/ # ルーティング
| | | |
| | | +--schemas/ # バリデーション定義
| |
| +--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-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 にすべて定義されます。
Variables (GitHub)
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 |
Secrets (GitHub)
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 パイプラインで静的解析、テスト、スキャンが済んでおり、仮にリリースミスやデグレードが発生した場合も影響範囲は限られ、容易に切り戻し可能なためです。
以上