Skip to content

공통 사용자 그룹 동기화 런북 ​

공통 사용자 그룹(그룹 + 멤버십)은 PortalConsole 이 원장이고, 소비 포털이 REST 로 push/pull 한다. 리소스 권한(resource/action)은 각 포털 소유로 남는다. 배경·소유 경계는 central-directory-integration.md 「개정 (2026-10-04)」.

계약 ​

방향엔드포인트scope비고
pullGET /api/directory/v1/user-groups?since=&includeDeleted=directory:readincludeDeleted 기본 포함(true). tombstone이 있어야 삭제가 전파된다
push(생성·수정)POST /api/directory/v1/user-groupsdirectory:sync본문 { id?, name, description?, isActive? }. 멱등
push(삭제)DELETE /api/directory/v1/user-groups/:groupIddirectory:sync소프트 삭제(tombstone). 멱등
push(구성원 추가)POST /api/directory/v1/user-groups/:groupId/membersdirectory:sync본문 { directoryUserId }. 멱등
push(구성원 제거)DELETE /api/directory/v1/user-groups/:groupId/members/:directoryUserIddirectory:sync멱등

조인 키: PC 의 directory 사용자 id 는 users.openId = "directory:<usr_…>" → 접두사를 뗀 값이 원장의 directory_user_id 와 같다. 관리자 화면은 admin.listUserGroups 가 아니라 directory.listUserGroups(tRPC)로 조회한다.

Process Center 측 동작 ​

server/directoryGroupSync.ts

  • push outbox: portalGroupRouter 의 로컬 쓰기(생성·수정·삭제·구성원 추가/제거)가 portal_user_group_sync_queue 에 op 를 적재한다. flushUserGroupOutbox() 가 원장으로 전송하고, 실패하면 attempts 를 올리고 지수 backoff(최대 1시간)로 next_attempt_at 을 민다.
  • pull: pullDirectoryUserGroups() 가 원장을 로컬로 미러한다. tombstone 은 로컬에서 제거한다.
  • 순서: syncUserGroups() = flush → pull. 원장이 정본이라 충돌 시 원장 값이 이긴다.
  • 임포트(이름 매칭): 로컬 그룹과 원장 그룹의 이름이 같은데 id 가 다르면, 로컬 구성원·리소스 권한을 원장 id 로 옮기고 로컬 행을 지운다. 옮긴 구성원은 ADD_MEMBER 로 큐에 넣어 원장에도 반영한다.
  • 격리: 그룹 동기화 실패는 사용자·부서 동기화를 실패로 만들지 않는다(syncDirectorySnapshot 이 warn 만 남긴다). 원장이 아직 user-groups API 를 제공하지 않아도 사용자·부서는 정상 동기된다.

마이그레이션 ​

  • PG: drizzle/postgres-migrations/0023_user_group_sync_queue.sql
  • MySQL: drizzle/0032_user_group_sync_queue.sql
  • 콘솔 원장 테이블: PG drizzle/postgres-migrations/0012_user_groups.sql, MySQL drizzle/0039_user_groups.sql

점검 ​

  1. 콘솔에서 그룹을 만들고 구성원을 넣는다 → directory_user_groups / directory_user_group_members 확인.
  2. PC 스케줄러가 한 주기 돌면 PC 의 portal_user_groups / portal_user_group_members 가 원장과 같아진다.
  3. PC 에서 그룹을 만들면 portal_user_group_sync_queue 에 UPSERT 가 쌓였다가 비워진다(원장 반영).
  4. attempts > 0 행이 남아 있으면 last_error 를 보고 scope·키·central API 상태를 확인한다. 붙지 못한 op 는 next_attempt_at 이후 재시도된다.
  5. 삭제는 양쪽 모두 소프트 삭제로 전파된다 — tombstone 을 건너뛰는 pull 은 삭제를 되살리므로 금지.