Skip to content

인사 조직도·직원 디렉터리 — Backend Handoff

정본과 범위

Supabase Prototype의 물리 스키마 정본은 다음 단일 baseline이다.

  • supabase/migrations/20260822000000_organization_workspace_canonical_baseline.sql

인사 디렉터리는 테넌트 Organization과 HR 조직도 단위를 서로 다른 개념으로 유지한다. 이전 scope 명칭을 위한 relation, column, RPC, JSON alias나 fallback은 두지 않는다.

개념DB 계약Frontend 계약
테넌트organization_idorganizationId
업무 범위workspace_idworkspaceId
HR 조직도 단위workforce_organization_idorganizationUnitId
상위 HR 조직도 단위parent_workforce_organization_idparentOrganizationUnitId

데이터 모델

workforce_organization_units

Organization 내부의 본부·부서·팀·현장 조직도를 보존한다.

  • 식별·범위: id, organization_id
  • 고객 식별: management_code, display_name
  • 계층: organization_type, parent_workforce_organization_id
  • 상태: active | inactive | archived
  • organization_type: organization | division | department | team | site
  • 상위 단위는 같은 organization_id를 가져야 한다.

workforce_employees

업무 모듈이 공유하는 최소 비PII 직원 identity다.

  • 식별·범위: id, organization_id
  • 고객 식별: management_code, employee_number, display_name
  • 수명주기: employment_status, hire_date, termination_date, revision
  • 로그인 연결은 선택적인 workforce_identity_id로 분리한다.

주민등록번호, 주소, 급여계좌 같은 민감정보는 이 테이블에 추가하지 않는다. 필요하면 별도 제한 테이블과 접근감사·암호화·보존정책을 먼저 설계한다.

workforce_assignments

직원의 Workspace·HR 조직도 배치를 effective-dated 행으로 보존한다.

  • 범위: organization_id, workspace_id
  • 대상: employee_id, workforce_organization_id
  • 배치: assignment_kind, position_title, job_title
  • 기간·상태: valid_from, valid_until, status, revision
  • 직원, Workspace, HR 조직도 단위는 모두 같은 Organization이어야 한다.

primary는 직원 전체에서 기간 중복을 허용하지 않는다. concurrent | temporary는 같은 Workspace·HR 조직도 단위·배치 유형의 기간이 겹치지 않아야 한다.

읽기 계약

src/composables/operationalSubjectRepo.js가 public table을 읽고 raw row를 canonical DTO로 정규화한다.

  • listOrganizationUnits(organizationId)workforce_organization_units
  • listEmployees(organizationId)workforce_employees
  • listAssignments(organizationId)workforce_assignments

컴포넌트는 Supabase row의 snake case를 직접 소비하지 않는다. HR 조직도 값은 DTO에서 항상 organizationUnitId로 노출하며 테넌트 organizationId와 혼용하지 않는다.

감사 이벤트는 검증된 도메인 mutation과 같은 트랜잭션에서 서버가 append한다. 브라우저 adapter에는 범용 감사 append 함수를 노출하지 않는다.

Mutation RPC

create_workforce_organization_unit

  • p_organization_id
  • p_management_code
  • p_display_name
  • p_organization_type
  • p_parent_workforce_organization_id
  • p_request_key

결과는 생성된 workforce_organization_units 행이다. 관리코드는 trim·대문자 정규화 후 Organization 범위에서 고유하고 생성 후 불변이다.

create_workforce_employee

  • p_organization_id
  • p_management_code
  • p_employee_number
  • p_display_name
  • p_hire_date
  • p_request_key

employee_number는 등록번호이므로 중복을 허용한다. UUID와 Organization 범위 management_code가 고유성을 보장한다.

update_workforce_employee

display_name, hire_date만 수정한다. employee_number, management_code, 로그인 identity, 재직상태는 입력이 아니다. p_expected_revision 불일치는 workforce_employee_revision_conflict다.

create_workforce_assignment

  • p_organization_id
  • p_employee_id
  • p_workspace_id
  • p_workforce_organization_id
  • p_assignment_kind
  • p_position_title, p_job_title
  • p_valid_from, p_valid_until
  • p_request_key

Workspace는 active이고 같은 Organization에 속해야 하며 human-capital 상품 capability가 유효해야 한다. HR 조직도 단위는 선택값이지만 전달하면 같은 Organization의 active 행이어야 한다.

배치·직원 수명주기

  • end_workforce_assignment: 현재 배치의 종료일과 revision을 확정한다.
  • cancel_workforce_assignment: 시작 전 scheduled 배치만 취소한다.
  • transition_workforce_employee_lifecycle: terminate | archive 전이를 처리한다.

termination_date는 첫 비재직일이고 valid_until은 마지막 유효일을 포함한다. 미래 퇴직은 termination_scheduled, 효력 발생 뒤에는 terminated로 투영한다. 보관은 퇴직 효력이 발생했고 현재·예정 배치가 없을 때만 허용한다.

상태 파생

배치 raw 상태는 active | ended | cancelled다. 화면 수명주기는 다음처럼 파생한다.

text
cancelled                         -> cancelled
ended                             -> ended
active + valid_from > as_of       -> scheduled
active + valid_until < as_of      -> ended
그 외                             -> current

직원은 termination_date와 raw employment_status를 함께 사용해 active | on_leave | termination_scheduled | terminated | archived를 투영한다.

권한·감사·동시성

  • public 함수는 검증된 wrapper이며 privileged 구현은 private schema, 빈 search_pathsecurity definer다.
  • 유효 access Role manager 이상이면서 범위·상품 게이트를 모두 통과한 주체만 민감 인사 mutation을 실행한다.
  • Workspace 범위 관리자는 활성 Workspace Membership과 human-capital Product Activation을 모두 가져야 한다.
  • authenticated의 direct INSERT/UPDATE/DELETE는 허용하지 않는다.
  • request_key는 Organization 범위 멱등 키다. 같은 payload 재시도는 기존 응답을 반환하고 다른 payload 재사용은 conflict로 거부한다.
  • employee·assignment advisory lock, row lock, expected revision 순서를 유지해 동시 생성·종료· 수명주기 변경의 write skew를 막는다.
  • mutation과 operational_audit_events, lifecycle event, command response는 같은 트랜잭션에 기록한다.

선택적 제품 연결

직원 수명주기 변경은 provider-neutral platform integration event를 기록할 수 있다. payload에는 직원 UUID, 관리코드, 사건·적용일, 저장/투영 상태, revision, assignment changes만 담고 사유와 PII는 전달하지 않는다. 근태·급여·전자결재는 이 이벤트를 독립적으로 소비하며 HR mutation이 다른 제품 원장을 직접 수정하지 않는다.

검증

  • SQL smoke: supabase/tests/human_capital_directory_smoke.sql
  • 배치 수명주기: supabase/tests/workforce_assignment_lifecycle_smoke.sql
  • 직원 수명주기: supabase/tests/workforce_employee_lifecycle_smoke.sql
  • Repository: src/composables/__tests__/humanCapitalDirectoryRepo.spec.js
  • 공통 subject seam: src/composables/__tests__/operationalSubjectRepo.spec.js

배포 전에는 단일 baseline reset, SQL smoke, RLS·ACL·cross-Organization 거부, 멱등 replay/conflict, revision conflict, 배치 overlap, 예정·즉시 종료와 catalog의 폐기 scope 식별자 0건을 확인한다.