26 KiB
웍스모바일 Directory 연동 기술 검토
개요
- 대상 Epic:
orgfront와 웍스모바일 Directory API 간 한맥가족 사용자/조직 연동 - 관련 이슈: #668 한맥가족 이메일 local-part unique 정책
- 대상 마일스톤:
한맥가족사 조직도 반영 및 웍스모바일 연동(id=42) - 기준 SoT:
hanmac-family테넌트 subtree 하위 Kratos identity - 작성일: 2026-05-04
현재 Baron SSO 구조 요약
Baron SSO는 Ory Stack을 SoT로 두고, PostgreSQL은 read-model 및 비즈니스 메타데이터 저장소로 사용합니다. docs/SoT_Architecture_Policy.md와 docs/tenant-usergroup-policy.md 기준으로 Identity는 Kratos, 권한/멤버십은 Keto, 테넌트/조직 메타데이터는 PostgreSQL이 담당합니다.
현재 사용자 생성 흐름은 다음과 같습니다.
backend/internal/handler/user_handler.goCreateUser:companyCode로 tenant slug를 찾아traits["tenant_id"]에 tenant UUID를 저장합니다.BulkCreateUsers: CSV/import row별로 tenant slug를 tenant UUID로 변환하고 Kratos identity를 생성합니다.- Kratos identity id는
users.id로 그대로 저장됩니다. 이 값이 웍스모바일userExternalKey후보입니다. mapToLocalUser는traits["tenant_id"]를users.tenant_id로 저장합니다.
backend/internal/handler/tenant_handler.goCreateTenant:TenantService.RegisterTenant호출 후 domains/config를 별도로 저장합니다.UpdateTenant: tenant 필드와 parent relation을 갱신합니다.
backend/internal/service/keto_relay_worker.goketo_outbox를 polling하여 Keto relation을 비동기로 반영합니다.
한맥가족 이메일 정책은 이미 #668에서 다음 방향으로 구현되어 있습니다.
hanmac-familyroot tenant와 descendant subtree에서 email local-part를 unique로 강제합니다.- 단건 생성은 중복 시
409 Conflict로 차단합니다. - bulk import는
@domain입력 시 이름 기반 local-part를 제안하고, 생성 직전 재검증합니다.
웍스모바일 Directory API 확인 사항
공식 문서 기준 Directory API는 구성원, 조직, 그룹, 직급, 직책, 사용자 유형 등을 관리합니다.
확인한 주요 엔드포인트와 제약은 다음과 같습니다.
- 인증
- API 호출에는 OAuth 2.0 Access Token이 필요합니다.
- 시스템 연동에는 서비스 계정 인증(JWT) 방식이 적합합니다.
- 필요한 scope는 최소
directory이며, 구성원만 다룰 경우user, 조직만 다룰 경우orgunit도 사용 가능합니다.
- 구성원
POST https://www.worksapis.com/v1.0/users- 필수 주요 필드:
domainId,email,userName - SSO 사용 시
userExternalKey가 필요합니다. userExternalKey는 테넌트 내 unique이며%,\,#,/,?를 사용할 수 없습니다.organizations[].orgUnits[].orgUnitId는 resource id 또는externalKey:{orgUnitExternalKey}형태를 사용할 수 있습니다.
- 조직
POST https://www.worksapis.com/v1.0/orgunits- 필수 주요 필드:
domainId,orgUnitName,displayOrder orgUnitExternalKey는 테넌트 내 unique이며%,\,#,/,?를 사용할 수 없습니다.parentOrgUnitId는 resource id 또는externalKey:{orgUnitExternalKey}형태를 사용할 수 있습니다.
- External Key Mapping
POST /users/external-keysPOST /orgunits/external-keys- 기존 웍스모바일 리소스에 External Key가 없는 경우 초기 bulk mapping에 사용합니다.
- 호출 제한/운영 주의
- 조직 추가/수정/부분 수정/이동 API는 도메인당 단일 스레드로 1초에 1회, 순서대로 호출해야 합니다.
- 동일 구성원에 대한 추가/수정/부분수정/전배 API는 동시에 호출하지 않아야 합니다.
- Directory API 조직 연동 배치는 직급/직책/사용자 유형 -> 조직 -> 구성원 -> 그룹 순서를 권장합니다.
- API 동시 호출은 5회 이상 하지 않도록 관리해야 하며, 특히 조직 API는 단일 스레드가 필요합니다.
AdminFront bulk 생성과 NAVERWORKS bulk 생성 비교
구현 전에 adminfront의 기존 조직/사용자 bulk 생성 방식과 adminfront/NAVERWORKS_member_add_sample_English.csv의 구성원 bulk 필드를 비교했습니다.
Baron/AdminFront 기존 방식
- 조직 bulk
adminfront/src/features/tenants/utils/tenantCsvImport.ts- 주요 컬럼:
tenant_id,name,type,parent_tenant_id,parent_tenant_slug,slug,memo,email_domain - Baron tenant tree를 직접 생성/갱신합니다.
- parent는 Baron tenant UUID 또는 slug 기준으로 해석합니다.
- 사용자 bulk
adminfront/src/features/users/components/UserBulkUploadModal.tsxparseUserCSV가BulkUserItem으로 변환한 뒤/api/v1/admin/users/bulk로 전송합니다.- 주요 컬럼:
email,name,phone,role,tenant_slug,department,position,jobTitle,employee_id - 사용자 import 중 없는 tenant를 미리 생성할 수 있고, #668 한맥가족 email local-part unique preview를 거칩니다.
NAVERWORKS sample 방식
- 구성원 bulk sample
- 파일:
adminfront/NAVERWORKS_member_add_sample_English.csv - 주요 컬럼:
LastName,FirstName,ID,Personal email,Sub email,User type,Level,Organization,Position,Mobile/Country code,Mobile/Numbers,Responsibilities,Workplace,Entry Date,Employee number,Account activation time ID는 Baron loginId 및 Worksmobile userExternalKey와는 다른 계정 local-part 성격입니다.Organization은org.1|org.2|org.3|myteam처럼 path 문자열로 제공됩니다.Employee number는 Baron metadata의employee_id로 보존합니다.
- 파일:
구현 반영
parseUserCSV를 quoted CSV/BOM에 대응하도록 보강했습니다.- NAVERWORKS 구성원 sample 필드를 Baron bulk user field로 흡수합니다.
Sub email의 첫 이메일 -> BaronemailID-> BaronloginId, metadatanaverworks_idFirstName+LastName-> BaronnameMobile/Country code+Mobile/Numbers-> BaronphoneOrganizationpath leaf -> Barondepartment, tenant import namePosition-> BaronpositionResponsibilities-> BaronjobTitleEmployee number-> metadataemployee_id
- Worksmobile API payload 생성 시에는 request body의 external key/domainId를 사용하지 않고 Baron UUID와
tenant.config.worksmobile.domainMappings및.env의 domainId 값을 server-side 계산합니다.
매핑 설계
External Key
Baron 내부 UUID는 웍스모바일 External Key 제한 문자와 충돌하지 않으므로 그대로 사용할 수 있습니다.
- 구성원
userExternalKey: Kratos identity UUID, 즉users.id - 조직
orgUnitExternalKey: Barontenants.id - 조직 지정:
externalKey:{tenant.ID} - 구성원 지정:
externalKey:{user.ID}또는 email/resource id
이 선택은 "Kratos account가 사용자 SoT"라는 정책과 맞습니다. 사용자 생성 후 Worksmobile resource id가 생기더라도 Baron의 primary mapping은 Kratos UUID를 유지하고, Worksmobile resource id는 캐시/응답 추적용으로만 보관하는 것이 좋습니다.
조직
Baron tenant를 Worksmobile orgunit으로 보냅니다.
- 대상 tenant:
hanmac-familyroot 하위 subtree 중COMPANY,USER_GROUP - 제외 후보:
PERSONAL, system/global 성격 tenant orgUnitName:tenant.nameorgUnitExternalKey:tenant.idparentOrgUnitId: parent가 Worksmobile 동기화 대상이면externalKey:{parentTenant.ID}domainId: tenant domain 또는 root integration config에서 email domain별로 해석displayOrder: 동일 parent 내 deterministic order 필요. 1차 구현은name asc,created_at asc, 또는 별도config.worksmobile.displayOrder정책 중 하나를 선택해야 합니다.
주의할 점은 Worksmobile orgunits가 domainId를 필수로 요구한다는 점입니다. Baron은 한맥가족 root 아래에 여러 이메일 도메인과 법인/조직 subtree를 둘 수 있으므로, 우선 tenant.config.worksmobile.domainMappings를 지원하되 운영 domainId는 .env의 다음 값을 fallback으로 사용합니다.
SAMAN_DOMAIN_ID: 삼안 계열HANMAC_DOMAIN_ID: 한맥 계열GPDTDC_DOMAIN_ID: 총괄기획&기술개발센터BARONGROUP_DOMAIN_ID: 위 세 가지에 속하지 않는 모든 한맥가족사
분류 순서는 config mapping -> 삼안 -> 한맥 -> GPDTDC -> BARONGROUP fallback입니다.
개발/스테이징에서 테스트한 값은 프로덕션에도 동일하게 반영해야 합니다. 네이버웍스 쪽 backend가 동일 환경이므로 이 mapping은 env-only 값으로 숨기지 않고 seed/config migration 등 코드 레벨에서 추적 가능한 값으로 남깁니다.
{
"worksmobile": {
"enabled": true,
"tenantId": "hanmac-family",
"domainMappings": {
"hanmaceng.co.kr": 10000001,
"samaneng.com": 10000002
}
}
}
구성원
Baron Kratos identity를 Worksmobile user로 보냅니다.
userExternalKey: Kratos UUID (users.id)email: #668 정책으로 확정된 emailuserName.lastName:traits["name"]또는users.name전체를 우선 입력합니다. 성/이름 분리가 확정되기 전까지는 API 필수 조건 충족을 위해 전체 표시명을lastName에 둡니다.cellPhone: normalized phoneemployeeNumber: metadata의employee_id또는 schema login ID 값이 있으면 사용privateEmail: 기본 매핑하지 않습니다. NAVERWORKS sample의Personal email은 Baron metadata에는 보존하지만 Worksmobile payload에는 기본 전송하지 않습니다.aliasEmails: 한맥 tenant에 속하고employee_id가 있으면employee_id@hanmaceng.co.kr을 추가합니다.locale: 별도 지정이 없으면ko_KRpasswordConfig.passwordCreationType:ADMIN값을 구성원 생성 시에만 사용합니다.passwordConfig.password: 구성원 생성 시 숫자, 영문, 기호를 모두 포함한 16자리 난수 초기 비밀번호를 생성합니다.task: BaronjobTitle을 우선 사용organizations- 원직: 대표 tenant 또는
additionalAppointments중 primary로 선택된 tenant - 겸직:
metadata.additionalAppointments또는 KetojoinedTenants orgUnits[].orgUnitId:externalKey:{tenant.ID}levelId,positionId,userTypeId: 이번 scope에서는 External Key mapping을 사용하지 않고 사용자 정보 업데이트 필드로 최대한 커버isManager:additionalAppointments[].isOwner == true또는 Keto owners/admins relation을 기준으로 변환
- 원직: 대표 tenant 또는
초기 비밀번호는 Worksmobile user upsert outbox payload에 loginEmail, initialPassword 형태로 함께 보관하고, adminfront의 한맥가족 Worksmobile 관리 화면에서 email,initialPassword,status,lastError CSV로 다운로드할 수 있게 합니다. 생성 성공/실패 판정은 outbox 작업 상태(processed, failed)와 함께 확인할 수 있으며, 운영상 평문 초기 비밀번호가 포함되므로 다운로드 권한은 hanmac-family tenant manage 권한으로 제한하고 보존 기간 정책을 별도 확정해야 합니다.
현재 backend CreateUser와 UpdateUser는 adminfront가 보내는 top-level additionalAppointments 및 metadata.additionalAppointments를 수용합니다. 한맥가족 단건 생성에서 대표 tenantSlug 없이 appointment만 오는 경우에는 first/primary appointment tenant를 대표 tenant로 해석해 Kratos traits, local read-model, Worksmobile enqueue가 누락되지 않게 합니다.
구성원 수정과 비밀번호 정책
Worksmobile 구성원 수정 API에는 PUT(user-update-put)과 PATCH(user-update-patch)가 있지만, 비밀번호 변경 경로로 사용하지 않습니다.
- Worksmobile Directory API에서 관리자 지정 초기 비밀번호 값은 별도 top-level
password가 아니라passwordConfig.password입니다. passwordConfig.passwordCreationType은 생성 방식이고,passwordConfig.password가 실제 초기 비밀번호 값입니다.- 공식 문서와 개발자 포럼 확인 결과,
passwordConfig.passwordCreationType과passwordConfig.password는 모두 구성원 등록 시에만 실제 반영됩니다. - PUT/PATCH request body에
passwordConfig.password를 포함해도 기존 구성원의 비밀번호 변경에는 반영되지 않습니다. - 따라서 Baron SSO는 WORKS Mobile 구성원 생성 시에만 초기 비밀번호를 설정하고, 생성 이후 Baron 사용자 비밀번호 변경을 WORKS Mobile PUT/PATCH로 전파하지 않습니다.
- 생성 이후 WORKS Mobile 비밀번호 변경은 WORKS Mobile 관리자 페이지 또는 WORKS Mobile이 제공하는 별도 운영 절차에서 직접 처리합니다.
- PATCH/PUT payload에는
passwordConfig를 포함하지 않습니다. privateEmail도 기존 정책대로 기본 전송하지 않습니다.- 기존 WORKS Mobile 구성원에 대한 일반 속성/조직/겸직 동기화는 생성 효율을 위해 먼저
POST /v1.0/users를 시도하고,409 Conflict일 때PATCH /v1.0/users/{email}로 전환합니다. - PUT은 전체 교체 성격이 강하고 누락 필드 초기화 위험이 있으므로 현 scope에서는 사용하지 않습니다. 모든 Baron -> WORKS 변경 반영은 부분 수정 PATCH를 우선합니다.
비동기 아키텍처 권장안
Worksmobile API를 handler에서 직접 호출하지 않고, 별도 outbox와 relay worker를 둡니다.
권장 신규 테이블:
worksmobile_outboxidresource_type:ORGUNIT,USERresource_id: Baron tenant/user UUIDaction:UPSERT,DELETE,EXTERNAL_KEY_MAPpayload: JSONBdedupe_keystatus:pending,processing,processed,failedretry_count,last_errornext_attempt_at,processed_at,created_at,updated_at
worksmobile_resource_mappingsbaron_resource_typebaron_resource_idexternal_keyworksmobile_resource_iddomain_idlast_synced_at
권장 신규 service/client:
backend/internal/service/worksmobile_client.gobackend/internal/service/worksmobile_sync_service.gobackend/internal/service/worksmobile_relay_worker.gobackend/internal/repository/worksmobile_outbox_repository.go
현재 구현은 WORKS_ADMIN_* OAuth 또는 directory token을 사용하는 WorksmobileHTTPClient와 WorksmobileRelayWorker를 통해 worksmobile_outbox의 pending 사용자 작업을 Directory API로 전달합니다. 사용자 생성은 POST https://www.worksapis.com/v1.0/users를 먼저 호출하고, 이미 존재하는 구성원으로 409 Conflict가 발생하면 PATCH /v1.0/users/{email}로 전환합니다.
SCIM은 주경로로 사용하지 않습니다. 기존 검토에서 SCIM은 다음 이유로 보류했습니다.
- Directory API의
passwordConfig.passwordCreationType = ADMIN생성 정책을 그대로 표현하기 어렵습니다. - SCIM 경로는 검증 조건 때문에 private mail 성격의 email 값을 강제해야 하는 문제가 있었습니다.
- Baron 정책은
privateEmail기본 미전송이므로 SCIM을 주경로로 삼지 않습니다.
WORKS_ADMIN_OAUTH_CLIENT_ID, WORKS_ADMIN_OAUTH_CLIENT_SECRET, service account private key는 Directory API 호출용 토큰 발급에 사용합니다. OAuth redirect URI 등록이 필요한 경우 다음 경로를 사용합니다.
http://localhost:5000/api/v1/admin/worksmobile/oauth/callback
로컬 Playwright 검증에서는 위 callback 경로가 브라우저에서 도달 가능함을 확인했습니다.
Worker 정책:
- orgunit 작업은
domainId별 단일 worker lane, 최소 1초 간격 - user 작업은 같은
userExternalKey단위로 순차 처리 - 전체 동시성은 5 미만
- 409는 idempotent conflict로 보고 user PATCH 전환
- 404 parent orgunit은 parent job 선처리 후 retry
- 429/5xx는 exponential backoff
AdminFront 운영 화면 배치와 권한 정책
Worksmobile 운영 화면은 orgfront가 아니라 adminfront의 tenant detail 하위에 둡니다. 데이터 성격상 tenant 관리자 도구이며, 실제 사용자 동선은 hanmac-family tenant detail에서 바로 확인할 수 있게 만드는 쪽이 맞습니다.
화면 노출 정책:
- 전역 메뉴에는 Worksmobile 메뉴를 추가하지 않습니다.
adminfronttenant list에서 한맥가족 root tenant detail에 들어갔을 때만 Worksmobile 탭 또는 버튼을 표시합니다.- 노출 조건은 tenant detail API 응답의
slug == "hanmac-family"또는 동일한 canonical 식별자로 판단합니다. - 별도 운영 URL을 새 탭으로 여는 방식은 허용합니다. 단, 새 탭 화면도 tenant-scoped route여야 하며 URL을 직접 입력해도 같은 권한 검사를 통과해야 합니다.
orgfront는 필요 시 read-only sync badge나 adminfront deep link 정도만 담당하고, 생성/재시도/삭제 같은 운영 액션은 제공하지 않습니다.
권한 강제 정책:
- frontend의 탭 숨김은 UX 보조일 뿐이며 보안 경계로 보지 않습니다.
- backend endpoint는
/api/v1/admin/tenants/:tenantId/worksmobile/*처럼 tenant-scoped 형태로 둡니다. - handler 또는 middleware에서
tenantId가 존재하는지, 해당 tenant가 정확히hanmac-familyroot인지 먼저 확인합니다. - 요청자는
super_admin이거나Tenant:{tenantId}에 대한 관리 권한을 Keto로 통과해야 합니다. 기존RequireKetoPermission(..., "Tenant", "manage")또는 이에 준하는 relation을 사용합니다. - user/orgunit 개별 동작은 대상 resource가
hanmac-familysubtree 안에 있는지 추가로 확인합니다. - Worksmobile
domainId,userExternalKey,orgUnitExternalKey는 request body의 임의 입력을 신뢰하지 않고 server-side tenant config와 Baron UUID에서 계산합니다. - tenant가
hanmac-family가 아니거나, 사용자가 해당 tenant를 관리할 수 없거나, 대상 resource가 subtree 밖이면 작업을 생성하지 않고403 Forbidden또는 존재 노출을 줄이는404 Not Found로 차단합니다.
관리 화면에서 필요한 최소 기능:
- Worksmobile config/domain mapping 조회
- 조직/구성원별 최근 sync 상태와 마지막 오류 조회
- 단건 조직/구성원 sync enqueue
- 실패 작업 retry
- backfill dry-run 결과 조회 및 제한된 실행 버튼
삽입 지점
기존 계정 bulk/backfill
hanmac-familysubtree tenant 전체를 읽습니다.- Worksmobile에 이미 존재하는 조직/구성원의 External Key Mapping을 먼저 수집하거나 Developer Console CSV mapping으로 보정합니다.
- Baron tenant를 depth asc로 정렬해
ORGUNIT UPSERToutbox를 생성합니다. - Baron user를
users.id기준으로 읽고USER UPSERToutbox를 생성합니다. USER UPSERT는 해당 사용자의 orgunit mapping이 processed인 뒤 실행합니다.
신규 tenant 생성
- 후보 위치:
TenantHandler.CreateTenant에서replaceTenantDomains성공 후 - 더 좋은 위치:
TenantService.RegisterTenant가 tenant/domain/config/outbox를 하나의 transaction으로 저장하도록 정리한 뒤 같은 transaction 안에서worksmobile_outbox를 생성 - 조건: 생성된 tenant가
hanmac-familysubtree 하위이고 type이COMPANY또는USER_GROUP
tenant 수정
- 후보 위치:
TenantHandler.UpdateTenant에서h.DB.Save(&tenant)및 domain update 성공 후 - parent 변경 시 Worksmobile orgunit move 또는 patch가 필요합니다.
- 현재
UpdateTenant는 Keto parent outbox를 tenant 저장 전에 생성하므로, Worksmobile 이전에 outbox transaction 정합성 개선을 권장합니다.
신규 사용자 단건 생성
- 후보 위치:
UserHandler.CreateUser에서 Kratos 생성, local DB sync, login ID sync, Keto outbox enqueue 후 - payload에는
identityID, email, name, phone, tenantID, metadata/additionalAppointments를 포함합니다. hanmac-familyscope가 아니면 enqueue하지 않습니다.
신규 사용자 bulk 생성
- 후보 위치:
UserHandler.BulkCreateUsers에서 row별 local DB sync와 Keto outbox enqueue 후 - row별 partial success를 유지하고, Worksmobile enqueue 실패는 사용자 생성 실패와 분리하는 것이 좋습니다.
- 단, enqueue 실패는 audit/error로 남기고 운영자가 재시도할 수 있어야 합니다.
사용자 수정/소속 변경
- 후보 위치:
UserHandler.UpdateUser에서 Kratos update와 local DB sync 후 email,name,phone,companyCode,tenant_id,metadata.additionalAppointments변경 시USER UPSERTenqueueinactive는 Worksmobile suspend로 동기화합니다.- Baron user delete는 Worksmobile delete로 동기화합니다.
leave-of-absence는 필요하지만 orgfront/Baron user status model 확장이 선행되어야 하므로 별도 scope로 분리합니다.
테스트 전략
기능 추가이므로 테스트를 먼저 작성합니다.
Backend unit
- Worksmobile request mapper
- tenant UUID ->
orgUnitExternalKey - parent tenant ->
parentOrgUnitId = externalKey:{parentID} - Kratos UUID ->
userExternalKey additionalAppointments->organizations[].orgUnits[]- #668 email final value 사용
- tenant UUID ->
- External Key validator
%,\,#,/,?포함 시 reject
- Domain mapping resolver
- email domain ->
domainId - missing mapping -> blocking error 또는 skipped job
- email domain ->
Backend repository/service
worksmobile_outboxenqueue idempotency- orgunit depth order enqueue
- user job이 orgunit processed 전에는 보류되는지 확인
- retry/backoff/status transition
- 409 conflict 시 get/patch 전환
Handler
CreateTenant가 한맥가족 subtree tenant 생성 시ORGUNIT UPSERTjob을 생성CreateUser가 한맥가족 사용자 생성 시USER UPSERTjob을 생성- 한맥가족 외부 tenant는 job을 만들지 않음
BulkCreateUsers에서 row별 성공 결과와 Worksmobile enqueue 결과가 분리됨
Frontend unit
- CSV alias가 Worksmobile/Baron 컬럼을 정상 매핑하는지 확인
additionalAppointments를 유지한 채 사용자 생성/수정 payload가 만들어지는지 확인- tenant detail이
hanmac-familyroot일 때만 Worksmobile 탭/버튼을 표시하는지 확인 - non-hanmac tenant detail이나 직접 URL 접근에서 Worksmobile 운영 화면이 차단되는지 확인
E2E/manual
- adminfront에서
hanmac-family하위 조직 생성 -> outbox 생성 -> mock Worksmobile orgunit create 확인 - bulk import로 기존 계정 생성 -> #668 email preview -> Worksmobile user create mock 확인
- 신규 구성원 단건 생성 -> Worksmobile user create mock 확인
- orgfront 조직도 표시가 기존
joinedTenants/additionalAppointments기준과 충돌하지 않는지 확인 - adminfront tenant list -> 한맥가족 detail -> Worksmobile 운영 화면 새 탭 -> 단건 sync 결과 확인
- non-hanmac tenant detail에서는 Worksmobile UI가 보이지 않고 직접 URL 접근도 backend에서 차단되는지 확인
주요 리스크와 선행 결정
-
domainIdmapping 관리- Worksmobile API는
domainId가 필수입니다. - mapping 위치는
tenant.config.worksmobile.domainMappings로 결정했습니다. - 개발/스테이징에서 검증한 값이 프로덕션에도 적용되어야 하므로 코드 레벨 seed/config로 추적 가능해야 합니다.
- Worksmobile API는
-
additionalAppointmentsbackend 정규화- 현재 frontend는 값을 보내지만 backend 단건 생성 path는 구조적으로 처리하지 않습니다.
- Worksmobile 연동 전 Baron 내부 membership과 metadata 정합성을 먼저 맞춰야 합니다.
-
transaction 정합성
- 현재 tenant create/update와 Keto outbox는 완전한 transactional outbox가 아닙니다.
- Worksmobile outbox는 신규 구현 시 DB 변경과 같은 transaction으로 enqueue되도록 설계하는 것이 좋습니다.
-
Worksmobile orgunit rate limit
- 조직 API는 도메인당 단일 스레드/1초 1회 제약이 있으므로 worker lane 설계가 필수입니다.
-
기존 Worksmobile 데이터 backfill
- Developer Console External Key Mapping이 비어 있는 기존 조직/구성원은 CSV mapping 또는 API external-key update가 선행되어야 합니다.
-
상태 정책
- Baron
inactive는 Worksmobile suspend로 동기화합니다. - Baron delete는 Worksmobile delete로 동기화합니다.
- leave-of-absence는 별도 user 상태 확장 이슈로 분리합니다.
- 직급/직책/사용자 유형 External Key sync는 이번 scope에서 제외합니다.
- Baron
-
adminfront 권한 경계
- Worksmobile 운영 화면은
hanmac-familyroot tenant detail에서만 보이게 합니다. - 별도 URL/새 탭은 허용하지만 backend는 tenant slug, Keto 관리 권한, subtree membership을 모두 확인해야 합니다.
- UI hidden state만으로 접근 제어를 대신하지 않습니다.
- Worksmobile 운영 화면은
권장 구현 순서
- Worksmobile integration config와
tenant.config.worksmobile.domainMappingsseed/config 정책 확정 additionalAppointmentsbackend DTO/parser/Keto membership sync 정리- Worksmobile mapper unit test 작성 후 RED 확인
worksmobile_outboxrepository/service/worker 구현- tenant orgunit enqueue 및 mock client GREEN
- user enqueue 및 mock client GREEN
- backfill command 또는 admin API dry-run 구현
- adminfront 상태/재시도 UI 또는 최소 운영 조회 API 추가
- E2E/mock integration 검증
문서 업데이트 후보
README.md: 관리 데이터 import 정책에 Worksmobile sync 상태와 External Key 원칙 추가docs/organization-chart-policy.md: Worksmobile orgunit mapping 정책 추가docs/tenant-usergroup-policy.md: 외부 Directory sync outbox 정책 추가docs/worksmobile-directory-sync-technical-review.md: 본 문서를 위키 반영 전 검토본으로 유지