Skip to content

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. 저장·재시도·감사

  1. 문서 스냅샷과 원문 저장을 먼저 커밋한다.
  2. 같은 트랜잭션에서 외부 보관 outbox 사건을 기록한다.
  3. 워커가 outbox를 소비해 어댑터를 호출한다.
  4. 성공 시 외부 참조·해시·타임스탬프를 기록한다.
  5. 실패 시 문서 생성 기록은 유지하고 재시도 횟수·다음 시각·오류 분류를 기록한다.

재시도는 동일 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과 템플릿 레지스트리를 확장하되, 저장·보관 어댑터 계약은 재사용한다.