다크모드
HR 문서센터 — 외부 전자문서 보관 연동 (BE 참고)
독자: BE 개발자/AI. 급여명세서·재직증명서 등 HR 문서를 외부 전자문서 보관 서비스와 연결할 때 지켜야 할 제품 경계, 데이터 계약, 상태와 실패 처리를 정의한다. 현 상태: 프런트 프로토타입은
documentCenterMock.js의 메모리 데이터만 사용한다. 서버 저장, 외부 보관 등록, 증명파일 발급은 아직 연결되지 않았다. 공급사 비노출 원칙: 공개 화면·도움말·매뉴얼·핸드오프에는 계약 공급사 이름, 포털 주소, 계정, 전용 SDK 이름을 쓰지 않는다. 구체 설정은 비공개 운영 런북과 시크릿에서만 관리한다.
1. 제품 경계
- 문서센터는
documentCenterRepo만 호출한다. 공급사 SDK, 외부 API, 서버 저장 방식은 어댑터 뒤에 숨긴다. - 급여명세서·증명서 발급과 외부 보관은 별개 사건이다. 외부 서비스 장애가 문서 생성 기록을 지우거나 발급 스냅샷을 바꾸면 안 된다.
- 공급사를 교체해도 컴포넌트와 문서 템플릿은 바뀌지 않아야 한다.
- 전자문서 보관·증명과 수신자 송달은 같은 기능이 아니다. 실제 계약에 수신자 전달 API가 없으면
발송,열람,송달완료를 운영 상태로 만들지 않는다.
문서센터 UI
→ documentCenterRepo
→ 문서 저장소
→ 외부 전자문서 어댑터
→ 서버 전용 연결브라우저는 외부 서비스 자격증명, 라이선스, 원문 바이트를 직접 다루지 않는다. 외부 호출은 서버 신뢰 경계 안에서 수행한다.
2. 어댑터 계약
업무 계층이 소비할 최소 계약은 공급사 중립이어야 한다.
| 메서드 | 역할 | 현재 상태 |
|---|---|---|
list() / find(id) / byEmployee(employeeId) | 문서 원장 조회 | ⚠️ 메모리 mock |
createEmploymentCert(input) / createCareerCert(input) | 증명서 스냅샷 생성 | ⚠️ 메모리 mock |
createPayslipBatch(period) | 귀속월 급여명세서 멱등 생성 | ⚠️ 메모리 mock |
register(input) | 생성된 원문을 외부 보관 서비스에 등록 | ❌ 미배선 |
issue(externalArchiveId, options) | 보관 문서의 증명파일 생성 | ❌ 미배선 |
외부 응답은 어댑터에서 다음 중립 형태로 정규화한다.
ts
type ExternalArchiveResult = {
externalArchiveId: string;
externalStorageNumber?: string;
certificateFilePath?: string;
timestampedAt?: string;
contentHash?: string;
traceId: string;
};외부 오류 코드·메시지는 원문 그대로 UI에 전달하지 않는다. 서버 로그에는 traceId와 공급사 원문 코드를 남기고, UI에는 중립적인 재시도 가능 메시지를 반환한다.
3. 데이터 모델
sql
create table hr_documents (
id uuid primary key,
company_id uuid not null,
office_id uuid not null,
employee_id text not null,
employee_name text not null,
company_name text not null,
doc_type text not null,
period text,
gross_pay integer,
purpose text,
period_end date,
issued_at date,
status text not null default 'draft',
file_path text,
external_archive_id text,
external_issue_id text,
certificate_path text,
external_storage_no text,
timestamped_at timestamptz,
content_hash text,
retention_until date,
trace_id text,
registered_at timestamptz,
created_at timestamptz not null default now()
);불변식:
- 발급 문서는 발급 시점 직원·급여·회사 정보의 스냅샷이다. 원장 변경으로 기존 문서가 바뀌지 않는다.
- 급여명세서는
(company_id, office_id, employee_id, doc_type, period)를 멱등 키로 사용한다. - 외부 등록 ID와 보관번호는 고객 관리코드가 아니다. 공급사 변경을 고려한 외부 참조값으로만 저장한다.
- 원문과 증명파일은 비공개 스토리지에 저장하고, 권한 확인을 통과한 짧은 수명의 다운로드 URL만 발급한다.
- Company·Office 스코프를 모든 조회·쓰기에서 검증한다.
4. 상태와 사건
운영 상태는 실제 서버 사건만 반영한다.
| 상태 | 의미 | 전이 원인 |
|---|---|---|
draft | 문서 스냅샷 생성, 외부 미등록 | 로컬/서버 문서 생성 |
registering | 외부 보관 등록 요청 중 | outbox 작업 시작 |
registered | 외부 보관 등록 완료 | 어댑터 성공 |
issuing | 증명파일 생성 요청 중 | 명시적 발급 요청 |
issued | 증명파일 생성 완료 | 어댑터 성공 |
terminated | 보관 종결 | 권한 있는 명시적 종결 |
error | 외부 처리 실패, 재시도 가능 | 어댑터 실패 |
현재 mock의 sent·viewed·completed·returned는 데모 처리내역일 뿐 운영 보관 상태가 아니다. 라이브 전환 때 실제 외부 보관 상태로 교체하고, 수신자 송달 상품이 별도로 구현되기 전에는 송달 성공처럼 표시하지 않는다.
5. 저장·재시도·감사
- 문서 스냅샷과 원문 저장을 먼저 커밋한다.
- 같은 트랜잭션에서 외부 보관 outbox 사건을 기록한다.
- 워커가 outbox를 소비해 어댑터를 호출한다.
- 성공 시 외부 참조·해시·타임스탬프를 기록한다.
- 실패 시 문서 생성 기록은 유지하고 재시도 횟수·다음 시각·오류 분류를 기록한다.
재시도는 동일 request key와 원문 해시를 유지해야 한다. 다른 문서가 같은 request key를 재사용하면 차단한다. 한 직원의 실패가 배치 전체를 롤백하지 않도록 문서 단위로 격리한다.
감사로그 최소 항목:
- Company·Office·사용자·문서 ID
- 사건 종류와 전후 상태
- request key·trace ID·원문 해시
- 요청·완료·실패 시각
- 실패 분류와 재시도 횟수
6. 운영 전제와 현재 UI
- 서버 저장소와 외부 전자문서 어댑터는 미구현이다.
documentCenterRepo.register()와issue()는 명시적으로 미배선 오류를 반환한다.- UI는 실제 핸들러가 없는 발송·일괄 발송·원본 다운로드·증명서 발급·진본 확인 버튼을 노출하지 않는다.
- 목록의 외부 보관번호와 처리내역은 데모 데이터이며 운영 결과로 해석하면 안 된다.
- 운영 활성화 시 권한, 다운로드 URL, 실패·재시도, 감사로그, 상태 동기화를 함께 구현하고 3종 문서를 갱신한다.
7. 문서 종류 확장
payslip: 귀속월·지급총액·급여 계산 스냅샷을 포함한다.employment-cert: 용도·발급일·재직기간을 포함한다.career-cert: 용도·발급일·근무종료일을 포함한다.retirement-settlement: 발급 시점 계산 스냅샷을 포함하며 화면에서 재계산하지 않는다.
새 문서 종류는 doc_type과 템플릿 레지스트리를 확장하되, 저장·보관 어댑터 계약은 재사용한다.