この記事は約7分で読めます。
筆者プロフィール: ソフトウェアエンジニア。「知った気にならない。いつまでも学び続ける」を信条に、業務と個人開発の両輪で技術を磨いています。AI 駆動開発で複数の個人開発アプリを構築・運用中。
👉 ポートフォリオ: 筆者ホームページ
個人開発した AI業務管理秘書 「たすきば Knowledge Relay」(過去の業務資産の管理を AI 秘書が手伝い、プロジェクト管理も担う)の技術スタックと設計判断を、運用中の実装ベースで整理した記事です。Next.js 16 / Prisma 7 / PostgreSQL を中核に、認証・認可・監査・自動テスト・CI セキュリティ をどう組み上げたかを公開します。
サービスの機能紹介・画面イメージ・コンセプトは公式プロダクトページをご覧ください。
👉 たすきば Knowledge Relay — 公式プロダクトページ
サービスの技術的ポジション
「プロジェクトの知見を蓄積し、次の判断を強くする」ことをテーマにした運営プラットフォームです。ナレッジ管理を中核に据え、見積もり・WBS・ガントチャート・リスク課題管理・振り返りが相互にリンクする構造になっています。
本記事では機能よりどう作ったかに焦点を当てます。
1. 技術スタック(全レイヤー)
実際に package.json に入っているバージョンで記載します。
| レイヤー | 採用技術・バージョン |
|---|---|
| フロントエンド | Next.js 16.2.3 (App Router) / React 19.2.4 / TypeScript 5 |
| UI | shadcn/ui / Tailwind CSS v4 / @base-ui/react 1.4.0 |
| バックエンド | Next.js API Routes / Server Actions / Server Components (RSC) |
| ORM |
Prisma 7.7.0 (@prisma/adapter-pg 経由) |
| DB | PostgreSQL 16 (Supabase Pooler) |
| 全文検索 | pg_trgm (PostgreSQL 標準拡張) |
| 認証 | NextAuth.js (Auth.js) 5.0.0-beta.31 |
| MFA | otplib 13.4.0 (TOTP / RFC 6238) |
| バリデーション | zod 4.3.6 |
| ハッシュ | bcryptjs 3.0.3 (cost=12) |
| テスト (ユニット) | Vitest 4.1.4 |
| テスト (E2E) | Playwright 1.59.1 (Chromium + 視覚回帰) |
| デプロイ | Vercel + Supabase (PostgreSQL Pooler) |
技術選定のポイント
- 外部検索エンジンを入れない — pg_trgm で PostgreSQL だけで全文検索を完結させる
- Edge Runtime 対応 — 認証ミドルウェアは Edge で高速化
-
@prisma/adapter-pg 方式 — Prisma 7 推奨の
pgドライバ直結構成 - ゼロランタイムコスト — Vercel Hobby + Supabase Free でプレリリースまで運用
2. アーキテクチャとレイヤー構成
| レイヤー | 責務 |
|---|---|
| Middleware | 認証チェック、MFA 未検証時の誘導。Edge で高速実行 |
| Route Handlers / Server Components | リクエスト受付、認可、DTO 変換 |
| Service Layer | ビジネスロジック(DB 操作はここに集約) |
| Prisma + pg adapter | 型安全な DB アクセス、PostgreSQL Pooler 経由 |
DB 操作は Service Layer に集約しており、Route Handler や Server Component から Prisma を直接叩くことは禁止しています。ルール違反は ESLint カスタムルールで検知します。
3. データベースと pg_trgm による全文検索
State Machine によるプロジェクト状態遷移
プロジェクトは 7 状態の線形フローで管理しています。
planning → estimating → scheduling → executing → completed → retrospected → closed
状態遷移は state-machine.ts に集約し、遷移可否テーブルを唯一の Source of Truth にしています。画面側で遷移可否ボタンを出し分ける際も、テスト時も、同じテーブルを参照します。
pg_trgm での全文検索
PostgreSQL 標準拡張の pg_trgm(トライグラム検索)を使って、外部検索エンジンなしで類似検索を実装しています。
-- Prisma migration で拡張を有効化
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- GIN インデックスをタイトル・本文に付与
CREATE INDEX idx_knowledge_title_trgm
ON knowledges USING GIN (title gin_trgm_ops);
利用シーン:
- ナレッジ全文検索 — タイトル・本文の部分一致+類似度順ソート
- 提案型サービス(核心機能) — 業務ドメインタグ・工程タグ・技術スタックをキーに、過去プロジェクトから関連ナレッジ・リスク・振り返りを自動提案
4. 認証設計(NextAuth v5 + MFA)
セッション暗号化
NextAuth.js v5 の JWE 暗号化セッション をそのまま採用しています。NextAuth v5 の既定アルゴリズムは A256CBC-HS512(AES-256-CBC + HMAC-SHA-512)で、鍵は NEXTAUTH_SECRET から派生します。セッション Cookie は maxAge 未指定のブラウザセッション Cookie とし、ブラウザを閉じた時点で失われる運用です。
TOTP MFA + 復旧コード
- TOTP 実装は otplib ベース
- TOTP シークレットは AES-256-CBC で暗号化して DB 保存(
NEXTAUTH_SECRETを鍵に流用) - 復旧コードは 10 個、紛らわしい文字(0/O, 1/I/L)を除外した文字集合で生成
- 管理者ロールは MFA 必須 — MFA 未設定だとシステム管理機能にアクセス不可
// src/services/mfa.service.ts 抜粋
const ENCRYPTION_KEY = process.env.NEXTAUTH_SECRET?.slice(0, 32).padEnd(32, '0');
const ALGORITHM = 'aes-256-cbc';
function encrypt(text: string): string {
const iv = randomBytes(16);
const cipher = createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY, 'utf-8'), iv);
let encrypted = cipher.update(text, 'utf-8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted;
}
パスワードポリシー(src/config/security.ts に集約)
| 項目 | 値 | 定数名 |
|---|---|---|
| 最小文字数 | 10 | PASSWORD_MIN_LENGTH |
| 最大文字数 | 128 |
PASSWORD_MAX_LENGTH (DoS 対策) |
| 文字種最低数 | 3 種類(英大・英小・数字・記号のうち) | PASSWORD_REQUIRED_CHAR_TYPE_COUNT |
| 同一文字連続禁止 | 4 文字まで | PASSWORD_MAX_CONSECUTIVE_SAME_CHARS |
| 履歴保持件数 | 5 回 | PASSWORD_HISTORY_COUNT |
| bcrypt コスト | 12(OWASP 推奨) | BCRYPT_COST |
アカウントロック
| トリガー | 挙動 |
|---|---|
| ログイン失敗 5 回 | 30 分間の一時ロック(時間経過で自動解除) |
| 管理者 MFA 未設定 | システム管理機能にアクセス不可 |
| 30 日未ログイン | 日次 cron が /api/admin/users/cleanup-inactive で論理削除 |
論理削除時は ProjectMember も同時に物理削除されるため、管理者が手動で消し忘れても DB 整合性が保たれます。
5. 認可設計(RBAC 2 軸マトリクス)
-
システムロール:
admin/general -
プロジェクトロール:
pm_tl/member/viewer
認可判定は「アクション × ロール × プロジェクト状態」の 3 次元マトリクスで厳密に制御します。「見積もり確定後は PM/TL でも WBS を編集できない」といった状態依存の制約も同一のマトリクスで表現できます。
IDOR(他プロジェクトの不正アクセス)対策は共通関数に切り出し、各 API の冒頭で必ず呼び出す設計にしています。
6. 監査ログ(3 テーブル分離)
用途別に 3 テーブルへ分離しています。
| テーブル | 記録対象 |
|---|---|
audit_logs |
全データ変更(誰が・いつ・何を・どう変えたか) |
auth_event_logs |
認証イベント(成功・失敗・ロック・MFA 検証) |
role_change_logs |
権限変更(システムロール/プロジェクトロールの変更) |
管理者は専用画面から検索・閲覧が可能です。
7. テスト戦略(自動 3 層)
テスト件数(2026-04-23 時点の実測値)
| 層 | ツール | 規模 |
|---|---|---|
| ユニットテスト | Vitest |
641 テスト(src/**/*.test.ts) |
| E2E テスト | Playwright | 9 spec ファイル(smoke / setup / detail / 個人機能 / WBS / ガント / 見積もり 等) |
| 視覚回帰 | Playwright toHaveScreenshot
|
10 テーマ × 主要画面 の pixel 比較 |
カバレッジ閾値(vitest.config.ts)
thresholds: {
lines: 80,
statements: 80,
functions: 80,
branches: 70,
}
閾値未達は CI で fail させています。閾値達成のために品質の低いテストを量産するのを避けるため、対象ディレクトリを src/lib/** と src/services/** に限定しています(UI コンポーネントは視覚回帰で担保)。
E2E の罠を knowledge として蓄積
router.refresh race、next-auth session 更新 race、Suspense hydration race など、E2E 実装で詰まった 25 パターンを docs/developer/E2E_LESSONS_LEARNED.md に集約しています。新しい E2E を書く前・失敗をデバッグする前に一読するワークフローで、同じ罠に二度引っ掛からない体制にしました。
8. CI/CD — 6 層セキュリティスキャン
開発工程に以下の 6 層を組み込んでいます。
| 層 | ツール | 検知対象 |
|---|---|---|
| 1 | gitleaks | コミット履歴への機密情報混入 |
| 2 | pnpm audit(日次実行) | 依存パッケージの既知脆弱性 |
| 3 | Semgrep | SAST(静的セキュリティ解析) |
| 4 | CodeQL(GitHub 標準) | 高度な SAST |
| 5 | Dependency Review | PR 時の差分脆弱性検知(GitHub Advisory DB 照合) |
| 6 | 独自 hook block-dangerous-edit
|
eval / innerHTML / SQL 文字列連結 等の危険パターン編集をブロック |
日次 Security Scan Workflow
# .github/workflows/security.yml 抜粋
on:
schedule:
- cron: '0 3 * * *' # 毎日 03:00 UTC
pull_request:
加えて、四半期ごとに STRIDE 脅威モデリング を /threat-model スキル経由で自動化しています。
9. AI 駆動開発の仕組み(Claude Code 運用)
本リポジトリは AI 支援前提で設計されており、以下を .claude/ ディレクトリに制度化しています。
| 仕組み | 件数 | 内容 |
|---|---|---|
| Hooks | 4 | SessionStart(日次ブランチ自動化)/ PreToolUse(危険コード検知)/ PostToolUse(自動整形)/ Stop(機密スキャン + lint + test + 自動コミット) |
| Skills | 5 |
/threat-model / /check-deploy / /fix-issue / /release / /update-labels
|
| Agents | 7 | 認証 / インジェクション / XSS / 機密情報 / 依存脆弱性 / パフォーマンス / ラベル検出 の観点別並列レビュー |
Stop Hook が毎コミット前に検証する 5 点
- 横展開 — 同一パターンが他ファイルに残っていないか
- セキュリティ — ユーザー入力サニタイズ / 生 SQL / 機密情報ハードコード
- パフォーマンス — ループ内 DB 問い合わせ / 不要な再描画 / N+1
- テスト整合性 — 旧文言残留 / テスト数の不自然な増減
- ドキュメント更新 — 仕様変更時の関連ドキュメント更新漏れ
main / master / develop / release/* / hotfix/* への直接コミットは自動ブロックしています。
10. ドキュメント体系(役割別 3 ディレクトリ)
| ディレクトリ | 対象 | 内容 |
|---|---|---|
docs/beginner/ |
初見開発者 | 開発環境構築 → PR 作成までの 30 分 onboarding |
docs/developer/ |
開発者 | 要件 / 仕様 / 設計 / テスト戦略 / E2E 罠集 / 知見・改修履歴 |
docs/administrator/ |
運用管理者 | デプロイ / 環境変数 / migration / 障害対応 / ロールバック |
開発過程で得たナレッジは docs/developer/knowledge/ に KNW-NNN 形式で体系化して蓄積しています(例: KNW-001: 設計文書の質と開発速度 / KNW-002: Next.js App Router のパフォーマンス最適化パターン)。
11. デプロイ形態と今後のロードマップ
| 形態 | 対応状況 |
|---|---|
| Vercel + Supabase(クラウド SaaS) | ✅ 稼働中 |
| PC / ローカル(Docker Compose) | Phase 2 対応予定 |
| オンプレミス(Nginx + Docker Compose) | Phase 2 対応予定 |
| クラウド(AWS ECS / Azure App Service) | 将来対応 |
- プレリリース: 2026-05-01 — 紹介記事としての公開(招待制)
- 正式リリース: 2026-06-01 — 3 環境対応の手順書完備
まとめ
たすきば Knowledge Relay の技術スタックをまとめると:
- Next.js 16 / React 19 / Prisma 7 / PostgreSQL を中核に、外部検索エンジン不要の設計
- NextAuth v5 JWE(A256CBC-HS512)+ TOTP MFA + RBAC 2 軸 で認証・認可を構築
- Vitest 641 テスト + E2E 9 spec + 視覚回帰 10 テーマ を CI で閾値強制
- CI セキュリティ 6 層 + 四半期 STRIDE で脅威を継続監視
- Claude Code 運用の制度化(Hooks 4 / Skills 5 / Agents 7)で少人数でも品質を維持
サービスの機能・画面イメージ・利用シーンを見たい方はこちらへ 👉 たすきば Knowledge Relay — 公式プロダクトページ
本記事で触れていない設計判断の詳細(E2E 実装で詰まった罠集・設計文書の構成 等)は、プレリリース後に順次公開予定です。
参考
- Next.js 16 公式ドキュメント
- Prisma 公式ドキュメント
- Auth.js (NextAuth v5) 公式ドキュメント
- pg_trgm — PostgreSQL 公式ドキュメント
- OWASP Password Storage Cheat Sheet