forked from baron/baron-sso
네이버 계정 정합성 맞춤
This commit is contained in:
49
docs/adminfront-flicker-trace-analysis-2026-06-15.md
Normal file
49
docs/adminfront-flicker-trace-analysis-2026-06-15.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# adminfront 깜빡임 trace 분석
|
||||
|
||||
작성일: 2026-06-15
|
||||
|
||||
## 입력 자료
|
||||
|
||||
- `adminfront/Trace-20260615T113806.json.gz`
|
||||
|
||||
## 관찰 결과
|
||||
|
||||
- trace 구간은 약 4.18초이다.
|
||||
- DevTools screenshot은 250장 포함되어 있다.
|
||||
- 화면 전체 shell이 사라지는 현상은 아니고, 본문 영역이 반복적으로 repaint되는 형태이다.
|
||||
- `Animation` 이벤트에서 `enter` 애니메이션이 147회 반복 시작되었다.
|
||||
- 반복 대상은 다음 nodeName으로 확인되었다.
|
||||
- `DIV class='space-y-4 animate-in fade-in duration-500'`
|
||||
- 같은 nodeName의 `Paint` 이벤트도 147회 발생했고, span은 약 4.1초였다.
|
||||
|
||||
## 원인
|
||||
|
||||
장기 유지되는 admin page/tab container에 `animate-in fade-in duration-500` 진입 애니메이션이 붙어 있었다.
|
||||
|
||||
이 클래스가 query/refetch, tab content 갱신, 렌더 상태 변화와 결합되면서 본문 영역이 반복 진입 애니메이션을 수행했고, 사용자는 이를 간헐적인 깜빡임으로 보게 된다.
|
||||
|
||||
## 수정
|
||||
|
||||
다음 장기 컨테이너에서 페이지 레벨 진입 애니메이션을 제거했다.
|
||||
|
||||
- `TenantWorksmobilePage` tab panels
|
||||
- `GlobalOverviewPage` root container
|
||||
- `DataIntegrityPage` tab panels
|
||||
- `TenantDetailPage` nested outlet wrapper
|
||||
|
||||
Dialog, dropdown, toast처럼 짧게 열리고 닫히는 transient UI의 state-based animation은 유지한다.
|
||||
|
||||
## 회귀 방지
|
||||
|
||||
- Worksmobile tab panel에 trace 원인 클래스가 다시 들어오지 않도록 테스트를 추가했다.
|
||||
- adminfront 전체 `.tsx`에서 `animate-in fade-in duration-500` 페이지 레벨 패턴이 재도입되지 않도록 정책 테스트를 추가했다.
|
||||
|
||||
## 검증
|
||||
|
||||
```bash
|
||||
pnpm --dir adminfront exec vitest run src/features/coverage/adminPageAnimationPolicy.test.ts --bail 1
|
||||
pnpm --dir adminfront exec vitest run src/features/tenants/routes/TenantWorksmobilePage.test.ts --bail 1
|
||||
pnpm --dir adminfront exec vitest run src/features/overview/GlobalOverviewPage.test.tsx --bail 1
|
||||
pnpm --dir adminfront exec vitest run src/features/integrity/DataIntegrityPage.test.tsx --bail 1
|
||||
pnpm --dir adminfront exec vitest run src/features/tenants/routes/TenantDetailPage.worksmobile.test.tsx --bail 1
|
||||
```
|
||||
@@ -88,7 +88,7 @@
|
||||
테스트:
|
||||
|
||||
- `GOCACHE=/tmp/baron-sso-go-cache go test ./internal/service -run 'TestWorksmobileSyncServiceSkipsSoftDeletedUsersInComparison' -count=1`
|
||||
- `GOCACHE=/tmp/baron-sso-go-cache go test ./internal/handler ./internal/repository -run 'Test.*User|Test.*Projection|Test.*SoftDeleted|Test.*ListUsers' -count=1`
|
||||
- `GOCACHE=/tmp/baron-sso-go-cache go test ./internal/handler ./internal/repository -run 'Test.*User|Test.*SoftDeleted|Test.*ListUsers' -count=1`
|
||||
- `BASE_URL=http://127.0.0.1:5173 npm --prefix adminfront test -- worksmobile.spec.ts --project=chromium`
|
||||
|
||||
결과:
|
||||
|
||||
@@ -170,12 +170,35 @@ Baron Kratos identity를 Worksmobile user로 보냅니다.
|
||||
- `passwordConfig.password`: 구성원 생성 시 숫자, 영문, 기호를 모두 포함한 16자리 난수 초기 비밀번호를 생성합니다.
|
||||
- `task`: Baron `jobTitle`을 우선 사용
|
||||
- `organizations`
|
||||
- 원직: 대표 tenant 또는 `additionalAppointments` 중 primary로 선택된 tenant
|
||||
- 겸직: `metadata.additionalAppointments` 또는 Keto `joinedTenants`
|
||||
- 원직: Worksmobile 연동 제외 테넌트를 제외한 뒤 `additionalAppointments`에 가장 먼저 등록된 tenant
|
||||
- 겸직: `metadata.additionalAppointments` 또는 Keto `joinedTenants` 중 Worksmobile 연동 제외가 아닌 tenant
|
||||
- `orgUnits[].orgUnitId`: `externalKey:{tenant.ID}`
|
||||
- `levelId`, `positionId`, `userTypeId`: 이번 scope에서는 External Key mapping을 사용하지 않고 사용자 정보 업데이트 필드로 최대한 커버
|
||||
- `isManager`: `additionalAppointments[].isOwner == true` 또는 Keto owners/admins relation을 기준으로 변환
|
||||
|
||||
### 구성원 소속 변경 정책
|
||||
|
||||
Worksmobile 연동 화면의 변경 범위는 다음과 같이 분리합니다.
|
||||
|
||||
- 조직/그룹 탭은 Worksmobile `orgunits`/`groups` 리소스 자체의 생성, 수정, 삭제, 이동을 관리합니다.
|
||||
- 구성원 탭은 Worksmobile `users` 리소스의 조직 소속(`organizations[].orgUnits[]`) 변경을 관리합니다.
|
||||
- 구성원 소속 변경 중 Worksmobile에 대상 org unit이 없거나 부모 구조가 최신이 아니면, 먼저 조직/그룹 탭의 조직 sync로 구조 변경을 반영한 뒤 구성원 sync를 수행합니다.
|
||||
- `worksmobileExcluded=true`인 테넌트와 그 하위 조직은 조직 sync 및 구성원 소속 sync의 대상에서 제외합니다.
|
||||
|
||||
개인 사용자 상세 변경은 대표 조직 1개만 수정하는 동작으로 제한하지 않습니다. Baron의 `metadata.additionalAppointments`에 들어 있는 모든 소속 조직을 기준으로 Worksmobile payload의 `organizations[].orgUnits[]`를 구성합니다. 다만 Worksmobile 연동 제외 테넌트, Worksmobile domain ID를 해석할 수 없는 테넌트, 조직 연동 설정이 켜져 있지 않은 겸직 도메인은 payload에서 제외하고 비교 결과에는 skipped reason 또는 warning을 남깁니다.
|
||||
|
||||
WORKS Developers 문서 확인 결과, 구성원 추가/수정 payload는 `organizations[]`와 하위 `orgUnits[]` 배열을 제공하고 `orgUnits`는 최대 30개까지 허용합니다. 또한 대표 도메인과 대표 조직은 각각 하나가 필요합니다. Directory API의 조직 연동 설명에는 구성원 회사 겸직을 설정하려면 겸직 도메인도 조직 연동 사용 설정을 켜야 한다는 제약이 있습니다.
|
||||
|
||||
따라서 구현 원칙은 다음과 같습니다.
|
||||
|
||||
- 동일 Worksmobile domain 안에서는 Worksmobile 연동 제외가 아닌 조직 소속을 모두 동기화합니다.
|
||||
- 여러 Worksmobile domain을 넘는 겸직은 해당 domain의 조직 연동 설정이 켜져 있고 Baron에서 domain ID를 해석할 수 있을 때만 동기화합니다.
|
||||
- Worksmobile primary는 Baron 대표 테넌트 플래그나 `additionalAppointments[].isPrimary=true`를 기준으로 삼지 않습니다. Worksmobile 연동 제외 테넌트를 제거한 뒤 `additionalAppointments`에 가장 먼저 등록된 소속을 최초 반영 소속이자 primary로 둡니다.
|
||||
- `representative`, `isPrimary`, `primary` 같은 Baron 대표 소속 플래그는 Baron 내부 대표 소속 정책에만 사용하고 Worksmobile 동기화 판단 필드로 사용하지 않습니다.
|
||||
- 조직장 여부(`isManager`)는 Worksmobile 동기화 대상입니다. 비교 로직은 remote와 Baron의 조직장 여부 차이를 `needs_update`로 판단해야 합니다.
|
||||
- 비교 로직도 대표 조직 1개가 아니라 `organizations[].orgUnits[]` 전체 set을 기준으로 `org_unit_added`, `org_unit_removed`, `org_unit_moved`, `org_unit_primary_changed` 같은 diff reason을 산출합니다.
|
||||
- Worksmobile 공식 문서 근거: https://developers.worksmobile.com/kr/docs/user-create, https://developers.worksmobile.com/kr/docs/directory
|
||||
|
||||
초기 비밀번호는 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로 해석해 Ory/Keto 관계, 허용된 Backend read model, Worksmobile enqueue가 누락되지 않게 합니다.
|
||||
|
||||
35
docs/worksmobile-phone-outbound-policy-2026-06-15.md
Normal file
35
docs/worksmobile-phone-outbound-policy-2026-06-15.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# WORKS 전화번호 송신 포맷 정책
|
||||
|
||||
작성일: 2026-06-15
|
||||
|
||||
## 목적
|
||||
|
||||
Baron 내부 전화번호 표준과 WORKS API 송신 포맷을 분리한다.
|
||||
|
||||
## 정책
|
||||
|
||||
- Baron 내부 저장 및 비교 표준은 공백 없는 E.164 형태를 유지한다.
|
||||
- 예: `+821041585840`
|
||||
- WORKS 계정 생성 및 업데이트 요청으로 전화번호를 보낼 때는 국가번호와 국내 번호 사이에 공백을 둔다.
|
||||
- 예: `+82 01041585840`
|
||||
- WORKS 송신용 국내 번호는 `0`으로 시작해야 한다.
|
||||
- 내부값 `+821041585840`은 WORKS 송신 시 `+82 01041585840`으로 변환한다.
|
||||
- WORKS 응답 비교는 기존처럼 공백 포함/미포함 형식을 같은 전화번호로 정규화해서 비교한다.
|
||||
|
||||
## 적용 범위
|
||||
|
||||
- WORKS Directory 사용자 생성 요청
|
||||
- WORKS Directory 사용자 업데이트 요청
|
||||
- WORKS SCIM 사용자 생성 요청
|
||||
|
||||
## 비적용 범위
|
||||
|
||||
- Baron DB 저장값
|
||||
- Kratos traits 저장값
|
||||
- Adminfront 비교 화면의 내부 기준 표시값
|
||||
|
||||
## 구현 기준
|
||||
|
||||
- 내부 payload 생성 단계에서는 기존 E.164 정규화 값을 유지한다.
|
||||
- 실제 WORKS API 송신 직전 outbound formatter에서만 `+82 0...` 형식으로 변환한다.
|
||||
- 한국 번호가 아닌 값은 기존 정규화 결과를 유지한다.
|
||||
Reference in New Issue
Block a user