본문으로 건너뛰기

/docs/generate 전수 감사 보고서

날짜: 2026-04-19 목적: /docs/generate 플로우·UX·에러·접근성·성능 전수 점검 후 개선 우선순위 도출 작성 기준: 코드베이스 현재 상태 (v0.81.0 · 커밋 feat/ocr-ops-panel 머지 시점) 이 보고서의 위상: 이후 슬라이스의 기준점. 개선이 진행되면 해당 항목에 취소선 + 머지 PR 번호 표기.


1. 아키텍처 매핑

/docs/generate
├─ page.tsx (AiDocGeneratePage · 코디네이터)
│ ├─ useDocGenerate() [훅 — 모든 상태·로직]
│ │ ├─ useEffectivePlan() — Pro 게이트
│ │ ├─ useSearchParams() — caseId · docType preselect
│ │ ├─ getAssistUsageAction() — AI 사용량
│ │ ├─ getRagContextForDocGenAction() — 사무실 기억 미리보기
│ │ ├─ executeAiAction() — reserve → RAG → generate (환불 자동)
│ │ ├─ generateLegalDoc() — Gemini 호출
│ │ └─ saveDocWithHtmlAction() — Firestore + Storage HTML 저장
│ ├─ DocStepIndicator — 3단계 진행 표시
│ ├─ DocStep1SelectType — 서류 종류 4 선택
│ ├─ DocStep2FillInfo — 폼 + 사무실 기억 미리보기 + 제외권
│ └─ DocStep3Preview — 초안 확인/편집/저장/복사/다운로드

상태 머신

step=1 (서류 선택) → step=2 (폼 입력) → handleGenerate → step=3 generating
→ handleUseTemplate → step=3 done (template)

step=3 generating → executeAiAction 결과:
- reserve_failed → showActionError + setStep(2)
- run_failed → logError + toast + setStep(2) (환불 여부 따라 문구)
- succeeded → setDraft + setStep(3) done

step=3 done → handleSave → saveDocWithHtmlAction → router.push("/docs")
step=3 error (현재는 이 상태 도달 경로 없음 — onRetry 로 done 복귀)

진입·이탈 지점

  • 진입: /docs/generate · /docs/generate?caseId=X&docType=Y · /cases/{id}/docs 에서 "서류 생성" 클릭 후 InlineDocGenerate 모달
  • 이탈: handleSave 성공 → /docs, 또는 페이지 닫기

2. 강점 (유지·확장 가치)

#항목근거
S1플랜 게이트 + readOnly 분기canUseAI = isPaidPlan && !readOnly. grace 기간 안내 명확
S2AI 한도 임박 경고90%+ 사용 시 amber 강조
S3PII 사전 차단 (extraInstruction)Zod RRN_PATTERN / CARD_PATTERN superRefine
S4사무실 기억 미리보기 + 제외권Cycle 9 (# 205)
S5환불 자동화executeAiAction 래퍼 (refunded 플래그 사용자 안내)
S6사건 자동 입력CaseSelect → handleCaseSelect 로 10개 필드 일괄 주입
S7초안 복사·다운로드·편집·템플릿 분기Step 3 완성도
S8단계 인디케이터 · disabled 상태 시각화DocStepIndicator 피드백

3. 약점·개선 대상 리스트

P0 — 보안/오류 전달 (Critical)

P0-1. 에러 메시지 구체성 부족

  • step=3 error 화면이 한 종류 — "서류 생성에 실패했습니다 / 네트워크 상태를 확인하고 다시 시도해주세요"
  • 실제로는 (a) reserve 실패(한도·readOnly·플래그 off) · (b) RAG 페치 실패 · (c) AI 호출 실패(retryable/non-retryable) · (d) 저장 실패(네트워크·권한·Zod) 등 4+ 원인 이 있는데 모두 동일 문구
  • 현재 코드: step=3 error 경로가 이론상 존재하지만 handleGenerate 실패 시 setStep(2) + toast 로 처리 → Step 3 error UI 는 dead code 가능성
  • 위험: 사용자가 왜 실패했는지 모르면 무의미한 재시도 반복 → 한도 소진·부정적 인상

P0-2. 저장 경로의 PII 검사 부재

  • DocGenerateInputSchemaextraInstruction PII 만 차단
  • DocSaveInputSchema.finalTextPII 검사 없음 — 사용자가 직접 편집한 draft 에 주민번호·계좌번호 포함 가능
  • Firestore documents.content 에 직접 저장 → /docs 목록·DocPreviewModal 에 노출
  • 완화책: 사용자가 의도적으로 입력한 내용을 차단할 필요는 없으나 clientIdNumber 등 이미 필드로 저장되는 값을 본문에서도 마스킹 할지 정책 결정 필요

H — UX 핵심 (High)

H-1. 접근성 (ARIA) 전반 부족

  • DocStep1SelectType 카드(<button>) 에 aria-pressed 없음 — 선택 상태 스크린 리더 미전달
  • DocStepIndicatoraria-current="step" 없음
  • DocStep3Preview generating 의 progress bar 에 role="progressbar" + aria-valuenow/valuemin/valuemax 없음
  • 성공·에러·면책 배너에 role="alert" / role="status" 없음 → 상태 변화 감지 불가
  • Step 3 편집 <textarea><label> 연결 없음 (현재는 헤더 텍스트만)
  • 필드별 <label htmlFor> / <input id> 명시 연결 없음

H-2. 가짜 로딩 진행률 (정직성 결여)

  • DocStep3Preview generating 단계의 progress 는 실제 진행과 무관한 random increment
  • "사건 정보 분석 중 / 법원 양식 적용 중 / 청구 취지·원인 작성 중 / 첨부 서류 목록 구성 중" — 정적 메시지, 실제 AI 단계와 대응 0
  • 위험: 사용자가 신뢰를 잃을 수 있음 (예: 100% 가까이에서 오래 멈춤 → 실제는 처음부터 실패 중)
  • 원칙 (ai-pipeline.md): 사용자 트리거형 AI 는 투명한 상태 공개. 가짜 진행률은 원칙 위반

H-3. 편집 모드 저장/취소 분리 부재

  • DocStep3Preview 편집 모드 진입 시 draft state 를 직접 수정 → 편집 중 onBack 누르면 변경 내용 유지된 채 Step 2 이동
  • "편집 취소 (변경 내용 버리기)" 옵션 없음
  • Escape 키 편집 종료 없음
  • 편집 모드 상태에서 onSave 호출 가능 — 편집 확정 전 저장 위험

H-4. "다음 단계" 버튼 disabled 이유 안내 부재

  • canGenerate = baseRequired && (!needsIdNumber || hasIdNumber)
  • disabled 시 사용자에게는 클릭 불가만 보임. 왜 비활성인지 안내 없음
  • missingFields 는 아래쪽 별도 배너로 표시하나 caseId 존재 조건 에서만 렌더 — caseId 없을 때는 무음

M — UX 보강 (Medium)

M-1. 저장 후 흐름 비효율

  • handleSave 성공 → /docs 로 이동 → 방금 저장한 문서를 목록에서 찾아야 함
  • 방금 생성한 결과를 확인하는 자연 경로: /docs/{id} 또는 DocPreviewModal 오픈 옵션
  • 현재는 "저장했으니 목록 가세요" 수준

M-2. 필드 실시간 형식 검증 미흡

  • clientIdNumber 는 자동 포맷 있음 ("000000-0000000")
  • principal 숫자만 허용은 Zod 에서만 — 입력 중 문자 허용됨 (onChange 미가공)
  • interestRate "15%" 입력 가능 → Zod regex 에서만 잡힘 (submit 시점)
  • 입력 시점 실시간 피드백 없어 사용자가 끝에 가서야 실패 인지

M-3. Step 간 전환 시 포커스 관리 없음

  • step=1 → 2 → 3 이동 시 DOM 교체만 발생 — 포커스는 "다음 단계" 버튼에 남음
  • 스크린 리더 사용자가 Step 전환을 인지하려면 수동으로 화면 읽어야 함

M-4. 사건 자동 입력 후 수정 흔적 미표시

  • handleCaseSelect 가 10개 필드를 자동 채움
  • 사용자가 필드 수정 시 "원본과 다름" 표시 없음 → 저장 시 원본 사건 레코드와 불일치 가능 (서류와 사건 데이터 drift)

L — 장기·리서치 (Low)

L-1. 브라우저 뒤로가기·URL Step 반영 부재

  • useSearchParams 로 caseId·docType preselect 만. Step 변경은 state 만
  • 뒤로가기 시 페이지 전체 이탈 → 폼 데이터 유실

L-2. 자동 저장 없음

  • 편집 중 페이지 이탈·새로고침 시 변경 내용 유실
  • Cycle 2 의 saveDocContentAction 은 사후 편집용. 생성 위저드의 draft 는 저장 전 휘발성

L-3. 테스트 커버리지

  • schemas.test.ts (Zod 경계) 있음
  • constants.test.ts (헬퍼) 있음
  • useDocGenerate 훅 · Step1~3 컴포넌트 · 에러 경로 통합 테스트 없음
  • E2E 서류 생성 시나리오 커버리지 미확인 (e2e/ 확인 필요)

4. 우선순위 매트릭스 + 실행 결과

우선순위항목결과 PR릴리스상태
H-1접근성 ARIA#220v0.82.1✅ 완료
P0-1에러 메시지 구체화#221v0.83.0✅ 완료
H-2가짜 로딩 정직화#222v0.84.0✅ 완료
H-3편집 모드 저장/취소 분리#223v0.85.0✅ 완료
M-1저장 후 흐름 개선#224v0.86.0✅ 완료
H-4canGenerate 이유 안내#225v0.87.0✅ 완료

P0-2 (draft PII)정책 결정 필요 — 사용자가 의도적으로 입력한 PII 를 차단해야 하는가? 변호사의 업무 특성상 채권자 주민번호는 법원 제출 서류에 필수 이므로 단순 차단은 불가능. 범위 밖 으로 분류, 차기 세션에서 정책 검토 후 결정.

M-24, L-13 은 장기·리서치 항목 — 이번 세션 범위 밖.


5. 금지 영역 (슬라이스 수행 시 건드리지 않음)

  • lib/ai/client.ts (Gemini 호출 로직)
  • lib/ai/execute-ai-action.ts (reserve/refund 래퍼)
  • app/(workspace)/docs/_actions/doc-generate-actions.ts::saveDocWithHtmlAction (Firestore 저장 비즈니스 로직)
  • app/(workspace)/docs/_actions/ai-assist-actions.ts (한도 트랜잭션)

이유: 핵심 비즈니스 로직. 변경 시 실 변호사 업무에 직접 영향. 본 감사는 UX·에러 표시·접근성 범위 한정.


6. 다음 세션에서 다룰 항목 (이번 범위 밖)

  • P0-2: 본문 PII 처리 정책 결정 (채권자 주민번호 필수 vs 마스킹)
  • M-2: 입력 필드 실시간 검증 (interestRate · principal)
  • M-4: 사건 자동 입력 후 수정 흔적 표시
  • L-1: URL 에 Step 상태 반영
  • L-2: draft 자동 저장
  • L-3: E2E · 훅 테스트 커버리지 확장

7. 참고