본문으로 건너뛰기

ADR 0057: 인플랫폼 서식 템플릿

한 줄 본질("법률 사무소가 자기 데이터로 자기 AI 를 학습·활용해 소송을 더 빠르고 정확하게 관리")의 서류 생산 속도·정확성 축에 직접 기여. 사무소가 자기 표준 서식을 플랫폼 안에서 소유·재사용.

배경

설정 → 서식 템플릿은 docKind별 .docx 파일 업로드/다운로드 방식이었다. 이는 두 가지 마찰을 만든다.

  1. 외부 왕복: 사무소가 양식을 고치려면 Word/한글로 .docx 를 내려받아 편집 후 다시 업로드해야 한다. 플랫폼은 이미 ADR 0018(인플랫폼 Tiptap 에디터)·ADR 0056(카탈로그 → 바로 편집기)으로 편집을 플랫폼 안으로 옮겼는데, 템플릿 저작만 구(舊) docx-first 세계에 남아 있었다.
  2. 좁은 적용: .docx 템플릿(docx-templates 엔진)은 일부 docKind(집행·형사 등 파일 존재분)에만 적용되고, 대부분의 도메인 서류는 코드에 하드코딩된 평문 빌더(buildDemandNoticeText 등)로 생성되어 사무소가 골격을 커스터마이즈할 수 없었다.

또한 .docx 편집 경로는 에디터 진입 시 어차피 평문으로 납작해진다(plainTextToTiptapDoc) — 즉 docx 서식의 리치 포맷 가치는 편집 흐름에서 이미 대부분 소멸한다.

결정

서식 템플릿 저작을 플랫폼 내 Tiptap 에디터로 전환한다. 사무소는 docKind별 표준 서식을 에디터에서 직접 작성하고, {{필드}} 변수를 "변수 삽입"으로 꽂는다. 저장은 Tiptap JSON(tenants/{tid}/docTemplates/{kind}). .docx 업로드는 "기존 양식 가져오기" 보조 경로로 격하한다.

인플랫폼 템플릿은 .docx(docx-templates)와 별개 시스템이다. 따라서 placeholder 어휘를 우리가 정의하며(코드 SSoT = docgen-field-catalog.ts), .docx 의 FOR/IF 엔진 디렉티브를 변호사에게 노출하지 않는다(ADR 0056 교훈). 자동 다중 줄 필드(예: 청구원인)는 단일 토큰({{causeLines}})으로 표현하고 전개는 소비 단계가 책임진다.

단계 (phasing)

  • Phase 0 — 필드 카탈로그 (완료): docKind별 삽입 가능 변수 선언 SSoT + 골격(scaffold) 생성기 + invariant 테스트(scaffold 토큰 ⊆ 카탈로그). 동작 변경 0.
  • Phase 1 — 인플랫폼 편집기 (완료): 설정 → 서식 템플릿이 master-detail 목록(카드 클릭 → /settings/templates/[kind] 에디터). Tiptap 저작 + 변수 삽입 + Firestore JSON 저장(docTemplates). 대표(owner) 전용. mutation 은 @neohollo/business-logic/doc-templates(ADR 0028 pure/mutation 분리). 생성 초안 자동 반영은 아직 미연결 — 작성·보관 단계.
  • Phase 2 — 소비 연결 (완료, demand-notice): open-demand-notice-in-editor-action 이 사무소 JSON 서식 있으면 placeholder 를 사건 데이터로 치환해 초안으로 사용, 없으면 builder 폴백. 치환 엔진 substitute-template-tokens(스칼라 인라인 + 단일 리스트 토큰 문단 전개 + 미해결 [라벨]). e2e 라이브 검증.
  • Phase 3 기반 (완료): 공유 헬퍼 buildEditorDocContent(전역 플래그 + 서식/폴백 단일 진입점) + buildCommonCaseBindings(clientRole 해석 당사자 매핑) + 전역 킬 스위치 features.ai.docTemplateV2(gate !== false, seed scripts/seed-doc-template-v2-flag.ts).
  • Phase 3 도메인 연결 (완료, 19종): (a) 단일-kind 민사 11종 demand-notice + 소장·신청 10종. (b) 멀티-kind 4종 — 지급명령·소장(recovery-doc) + 추심·가압류(exec-doc, fallbackContentJson 경로). (c) engagement 4종 — 위임계약서·소송위임장·수임료정산·종결보고서(buildEngagementBindings, 의뢰인·사무소 중심). 당사자는 buildCommonCaseBindings 의 clientRole 해석.
  • Phase 3 도메인 특화 바인딩 (채권·집행 완료): 지급명령·소장(recoveryBindings ← toRecoverySummary: 청구 종류·금액·이율) + 추심·가압류(exec output: 압류·청구채권·피보전채권·공탁 금액) 자동 채움. 서식이 builder 를 대체하므로 금액 미채움 시 빈약 → 채움 필수.
  • HWPX/DOCX/TXT 가져오기 (완료): 서식 편집기 "파일에서 가져오기" — 기존 한글(.hwpx)·워드(.docx)·텍스트(.txt) 양식 본문 추출(born-digital, functions 포팅) → 에디터 로드 → 검토 후 저장. 사용자 원래 "hwp/hwpx 업로드" 요구 대응. PDF·구 .doc 는 OCR/CFB-binary 필요로 제외.
  • ops /templates JSON 가시성 (완료): 운영 콘솔에 사무소별 인플랫폼 서식(Firestore docTemplates) 패널 (tenant 순회 per-tenant 읽기, .docx override 와 별개 섹션).
  • engagement 도메인 바인딩 (완료): 위임계약서(수임료·개요·보수조건)·소송위임장(법원)·수임료 정산서(수임료·경비·총액)·종결 보고서(개요·결과) 자동 채움. scope·nextSteps 등 비정형 필드는 [라벨](변호사 입력).
  • per-docKind 기본 골격 (완료): buildTemplateScaffoldText 가 공통 골격 + docKind 유형별 필드를 포함 — 각 서류 종류에 맞춘 "공용 기본" 서식을 동적 생성(별도 Firestore 사전적재 불요).
  • 전 범위 완료. (선택 잔여: 공용 기본 JSON 의 Firestore 사전적재 — 현재 동적 scaffold 로 충분.)

Cross-feature 영향 / negative case

  • ops /templates: Storage(.docx) 스캔·mammoth 미리보기는 그대로 — Phase 1~2 는 병존이라 무영향. Phase 3 에서 JSON 대응.
  • 두 RAG 결합(ADR 0041): 템플릿 종류와 무관 → 영향 없음.
  • 권한: 편집·저장·삭제는 owner 전용(staff 는 목록 비활성 + 라우트 redirect). 액션 단(session.role === "owner") + 라우트 단 이중 게이트.
  • 격리: 경로가 tenants/{tid}/... 로 시작 — cross-tenant 불가. mutation 은 Admin SDK(server action)만, client onSnapshot 없음(docSnippets 선례와 동일, firestore.rules 항목 불요).
  • negative: 미지원/형사 kind → 거부. 빈 템플릿 → Phase 2 에서 폴백. Firestore 저장은 plain JSON(Timestamp 미반환).

대안 (기각)

  • .docx 업로드 포맷 확장(hwp/hwpx/pdf): 각 포맷용 치환 엔진을 새로 만들어야 하고, 무엇보다 플랫폼이 없애려는 파일 왕복을 오히려 강화한다. 인플랫폼 저작이 그 니즈("기존 한글 양식 재사용")의 상당 부분을 Phase 3 import 로 흡수한다.