de531bfe11
Enforce authentication/authorization for admin pages and add several management actions and UI improvements. Key changes: - Added auth checks and redirects on admin pages (contracts, stores, subsidies, vendors) to restrict access to SUPER_ADMIN/OPS_MANAGER. - Hooked server actions to authenticated user IDs (release escrow, review/publish stores, review subsidies/vendors, open disputes, create subsidy cases, create match requests, submit/delete store drafts). - Implemented store publish flow including policy version resolution and StoreActionButtons update to show approve/reject/publish based on reviewStatus. - Added filtering UIs and query handling: admin lists (stores/subsidies/vendors) now support status filters; public stores list uses a new client StoreFilters component to build search params. - New client-side improvements: invite form triggers router.refresh() after success; register page accepts/validates optional phone and persists it; store creation now uses createStoreDraftService and shows error banner on failure. - Matching and subsidies pages now support contextual forms to create match requests and subsidy cases when a storeId is provided. - Various UX tweaks: disabled inspection button, dispute form, list link styling, and revalidation calls after server actions. - Added docs/BUG-REPORT-2026-03-08.md. These changes centralize auth, connect actions to real user IDs, and improve admin and store workflows and filtering.
9.7 KiB
9.7 KiB
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/newform 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기반 Prismawhere조건 연동 (region/industry/status 필터)
BUG-011: /contracts - "분쟁 접수" 버튼 미동작 ✅
- 파일:
apps/web/src/app/contracts/page.tsx - 수정:
handleOpenDisputeserver action →openDisputeService연결 - 참고: "검수 요청" 버튼은 "준비 중" 상태로 비활성화
BUG-012: /matching - 매칭 요청 생성 기능 없음 ✅
- 파일:
apps/web/src/app/matching/page.tsx - 수정:
handleCreateMatchRequestserver action + 매칭 요청 폼 구현storeIdsearchParams 처리, 매장 정보 조회- matchType(인수/철거/인테리어) 선택 + 메시지 입력
matchRequest.create+auditLog.create트랜잭션
BUG-013: /subsidies - 지원금 케이스 생성 UI 없음 ✅
- 파일:
apps/web/src/app/subsidies/page.tsx - 수정:
handleCreateSubsidyCaseserver action + 신청 폼 구현storeIdsearchParams로 매장 연결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_MANAGERrole 체크, 미인증 시/403redirect
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 | 정상 |