# WORKS only 사용자 복구 보고서 관련 이슈: #1037 ## 요약 - 작업 시각: 2026-06-09 KST - 대상: WORKS 비교 결과에서 `missing_in_baron`으로 보이던 사용자 row - 최신 후보 수: 189명 - 조치: Baron `users.deleted_at` soft-delete 해제 - Kratos 조치: 직접 삽입/수정 없음. 189개 Kratos identity가 모두 현재 DB에 active 상태로 존재함을 확인함 - 최종 결과: WORKS 사용자 비교의 `missing_in_baron` 0건 ## 원인 이전 사용자 legacy sync 코드가 Kratos `ListIdentities()` 결과를 전체 identity 목록으로 간주했습니다. 해당 API 결과는 제한된 페이지 결과였고, 그 목록에 없던 기존 사용자가 Baron `users`에서 soft-delete 처리되었습니다. 이로 인해 WORKS에는 사용자가 남아 있고 `externalKey`도 Baron 사용자 UUID를 가리키지만, Baron 비교 로직에서는 soft-deleted 사용자가 visible 사용자로 조회되지 않아 `missing_in_baron`으로 표시되었습니다. ## 대조 결과 복구 전 후보 189건 기준: | 구분 | 현재 Baron | 과거 Baron 백업 | 현재 Kratos | 과거 Kratos 백업 | | --- | ---: | ---: | ---: | ---: | | 후보 | 189 | 189 | 189 | 189 | | visible 사용자 | 0 | 189 | - | - | | soft-delete 사용자 | 189 | 0 | - | - | | 누락 사용자 | 0 | 0 | 0 | 0 | | active identity | - | - | 189 | 189 | | credential 보유 identity | - | - | 189 | 189 | 확인한 백업: - `backups/pre-saman-works-users-20260608-063605Z` - `backups/pre-works-only-user-recovery-20260609-083105KST` 상세 리포트: - `reports/works-only-user-recovery-20260609-0832/missing_in_baron_external_keys.csv` - `reports/works-only-user-recovery-20260609-0832/current_baron_candidate_status.csv` - `reports/works-only-user-recovery-20260609-0832/pre_saman_baron_candidate_status.csv` - `reports/works-only-user-recovery-20260609-0832/current_kratos_candidate_status.csv` - `reports/works-only-user-recovery-20260609-0832/pre_saman_kratos_candidate_status.csv` - `reports/works-only-user-recovery-20260609-0832/post_recovery_baron_candidate_status.csv` ## 조치 내용 복구 전 DB 백업을 생성했습니다. - `make dump DUMP_SERVICES=postgres,ory-postgres BACKUP=backups/pre-works-only-user-recovery-20260609-083105KST` 이후 후보 189건에 대해 현재 Baron DB에서 다음 조건으로만 soft-delete를 해제했습니다. - WORKS `missing_in_baron` row의 `externalKey`가 UUID여야 함 - 해당 UUID가 현재 Baron `users.id`에 존재해야 함 - 해당 Baron row가 `deleted_at IS NOT NULL`이어야 함 - 해당 UUID가 현재 Kratos `identities.id`에 active 상태로 존재해야 함 복구 결과: - 복구된 Baron 사용자: 189명 - 전체 Baron `users`: 2114명 - visible 사용자: 2106명 - soft-delete 사용자: 8명 - 후보 189명 중 Kratos active identity: 189명 - 후보 189명 중 credential 보유 identity: 189명 ## 최종 검증 실제 Kratos admin 세션으로 로컬 게이트웨이를 경유해 검증했습니다. - `GET /api/v1/admin/users?limit=5000&offset=0` - HTTP 200 - `total=2106` - `items=2106` - `GET /api/v1/admin/tenants/038326b6-954a-48a7-a85f-efd83f62b82a/worksmobile/comparison?includeMatched=true` - HTTP 200 - 사용자 비교 총 2110건 - `matched=2073` - `missing_in_baron=0` - `missing_in_worksmobile=30` - `needs_update=1` - `missing_external_key=6` - 조직/그룹 비교 총 187건 - 조직/그룹 `missing_in_baron=1` 테스트: - `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` - `BASE_URL=http://127.0.0.1:5173 npm --prefix adminfront test -- worksmobile.spec.ts --project=chromium` 결과: - Go service 테스트 통과 - Go handler/repository 테스트 통과 - adminfront Worksmobile Playwright 4개 테스트 통과 ## 재발 방지 이미 적용된 코드 변경으로 다음 조건을 방어합니다. - admin 사용자 목록은 Kratos 250개 제한 결과가 아니라 Backend cursor API와 identity mirror 상태를 기준으로 조회합니다. - legacy replace sync는 Kratos partial list에 없는 사용자를 삭제 처리하지 않습니다. - WORKS 비교 로직은 soft-deleted Baron 사용자를 visible 사용자로 취급하지 않습니다. - WORKS 비교 UI에는 필터링 후 표시 row와 전체 row 수를 함께 표시합니다. 남은 확인 항목: - 조직/그룹 비교의 `missing_in_baron=1`은 사용자 복구와 별개 항목입니다. 별도 이슈로 원인 확인이 필요합니다. - `missing_external_key=6` 사용자 row는 WORKS 측 externalKey가 없으므로 자동 복구 대상에서 제외했습니다.