Skip to Content
문서 패키지 셀프서빙

문서 패키지 셀프서빙

집행부가 대시보드에서 동의서 패키지를 직접 준비하고 발행하는 기능입니다. 제품 단위가 개별 동의서에서 문서 패키지로 옮겨가면서, 구 “동의서 서식 워크스페이스”(/[unionId]/vote/workspace)를 대체했습니다.

전체 흐름

워크스페이스 생성 → 문서(hwp) 업로드 → 검토 요청 → 운영팀 승인(어드민) → 상세 정보 입력·확정 → 내보내기 마법사(5단계) → 발행 → 관리 탭에서 추적

발행하면 기존 assignment_package + vote 모델로 실제 생성됩니다. 즉 마법사는 “패키지를 만드는 준비 공간”이고, 발행 이후는 기존 패키지 관리 화면과 같은 데이터입니다.

핵심 개념 3가지

개념설명
워크스페이스 (package_workspace)패키지 1개를 준비하는 공간. 발행 후 전체가 영구 잠김(수정·재발행 불가)
문서 (package_workspace_document)워크스페이스 안의 동의서 슬롯. 업로드 → 운영팀 검토 → 상세 입력 → 준비 완료 파이프라인
내보내기 드래프트 (package_export_draft)마법사 5단계의 임시 저장. 워크스페이스당 1개(1:1)

화면

경로화면
/[unionId]/package만들기 / 관리 2탭 (?tab=)
/[unionId]/package/workspace/[workspaceId]워크스페이스 상세 — 문서 파이프라인 + 사이드패널
/[unionId]/package/workspace/[workspaceId]/export내보내기 마법사 (?step=)

주의: 상세 경로는 변경 금지입니다. 백엔드가 검토 승인·반려 알림톡에서 package/workspace/{workspace_id} 딥링크를 보내므로 경로를 바꾸면 링크가 깨집니다.

코드: apps/dashboard/src/app/(user-only)/[unionId]/package/ API: packages/api/lib/dashboard/index.ts// --- Package workspace --- 섹션 (base v1/dashboard/region/{regionId}/package/workspace) 스웨거 태그: DASHBOARD_PACKAGE_WORKSPACE

권한

전 엔드포인트가 조회까지 EDIT 권한을 요구합니다(authorized_user_id_by_region_for_edit_vote_management). 그래서 package/layout.tsx 가드는 전자문서 구간(READ)과 달리 VOTE_MANAGEMENT + EDIT 입니다. READ만 가진 사용자를 들여보내면 목록 조회부터 403이 나 빈 화면이 됩니다.

함정 (반드시 읽을 것)

이 기능을 만지기 전에 아래를 먼저 확인하세요. 전부 실제로 밟았거나 리뷰에서 잡힌 것입니다.

1. isExportable 이 두 곳에 있고 의미가 다르다

응답포함 조건
GET /{workspaceId} (워크스페이스 상세)문서≥1 + 전부 ready
GET /{workspaceId}/export-draft위 + 전부 fieldsPlaced

발행은 운영팀 필드 배치가 안 끝나면 PACKAGE_DOCUMENT_FIELDS_NOT_PLACED(409)로 막힙니다. 발행 버튼 활성 판정은 반드시 export-draft 쪽 값을 써야 합니다. 상세 쪽 값으로 판정하면 버튼이 켜졌는데 409가 납니다.

2. document_status_counts 는 키가 enum 인 유일한 응답 필드다

fetcher(objectKeySnakeToCamel, packages/utils-ts/object.ts)가 응답 객체의 키를 재귀적으로 전부 camelCase로 바꿉니다. 그래서 6개 상태 중 review_pending 키만 reviewPending으로 도착합니다(나머지는 언더스코어가 없어 무사).

방치하면 counts[PackageWorkspaceDocumentStatus.REVIEW_PENDING]이 조용히 undefined가 되어 만들기 탭 상태 칩 판정이 틀어집니다. API 레이어의 normalizePackageDocumentStatusCounts가 두 키를 모두 보며 흡수합니다.

3. last_completed_step 은 되돌아간다

1단계를 다시 저장하면 서버가 basic으로 리셋합니다. 매 렌더에서 이 값으로 현재 단계를 결정하면 4단계에 있던 사용자가 1단계 저장 직후 앞으로 끌려갑니다. 진입 시 한 번만 시작 위치 계산에 쓰고, 이후 이동은 URL(?step=)이 단일 소스입니다. 되돌아갈 수 있는 범위는 로컬에 누적합니다.

4. 마법사 2단계 ↔ 3단계가 비대칭이다

  • 2단계에서 전자증명서를 고르면 서버가 간편인증을 조용히 해제한다
  • 3단계에서 증명서가 선택된 채 간편인증을 켜면 409 PACKAGE_EXPORT_SETTING_CONFLICT

상호 배타 UI로 처리하고, 2단계 저장 후 드래프트를 refetch해야 합니다(mutation이 getPackageExportDraft를 무효화).

5. 일괄 검토 요청은 uploaded 문서만 담아야 한다

도메인 전이가 uploaded → review_pending만 허용합니다. rejected 문서는 재업로드로 uploaded를 거쳐야 합니다. 하나라도 불가 상태가 섞이면 PACKAGE_DOCUMENT_INVALID_STATUS(409)로 전체가 롤백됩니다(all-or-nothing).

6. 재업로드는 검토요청 행을 새로 만든다

구 플로우의 re_upload_hwp(기존 행 변경)와 달리 새 DRAFT 행이 append됩니다. 그래서 문서 하나가 reviewRequests 여러 건을 갖고, 이력은 요청들을 최신순으로 펼쳐서 보여야 합니다. 반려 사유를 고를 때는 comment 유무가 아니라 action === 'reject' 로 필터해야 합니다. 안 그러면 옛 승인 메모가 “반려 사유”로 노출됩니다.

7. 일시는 KST naive datetime 이다

서버가 타임존 오프셋 없는 문자열을 주고받습니다. API 레이어가 Date를 받아 formatDateToLocalISO로 직렬화하므로 toISOString()을 쓰면 일정이 9시간 밀립니다. 변환 헬퍼는 package/_utils/dateTime.ts에 모아 뒀습니다.

또 서버가 검사하지 않는 순서 제약이 있어 프론트에서 막습니다:

  • 마법사 1단계: startedAt < deadline (서버 미검사)
  • 문서 상세: resultPublishedAt >= deadline (서버 미검사 — 마감 전 공개는 진행 중 결과 유출)

8. 운영팀 재검토는 확정을 해제한다

운영팀이 재검토로 전환하면 서버가 confirmedAtnull로 되돌립니다. ready 문서가 reviewing으로 복귀할 수 있으므로 상태는 항상 서버 응답을 신뢰해야 하고, 사이드패널이 문서 스냅샷을 들고 있으면 안 됩니다(id로 매번 다시 찾음).

9. 실시간 반영이 없다

승인·반려는 운영팀 어드민에서 일어납니다. 화면 재진입·새로고침 시 최신 상태가 조회됩니다.

상태 값

문서 PackageWorkspaceDocumentStatus

enum배지다음 액션
uploaded검토 필요검토 요청
review_pending검토 대기(운영팀 착수 대기)
reviewing검토 중(운영팀 진행 중)
rejected반려됨수정본 업로드 → 검토 요청
approved승인됨 · 입력 필요상세 정보 입력
ready준비 완료(잠금 — 수정은 운영팀 문의)

배지 색은 “누구 차례인가” 로 맞췄습니다 — warning=집행부 차례, neutral=운영팀 차례, error=반려, info=완료.

워크스페이스 PackageWorkspaceStatus: draft(준비 중) / exported(발행 완료·잠금) 마법사 단계 PackageExportStep: basic / certificate / simple_identity_verification / document_order / final_confirm

에러 코드

모두 {error_code, message} 형식이고 message는 한글 그대로 노출 가능합니다.

HTTPerror_code상황
409PACKAGE_WORKSPACE_ALREADY_EXPORTED발행 완료된 워크스페이스 수정
409PACKAGE_DOCUMENT_INVALID_STATUS현재 상태에서 불가한 전환
409PACKAGE_DOCUMENT_NOT_APPROVED승인 전 문서에 상세 정보 입력
409PACKAGE_DOCUMENT_ALREADY_CONFIRMED확정된 문서 수정
400PACKAGE_DOCUMENT_DETAILS_INVALID제목 공백 / 일정 역전
409PACKAGE_EXPORT_SETTING_CONFLICT전자증명서 + 간편인증 동시 설정
409PACKAGE_EXPORT_NOT_READY준비 안 된 문서 포함 / 문서 순서 부분 목록
409PACKAGE_DOCUMENT_FIELDS_NOT_PLACED운영팀 필드 배치 미완료 문서 포함 발행
403PACKAGE_REGION_MISMATCHregion과 워크스페이스 불일치

PACKAGE_REGION_MISMATCH403 입니다(핸드오프 문서에는 404로 묶여 있었습니다). 미존재·soft-delete 리소스는 404 REPOSITORY_NOT_FOUND 입니다.

발행 이후

발행하면 문서마다 vote가 생성되고 전체 명부(토지등소유자 전체) 기준으로 대상자가 등록됩니다. 간편인증·전자증명서 assignment는 서버가 순서상 맨 앞에 배치하므로, 마법사 4단계는 동의서끼리의 순서만 보냅니다.

발송(문자·알림톡)은 발행에 포함되지 않습니다. 패키지 상세의 문자 발송 CTA로 이어집니다. 모든 문서 미제출자에게 문자vote_participations 필터를 프리셋하는데, 백엔드가 이 필터를 AND로 합성하므로(and_(*vote_conditions)) “하나라도 제출한 사람은 빠지고 전부 미제출인 소유자만” 잡힙니다. 라벨이 그 의미로 되어 있습니다.

알려진 미완성

  • 앱 화면 미리보기: 승인 최종본 PDF만 렌더하고, 입력한 상세 정보 오버레이는 미구현
  • 문서 이름 변경: 백엔드 도메인에 rename()이 있으나 HTTP 엔드포인트가 없음
  • 관측: 발행(exportPackage)은 되돌릴 수 없는 액션인데 Logger.addAction 계측이 없음
  • 드로어 포커스 트랩: 사이드패널에 dialog 시맨틱·ESC·포커스 이동은 있으나 Tab 순환 트랩은 없음(공용 유틸 부재)
  • 화면 중복: /[unionId]/vote(전자 문서)와 관리 탭이 같은 두 섹션을 렌더
Last updated on