diff --git a/BUGS.md b/BUGS.md new file mode 100644 index 0000000..251f72e --- /dev/null +++ b/BUGS.md @@ -0,0 +1,259 @@ +# レビューテスト用バグ一覧 + +このドキュメントには、意図的に仕込まれたバグの詳細が記載されています。採点用の参考資料として使用してください。 + +## バグ一覧 + +### 難易度1: UIの状態管理不備 + +**ファイル**: `components/TodoItem.tsx` +**行番号**: 97-104行目 + +**バグ内容**: +削除ボタンの`disabled`属性が`isDeleting`のみを考慮しており、`isUpdating`が考慮されていない。 + +**問題コード**: +```tsx + +``` + +**問題点**: +- TODOの完了状態を更新中(`isUpdating === true`)でも削除ボタンが有効になっている +- 更新処理と削除処理が同時に実行される可能性があり、データの整合性が損なわれる可能性がある +- ユーザー体験が悪化する(更新中に誤って削除してしまう可能性) + +**影響範囲**: +- フロントエンドのUI動作 +- データ整合性への潜在的な影響 + +**期待される修正**: +```tsx +disabled={isDeleting || isUpdating} +``` + +--- + +### 難易度2: データバリデーション不備 + +**ファイル**: `app/api/todos/route.ts` +**行番号**: 34-38行目 + +**バグ内容**: +`description`が空文字列の場合、`null`に変換されずにそのまま空文字列がデータベースに保存される。 + +**問題コード**: +```typescript +const todo = await prisma.todo.create({ + data: { + title: validatedData.title, + description: validatedData.description, // 空文字列がそのまま保存される + }, +}) +``` + +**問題点**: +- zodスキーマで`description`が`optional()`のため、空文字列がバリデーションを通過してしまう +- データベースに空文字列が保存され、`null`と空文字列が混在する可能性がある +- データの一貫性が損なわれる +- フロントエンドで`todo.description && ...`のような条件分岐が正しく動作しない可能性がある + +**影響範囲**: +- データベースのデータ整合性 +- フロントエンドの表示ロジック + +**期待される修正**: +```typescript +description: validatedData.description || null, +``` +または +```typescript +description: validatedData.description?.trim() || null, +``` + +--- + +### 難易度3: React Hooksの依存配列問題 + +**ファイル**: `components/TodoList.tsx` +**行番号**: 39-41行目 + +**バグ内容**: +`useEffect`の依存配列に`fetchTodos`が含まれているが、`fetchTodos`が毎回新しい関数として作成されるため、無限ループが発生する可能性がある。 + +**問題コード**: +```tsx +const fetchTodos = async () => { + // ... +} + +useEffect(() => { + fetchTodos() +}, [fetchTodos]) // fetchTodosが毎回新しい関数として作成される +``` + +**問題点**: +- `fetchTodos`がコンポーネント内で定義されているため、レンダリングごとに新しい関数インスタンスが作成される +- `useEffect`の依存配列に`fetchTodos`を含めると、毎回依存が変化したと判断され、無限ループが発生する +- 不要なAPIリクエストが大量に発生し、パフォーマンスが低下する +- サーバーに負荷がかかる + +**影響範囲**: +- フロントエンドのパフォーマンス +- サーバーへの負荷 +- ユーザー体験(画面がフリーズする可能性) + +**期待される修正**: +```tsx +useEffect(() => { + fetchTodos() +}, []) // 依存配列を空にする +``` +または +```tsx +const fetchTodos = useCallback(async () => { + // ... +}, []) + +useEffect(() => { + fetchTodos() +}, [fetchTodos]) +``` + +--- + +### 難易度4: セキュリティ/データ整合性問題 + +**ファイル**: `app/api/todos/[id]/route.ts` +**行番号**: 16-20行目(GET, PATCH, DELETEすべて) + +**バグ内容**: +IDの形式検証が行われておらず、不正なID形式(SQLインジェクションや不正な文字列)が送られても検証されない。 + +**問題コード**: +```typescript +try { + const { id } = await params + const todo = await prisma.todo.findUnique({ + where: { id }, // IDの形式検証がない + }) + // ... +} +``` + +**問題点**: +- PrismaはSQLインジェクションを防いでいるが、不正なID形式(例: 非常に長い文字列、特殊文字を含む文字列)が送られても検証されない +- 不正なIDでリクエストが送られた場合、適切なエラーメッセージが返されない可能性がある +- データベースへの不要なクエリが発生する可能性がある +- エラーハンドリングが不十分 + +**影響範囲**: +- セキュリティ(軽微) +- エラーハンドリング +- パフォーマンス(不要なクエリ) + +**期待される修正**: +```typescript +// cuid形式の検証を追加 +const isValidCuid = (id: string): boolean => { + return /^c[a-z0-9]{24}$/.test(id) +} + +try { + const { id } = await params + if (!isValidCuid(id)) { + return NextResponse.json( + { error: '不正なID形式です' }, + { status: 400 } + ) + } + // ... +} +``` + +--- + +### 難易度5: 本番環境での接続管理問題 + +**ファイル**: `lib/prisma.ts` +**行番号**: 7-13行目 + +**バグ内容**: +本番環境でもPrismaクライアントをグローバルに保存しないため、リクエストごとに新しい`PrismaClient`インスタンスが作成される可能性がある。 + +**問題コード**: +```typescript +export const prisma = globalForPrisma.prisma ?? new PrismaClient({ + log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], +}) + +if (process.env.NODE_ENV !== 'production') { + globalForPrisma.prisma = prisma +} +``` + +**問題点**: +- 本番環境では`globalForPrisma.prisma`が保存されないため、Next.jsのサーバーレス環境ではリクエストごとに新しい`PrismaClient`インスタンスが作成される可能性がある +- 接続プールが適切に管理されず、データベース接続が枯渇する可能性がある +- メモリリークやパフォーマンス低下の原因になる +- 本番環境でのスケーラビリティに影響する + +**影響範囲**: +- 本番環境のパフォーマンス +- データベース接続管理 +- スケーラビリティ +- メモリ使用量 + +**期待される修正**: +```typescript +export const prisma = globalForPrisma.prisma ?? new PrismaClient({ + log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], +}) + +// 本番環境でもグローバルに保存 +globalForPrisma.prisma = prisma +``` + +--- + +## 採点基準(参考) + +### 難易度1(初級) +- コードレビューで簡単に発見できる +- UIの動作を確認すれば発見できる +- 初心者でも発見可能 + +### 難易度2(初中級) +- データフローの理解が必要 +- バリデーションロジックの確認が必要 +- 中級者レベル + +### 難易度3(中級) +- React Hooksの理解が必要 +- 無限ループの原因を理解する必要がある +- 実際に動作を確認しないと発見が難しい場合がある + +### 難易度4(上級) +- セキュリティ意識が必要 +- エラーハンドリングの観点が必要 +- 実装経験が3年以上のエンジニアが発見可能 + +### 難易度5(最上級) +- 本番環境での動作を理解する必要がある +- Next.jsのサーバーレス環境の理解が必要 +- Prismaの接続管理の深い理解が必要 +- 実装経験が5年以上のエンジニアが発見可能 + +--- + +## 注意事項 + +- これらのバグは意図的に仕込まれたものです +- 実際のプロダクションコードでは、このようなバグを避けるためにコードレビューやテストを実施してください +- 採点時は、バグの発見数だけでなく、バグの重要度や影響範囲も考慮してください