# Startover DB 상호작용 전수조사 버그 리포트 **조사일**: 2026-03-08 **조사 범위**: DB와 상호작용하는 모든 페이지 (총 15개 페이지, 5개 API 라우트, 4개 서비스) **조사 방법**: 코드 정적 분석 (모든 CRUD 경로 추적) **빌드 검증**: `pnpm turbo build` 전체 통과 (7/7 tasks, 0 type errors) --- ## 수정 현황 | 심각도 | 발견 | 수정 완료 | 미수정 | |--------|------|-----------|--------| | CRITICAL | 6건 | **6건** | 0건 | | HIGH | 9건 | **8건** | 1건 | | MEDIUM | 8건 | **6건** | 2건 | | **합계** | **23건** | **20건** | **3건** | ### 수정 완료 목록 (20건) - BUG-001~006: 하드코딩 사용자 ID → `auth()` 세션 사용 (CRITICAL x6) ✅ - BUG-007: `/stores/new` 서비스 레이어 우회 → `createStoreDraftService` 호출 (HIGH) ✅ - BUG-008: 매장 DRAFT→SUBMITTED 제출/삭제 + admin APPROVED→PUBLISHED 공개 버튼 (HIGH) ✅ - BUG-010: `/stores` 필터 동작 연결 (HIGH) ✅ - BUG-011: `/contracts` 분쟁 접수 버튼 → `openDisputeService` 연결 (HIGH) ✅ - BUG-012: `/matching` 매칭 요청 생성 폼 + `matchRequest.create` 구현 (HIGH) ✅ - BUG-013: `/subsidies` 지원금 케이스 생성 폼 + `createSubsidyCaseService` 연결 (HIGH) ✅ - BUG-014: admin 3개 페이지 필터 → `searchParams` + `Link` 연동 (HIGH) ✅ - BUG-015: 초대 후 목록 갱신 (`router.refresh()`) (HIGH) ✅ - BUG-017: 회원가입 전화번호 필드 추가 (MEDIUM) ✅ - BUG-018: `/stores/new` 에러 핸들링 추가 (MEDIUM) ✅ - BUG-019~022: 관리자 4개 페이지 인증 검증 추가 (MEDIUM x4) ✅ ### 미수정 (추가 작업 필요, 3건) - BUG-009: 매장 수정 폼 (DRAFT 상태 수정 기능) - BUG-016: 에스크로 RELEASE_REVIEW → RELEASED 전환 관리자 액션 - BUG-023: `/stores/new` form validation 빈 문자열 검증 (도메인 서비스에서 부분 처리됨) --- ## CRITICAL (즉시 수정 필요) - 전부 수정 완료 ✅ ### BUG-001: `/stores/new` - 하드코딩된 사용자 ID ✅ - **파일**: `apps/web/src/app/stores/new/page.tsx` - **수정**: `TEMP_OWNER_USER_ID = BigInt(1)` → `auth()` 세션에서 `session.user.dbId` 사용 ### BUG-002: `/vendors` actions - 하드코딩된 사용자 ID ✅ - **파일**: `apps/web/src/app/vendors/actions.ts` - **수정**: `ownerUserId: '1'` → `session.user.dbId` ### BUG-003: `/admin/stores` - 하드코딩된 actorUserId ✅ - **파일**: `apps/web/src/app/admin/stores/page.tsx` - **수정**: `actorUserId = '1'` → `session.user.dbId` ### BUG-004: `/admin/vendors` - 하드코딩된 actorUserId ✅ - **파일**: `apps/web/src/app/admin/vendors/page.tsx` - **수정**: 4개 server action 모두 `auth()` 세션 사용 ### BUG-005: `/admin/contracts` - 하드코딩된 actorUserId ✅ - **파일**: `apps/web/src/app/admin/contracts/page.tsx` - **수정**: `releaseEscrowService(prisma, contractPublicId, '1')` → `session.user.dbId` ### BUG-006: `/admin/subsidies` - 하드코딩된 actorUserId ✅ - **파일**: `apps/web/src/app/admin/subsidies/page.tsx` - **수정**: 승인/반려 모두 `session.user.dbId` 사용 --- ## HIGH (빠른 수정 필요) ### BUG-007: `/stores/new` - Server Action이 서비스 레이어를 우회 ✅ - **파일**: `apps/web/src/app/stores/new/page.tsx` - **수정**: 직접 `prisma.store.create` → `createStoreDraftService` 호출 - **효과**: 도메인 검증, AuditLog, OutboxEvent 정상 동작 ### BUG-008: 매장 등록 → 공개 플로우 단절 ✅ - **수정 내용**: - `/stores/[id]`: DRAFT 상태에서 "검토 제출" 버튼 (→SUBMITTED) + "삭제" 버튼 추가 - `/admin/stores`: APPROVED 상태에서 "공개" 버튼 추가 (`publishStoreService` 연결) - `StoreActionButtons`: 상태별 버튼 분기 (SUBMITTED: 승인/반려, APPROVED: 공개) ### BUG-009: `/stores/[id]` - 수정 기능 부재 ⚠️ 미수정 - **파일**: `apps/web/src/app/stores/[id]/page.tsx` - **현재 상태**: 삭제는 가능 (DRAFT 상태), 수정 폼은 미구현 - **남은 작업**: 매장 정보 수정 폼 페이지 추가 ### BUG-010: `/stores` - 필터가 동작하지 않음 ✅ - **파일**: `apps/web/src/app/stores/page.tsx` - **수정**: `searchParams` 기반 Prisma `where` 조건 연동 (region/industry/status 필터) ### BUG-011: `/contracts` - "분쟁 접수" 버튼 미동작 ✅ - **파일**: `apps/web/src/app/contracts/page.tsx` - **수정**: `handleOpenDispute` server action → `openDisputeService` 연결 - **참고**: "검수 요청" 버튼은 "준비 중" 상태로 비활성화 ### BUG-012: `/matching` - 매칭 요청 생성 기능 없음 ✅ - **파일**: `apps/web/src/app/matching/page.tsx` - **수정**: `handleCreateMatchRequest` server action + 매칭 요청 폼 구현 - `storeId` searchParams 처리, 매장 정보 조회 - matchType(인수/철거/인테리어) 선택 + 메시지 입력 - `matchRequest.create` + `auditLog.create` 트랜잭션 ### BUG-013: `/subsidies` - 지원금 케이스 생성 UI 없음 ✅ - **파일**: `apps/web/src/app/subsidies/page.tsx` - **수정**: `handleCreateSubsidyCase` server action + 신청 폼 구현 - `storeId` searchParams로 매장 연결 - `createSubsidyCaseService` 호출 - 체크리스트 진행률 프로그레스바 표시 ### BUG-014: 관리자 페이지 필터 버튼 미동작 ✅ - **수정**: - `/admin/stores`: `FILTER_LABELS` + `searchParams` + `StoreReviewStatus` 타입 캐스트 - `/admin/vendors`: `FILTER_BUTTONS` + `VendorCertificationStatus` 타입 캐스트 - `/admin/subsidies`: `FILTER_BUTTONS` + `SubsidyCaseStatus` 타입 캐스트 ### BUG-015: `/admin/settings/invite` - 초대 후 목록 갱신 안 됨 ✅ - **파일**: `apps/web/src/app/admin/settings/invite/invite-form.tsx` - **수정**: 성공 시 `router.refresh()` 추가 --- ## MEDIUM (개선 필요) ### BUG-016: `/admin/contracts` - releaseAction이 RELEASE_REVIEW까지만 처리 ⚠️ 미수정 - **파일**: `apps/web/src/services/contract-service.ts:150-154` - **증상**: `releaseEscrowService`가 `RELEASE_REVIEW`로만 변경. `RELEASED` 전환 후속 처리 없음 - **남은 작업**: RELEASE_REVIEW → RELEASED 전환 관리자 액션 추가 ### BUG-017: `/auth/register` - 전화번호 필드 누락 ✅ - **파일**: `apps/web/src/app/auth/register/page.tsx`, `actions.ts` - **수정**: phone 입력 필드 추가, zod 스키마에 phone 정규식 검증 추가 ### BUG-018: `/stores/new` - 에러 핸들링 부재 ✅ - **파일**: `apps/web/src/app/stores/new/page.tsx` - **수정**: `useActionState` + ErrorBanner로 에러 표시 ### BUG-019~022: 관리자 페이지 인증 검증 없음 ✅ - `/admin/stores`, `/admin/vendors`, `/admin/contracts`, `/admin/subsidies` - **수정**: `auth()` + `SUPER_ADMIN`/`OPS_MANAGER` role 체크, 미인증 시 `/403` redirect ### BUG-023: `/stores/new` - form validation 불완전 ⚠️ 미수정 - **현재 상태**: `createStoreDraftService` 도메인 레이어에서 부분 검증 - **남은 작업**: 서버 측 빈 문자열 추가 검증 --- ## 변경된 파일 목록 (15개 파일, +637 / -202 lines) | 파일 | 변경 내용 | |------|-----------| | `apps/web/src/app/admin/contracts/page.tsx` | auth 가드, session.user.dbId | | `apps/web/src/app/admin/settings/invite/invite-form.tsx` | router.refresh() | | `apps/web/src/app/admin/stores/StoreActionButtons.tsx` | 상태별 버튼 분기 (publish 추가) | | `apps/web/src/app/admin/stores/page.tsx` | auth 가드, publish, 필터, enum 캐스트 | | `apps/web/src/app/admin/subsidies/page.tsx` | auth 가드, 필터, enum 캐스트 | | `apps/web/src/app/admin/vendors/page.tsx` | auth 가드, 필터, enum 캐스트 | | `apps/web/src/app/auth/register/actions.ts` | phone zod 스키마 + DB 저장 | | `apps/web/src/app/auth/register/page.tsx` | phone 입력 필드 | | `apps/web/src/app/contracts/page.tsx` | 분쟁 접수 server action | | `apps/web/src/app/matching/page.tsx` | 매칭 요청 생성 폼 + server action | | `apps/web/src/app/stores/[id]/page.tsx` | 검토 제출 + 삭제 server action | | `apps/web/src/app/stores/new/page.tsx` | 서비스 레이어, auth, 에러 핸들링 | | `apps/web/src/app/stores/page.tsx` | searchParams 필터 연동 | | `apps/web/src/app/subsidies/page.tsx` | 지원금 신청 폼 + server action | | `apps/web/src/app/vendors/actions.ts` | session.user.dbId | --- ## 페이지별 DB 상호작용 현황 (수정 후) ### 사용자 페이지 | 페이지 | CREATE | READ | UPDATE | DELETE | 상태 | |--------|--------|------|--------|--------|------| | `/stores` | - | O | - | - | ✅ 필터 동작 | | `/stores/new` | O | - | - | - | ✅ 서비스 레이어, auth | | `/stores/[id]` | - | O | O | O | ✅ 제출/삭제 (수정 미구현) | | `/vendors` | O | O | - | - | ✅ auth 세션 사용 | | `/contracts` | - | O | O | - | ✅ 분쟁 접수 연결 | | `/subsidies` | O | O | - | - | ✅ 신청 폼 구현 | | `/matching` | O | O | - | - | ✅ 요청 생성 구현 | | `/auth/register` | O | O | - | - | ✅ phone 추가 | | `/auth/complete-profile` | O | O | O | - | 정상 | | `/auth/invite/[token]` | O | O | O | - | 정상 | ### 관리자 페이지 | 페이지 | CREATE | READ | UPDATE | DELETE | 상태 | |--------|--------|------|--------|--------|------| | `/admin/stores` | - | O | O | - | ✅ auth, 필터, publish | | `/admin/vendors` | - | O | O | - | ✅ auth, 필터 | | `/admin/contracts` | - | O | O | - | ✅ auth | | `/admin/subsidies` | - | O | O | - | ✅ auth, 필터 | | `/admin/settings/invite` | O | O | - | - | ✅ 목록 갱신 | ### API 라우트 | 엔드포인트 | 동작 | 상태 | |-----------|------|------| | `POST /api/v1/stores` | CREATE | 정상 | | `POST /api/v1/stores/[id]/submit` | UPDATE | 정상 | | `POST /api/auth/complete-profile` | UPDATE | 정상 | | `POST /api/auth/invite/create` | CREATE | 정상 | | `POST /api/auth/invite/accept` | CREATE | 정상 |