System Admin 재설계 — 개발 계획 (v0)
2026-08-20 작성.
feat/system-admin-redesign. 짝 문서:design-system-admin-statutory-monitor.md(이 재설계 위에 얹을 첫 소비 기능),project_account_layer(meta account — 인증 토대).
1. 목표 — 세 축
현재 tools/system-admin은 단일 DB에 raw pg 직접 쿼리 + 환경변수(IP allowlist + 단일 JWT) 구조다. 이를 다음으로 전환한다.
| # | 축 | 현재 | 목표 |
|---|---|---|---|
| ① | 데이터 접근 | server/db.ts의 raw pg (TypeORM/코어 로직 미사용) | API 서버 경유 — 각 엔드포인트의 tRPC 시스템 프로시저 호출 (코어 로직 재사용) |
| ② | 인증 | 환경변수 IP allowlist + 단일 시스템 JWT (server/auth.ts) | 시스템 계정 인증 — accounts.platformRole(meta account) 기반, 감사 주체 식별 가능 |
| ③ | 토폴로지 | 한 DB(한 조직군) | 멀티 엔드포인트 — 엔터프라이즈 클라이언트마다 독립 API+DB+인증 DB, 한 콘솔에서 전환·순회 |
핵심 통찰: ③ 때문에 system-admin은 어떤 단일 DB에도 직접 붙을 수 없다. N개의 독립 배포(API+DB)를 HTTP로 호출하는 중앙 오케스트레이터가 되어야 한다. 따라서 ①은 "TypeORM으로 바꾸기"가 아니라 "HTTP API 클라이언트로 바꾸기"다.
그리고 인증도 엔드포인트마다 독립이다 — §2.1이 이 재설계의 최난도 핵심.
2. 목표 아키텍처
┌───────────────────────────┐
운영자 로그인 ──▶ │ system-admin (중앙 콘솔) │
│ · 엔드포인트 레지스트리 │
│ · 운영자 세션(시스템 계정) │
│ · fan-out 오케스트레이터 │
│ · 중앙 릴리스/감사 기록(선택)│
└───────────┬───────────────┘
per-endpoint 시스템 계정 토큰으로 호출
┌───────────────┬───────┴───────┬───────────────┐
▼ ▼ ▼ ▼
API+DB (ent A) API+DB (ent B) API+DB (ent C) ...
systemProcedure systemProcedure systemProcedure
(전 org 집계 = 자기 DB 안에서)- 각 API 서버(
packages/api): 시스템 스코프 프로시저(systemProcedure)를 노출. 자기 DB 안에서 전 조직 집계/조직 상세를 코어 로직 재사용으로 계산. - system-admin 서버: raw
pg(db.ts) 제거 → ⑴ 엔드포인트 레지스트리(소규모 로컬 저장) + ⑵ 엔드포인트별 세션 매니저(§2.1) + ⑶ fan-out 유틸(부분 실패 허용·타임아웃·버전 미지원 구분). 화면은 그대로 Alpine 탭.
2.1 인증 모델 — 본인 계정 · 전 org 접근 시스템관리자 계정 (★ 핵심, 2026-08-20 정정)
결정(사용자): 기존 인증 시스템을 그대로 쓴다. 인증자는 서비스 계정이 아니라 자기 계정(본인 계정)으로 접근한다. 시스템관리자 계정 = 한 엔드포인트 안에서 모든 organization을 방문할 수 있는 Account이며, 특정 org의 권한은 그 org의 grant/role을 받아 부여된다. 본인 계정으로 접근하므로 감사 주체가 자연히 사람으로 남는다(이전 초안의 "서비스 계정 + 신원 전달" 긴장은 폐기).
현재 인증 시스템 (코드 재확인, 2026-08-20)
조직 접근은 전부 명시적·조직당이다. "전 org 접근" 능력은 아직 없다.
| 경로 | 무엇 | 스코프 |
|---|---|---|
org 세션 (resolveUser, JWT userId) | ACTIVE Employee 로드 → 그 조직 권한 | 조직 1개 (멤버십) |
owner 암묵 grant (resolveOwnerGrantUser) | ownerAccountId == account.id 검증 → 전권 | 자기 소유 조직 1개 |
grant 세션 (resolveGrantUser, JWT grantId) | organization_grants 행의 스코프/impersonation | grant가 지정한 조직 1개 |
platformRole(OPERATOR/SUPPORT) | grant 발급·회수 + audit 조회 | 그 자체로 org 접근 없음 — 들어가려면 명시적 grant 필요 |
→ 빌드해야 할 능력(gap): "시스템관리자 계정은 grant 행 없이도 임의의 org에 진입" — 즉 owner 암묵 grant(자기 조직)를 전 org로 일반화한 것. resolveOwnerGrantUser 패턴을 그대로 확장한다(Phase 0).
목표 모델 — 계층 확정 (2026-08-20)
결정: 벤더 발급 유지 + 시스템관리자는 전 org를 스스로 전권 제어(고객사 요청 루프 없음). org 동의는 계약 약관(시스템 강제 없음 — 2026-07 결정 유지). 두 계층으로 나뉜다.
- 시스템관리자 = OPERATOR (전 org 전권, 내재) —
platformRole == OPERATOR면 그 엔드포인트의 어느 org든 열거·진입 + 전권(owner 동형). 진입 시 JWT{accountId, organizationId, systemGrant:true}→resolveSystemGrantUser가 platformRole만으로 SUPER_ADMIN 상당 세션을 합성. per-org grant 행 불필요 — 매번 발급·승인 없이 즉시 제어(사용자: "그 권한까지 제어 가능해야, 매번 고객사 요청 불가"). 감사는 본인 계정으로 전량 기록되므로 통제는 사후 audit로. - SUPPORT = per-org 스코프 grant (OPERATOR가 발급) — 제한된 지원 인력은 전권이 아니라
organization_grants(roleCode/grantedPermissions/impersonation)를 OPERATOR가 벤더-사이드로 발급받아 특정 org만 스코프 접근. 이게 Phase B에서 이미 구현된 부분. (org 관리자 승인 경로는 두지 않음 — 발급 권한은 벤더.)
즉 최상위 시스템관리자는 계정 속성(OPERATOR)만으로 전 org 전권, 하위 지원 계층은 발급받은 grant로 스코프 접근. platformRole 두 값이 그대로 이 두 계층이다.
멀티 엔드포인트에서의 세션 (독립 realm은 그대로)
각 엔터프라이즈 배포는 여전히 독립 인증 realm(자체 accounts·JWT secret·auth_tokens)이라 공유 IdP는 없다. 따라서:
- 운영자의 시스템관리자 계정은 엔드포인트마다 존재(같은 이메일이라도 각 DB의 별도 account row). system-admin은 엔드포인트별로 운영자 본인 자격증명으로 로그인해 세션을 얻고, 그 세션으로 그 엔드포인트의 전 org에 접근한다.
- system-admin은 엔드포인트별 세션을 동시에 보유·관리한다(세션 매니저): 엔드포인트 키별 토큰 캐시·독립 만료·401 시 해당 엔드포인트만 재로그인. 한 엔드포인트 세션이 다른 엔드포인트에 영향 없음.
- 자격증명 저장: 운영자 본인 자격증명을 콘솔에서 엔드포인트별로 1회 입력(또는 리프레시 토큰 저장)해 볼트에 암호화 저장(
encryptedColumn재사용). 서비스 계정이 아니므로 이 볼트는 "운영자 개인의 엔드포인트별 세션 자격"이다. (저장 정책·SSO 여부는 §5-2.) - 감사: 각 엔드포인트 audit_log의 주체 = 운영자 본인 시스템관리자 계정. 별도 신원 전달 불필요.
3. 재사용 토대 (이미 있음)
accounts.platformRole(OPERATOR/SUPPORT) +AccountPrincipal.platformRole— 축 ②의 계정 속성. (meta account Phase B)operatorProcedure/platform.router패턴 — 시스템 계정 게이트의 선례.systemProcedure는 이걸 "org 비종속 + 전 조직 스코프"로 일반화한 형태.audit_logs(migration 1864) +recordOperatorAction— 축 ②의 감사 주체 기록에 재사용.- 즉 ②의 절반은 이미 구현됨. 남은 것은 "org에 묶이지 않는
systemProcedure타입"과 "각 엔드포인트가 시스템 계정 토큰을 발급/신뢰하는 방식".
4. 개발 단계
Phase 0 — 시스템관리자 계정: 전 org 전권 접근 + systemProcedure (API) · 토대
- 전 org 진입 능력(gap):
resolveOwnerGrantUser(자기 조직 1개)를 일반화한resolveSystemGrantUser추가 —platformRole == OPERATOR면 임의의 org에 전권(owner 동형) 진입.{accountId, organizationId, systemGrant:true}JWT. 진입 API:account.enterOrganizationAsSystemAdmin(organizationId)(OPERATOR 게이트, org 열거 포함). per-org grant 불필요. (SUPPORT 계층의 스코프 접근은 기존 grant 경로 그대로.) systemProcedure신설(packages/api/src/trpc.ts): 전 조직 집계(health)·특정 org 상세(audit) 둘 다 지원하는 org-비종속 시스템 스코프.platformRole필수. 멀티테넌시 예외를 여기서 명시적으로 못 박는다(토큰 위조 아닌 계정 속성으로 판정).- read/write 구분 축을 처음부터(§5-3): 최소 프로시저 단위, 가능하면
platformRole별. - 감사: 전 org 진입 세션은 이미
actingVia(meta account) 흐름을 타 audit_log에 본인 계정으로 기록됨 — Phase C 인프라 재사용.systemProcedure호출도 동일 훅. - 검증: 시스템관리자 계정으로 임의 org 진입 OK(본인 계정 감사행) / 일반 계정은 거부 /
systemProcedure전 org 집계 OK.
Phase 1 — 엔드포인트 레지스트리 + 다중 세션 매니저 + fan-out · 재사용 공통 (★ 인증 난제)
- 레지스트리: 이름·baseURL·환경(prod/stg)·서버버전 + 엔드포인트별 운영자 본인 자격증명(암호화, §2.1). 저장 위치 §5-1.
- 세션 매니저(§2.1): 엔드포인트별 본인 계정 독립 로그인 → 토큰 캐시(엔드포인트 키) → 만료 추적·선제 갱신 → 401 시 해당 엔드포인트만 재인증. 엔드포인트 간 세션 격리가 불변식.
- fan-out 유틸: 등록된 전 엔드포인트 순회 호출(각자 자기 토큰 부착) → 결과 병합(성공/실패/미지원(구버전) 3분류), 타임아웃, 재시도. 법정공제뿐 아니라 모든 헬스류가 재사용하므로 공통 유틸로.
- 검증: 2개 로컬 엔드포인트(같은 API 두 포트, 서로 다른 JWT secret으로 realm 분리 재현) → 각각 독립 로그인·세션 보유 확인 / 한쪽 토큰 만료·재인증이 다른 쪽 무영향 / 한쪽 다운 시 부분 실패 정상 처리.
Phase 2 — 기존 raw-pg 라우트를 API-경유로 점진 이관 (진행 중)
server/db.ts(rawpg) 소비처를 하나씩 API 클라이언트로 교체. 한 번에 다 바꾸지 않고 라우트별로.- 순서 권고(위험 낮은 read부터):
organizations목록/상세 →attendance뷰어 →roles/admins→provision(write, 신중) →secom-sync/integrations/sheet-templates. - 각 이관마다 대응
systemProcedure를 API에 추가(코어 로직 재사용). 화면 계약(응답 shape)은 유지해 UI 변경 최소화. platform-accounts탭(platformRole 부여)은 특수: 이건 "각 엔드포인트 DB의 accounts"를 다루므로, 멀티 엔드포인트에선 "어느 엔드포인트의 계정인가" 축이 붙는다 — 이관 시 이 점 반영.
진행 현황 (2026-08-20)
- ✅ 공통 인프라 + 읽기 라우트 6개 이관·검증:
- 공통:
orchestrator/runtime.ts(레지스트리+세션 싱글턴, dev 부트스트랩SYSADMIN_ENDPOINT_CREDS) ·orchestrator/org-scoped.ts(callOrgScoped— per-org 단일 엔드포인트 호출 헬퍼) ·fanout.ts의endpointStatuses헬퍼. - fan-out(크로스-org):
organizations목록 →platform.systemOrganizations/sheet-templates→systemSheetTemplates/national-holidays→systemHolidayYears. - per-org(단일 엔드포인트 라우팅,
?endpoint=):organizations/:id상세 →systemOrganizationDetail/:orgId/admins→systemOrgAdmins/:orgId/roles→systemOrgRoles. - API systemProcedure는 전부 TypeORM 재사용(raw SQL 복제 없음). 응답 계약 유지 + 엔드포인트 차원 부가.
- 라이브: 목록 162 · 상세 6역할/1관리자 · 시트템플릿 23 · 공휴일 3년 · admins 1 · roles(EMPLOYEE 32권한).
- 공통:
- ✅ 읽기 배치 2 이관·검증 (2026-08-20):
callOrgScoped를 임의 input 수용으로 일반화 후 —- per-org:
roles/:orgId/permissions→systemPermissions(109권한/35그룹) ·attendance/:orgId→systemOrgAttendance(필터·페이지네이션, BESTRH 6908건·page2·ISO checkIn 검증) - fan-out:
secom-sync→systemSecomSyncStates(엔티티 없어 API raw query 단일화)
- per-org:
- ✅ 읽기 배치 3 이관·검증 (2026-08-20):
integrations자격증명 목록 →systemIntegrationCredentials(fan-out). 복호화·마스킹을 API-side에서 처리 — 원문 시크릿은 API 밖으로 나가지 않고AK_liv...7890형태 마스킹만 전달. 라이브: 시드 1건 삽입해 마스킹 round-trip +provider필터 + 잘못된 provider 400 검증 후 정리. - ✅ 쓰기 배치 1 이관·검증 (2026-08-20): 조직 쓰기 3종 → API-경유 mutation.
PUT /:id/status→systemSetOrganizationStatus·PUT /:id→systemUpdateOrganization·DELETE /:id→systemDeleteOrganization(조직+직원+owner 계정 소프트삭제·토큰 무효화·confirmName 검사를 트랜잭션으로 재현).- 감사 패턴 확립: 각 쓰기가
recordOperatorAction(actor=운영자, 대상 org, redact input)으로 append-only 기록. system-admin은callOrgScopedMutation헬퍼로 400/403/404 원 상태코드 보존. - 라이브: 스크래치 조직+직원 1건으로 status→SUSPENDED / name·phone 수정 / confirmName 불일치 400 / 삭제(deletedEmployees=1) + 소프트삭제 캐스케이드 + audit_logs 3건(actorKind=OPERATOR·ok=true) 검증 후 정리.
- ✅ 쓰기 배치 2 이관·검증 (2026-08-20): secom-sync 4종(create/toggle/reset-cursor/delete) → API-경유 mutation.
secom_sync_state(엔티티 없음)는 API 내부 raw query, toggle/reset/delete는 대상 행 organizationId 조회 후 감사. 감사 공통 헬퍼recordSystemWrite추가.callOrgScopedMutation을 4xx(400/403/404/409) 원 상태코드 보존으로 일반화. 라이브: 전 라이프사이클+중복409+없는키404+audit_logs 4건 후 정리. - ✅ 쓰기 배치 3 이관·검증 (2026-08-20): integrations 5종(create/update/webhook-secret generate·revoke/delete) → API-경유 mutation. 암호화 키를 API만 보유 — sysadmin은 시크릿 원문(TLS)만 보내고 API가 암호화 저장, sysadmin에서 필드 암복호 로직 완전 제거. 응답은 항상 마스킹(웹훅 생성 시에만 새 원문 1회). 라이브: create시 DB에 ciphertext(
v1:…) 저장 확인 + 전 라이프사이클 + audit_logs 5건(create input apiKey=[REDACTED]) 후 정리. - ✅ 쓰기 배치 4 이관·검증 (2026-08-21): org-less 플랫폼 레벨 쓰기 3종 → API-경유. 결정 (a) 채택:
audit_logs.organizationId를 nullable 로 완화(마이그레이션 1886)하고 organizationId=null 로 감사 — platformRole 부여 같은 민감 행위도 감사에 남는다.- sheet-templates(category/isActive) →
systemPatchSheetTemplate· platform-accounts(platformRole 부여·해제, 읽기 계정검색도 함께 이관) →systemPlatformAccounts/systemSetPlatformRole· national-holidays(sync) →systemSyncNationalHolidays(코어syncNationalHolidays재사용 — raw-pg diff 규칙 복제 제거). recordOperatorAction/recordSystemWrite의 organizationId 를string|null로 확장. AuditLog 엔티티도 nullable.- 라이브(마이그레이션 후): SUPPORT 부여·해제 / 템플릿 category 변경(DB 확인) / 공휴일 2025 sync(fetched 19) + audit_logs 5건 전부 organizationId=NULL·OPERATOR 후 정리.
- sheet-templates(category/isActive) →
- ✅ 쓰기 배치 5 이관·검증 (2026-08-21): roles 3종(create/update/delete + role_permissions 트랜잭션) · admins 3종(create/update/delete + accounts·employees 트랜잭션) → API-경유 mutation.
- 역할: 중복 코드 409·permissionCodes 전체 교체·SUPER_ADMIN 삭제 금지·소프트삭제 시 권한링크 정리. 관리자: 비밀번호 원문(TLS)→API bcrypt 해시(감사 [REDACTED]), Account 생성→employees.accountId 연결, 비번 변경은 accounts 갱신, 마지막 관리자 삭제 방지.
- TypeORM gotcha 발견·교정:
manager.query는UPDATE…RETURNING을[rows, affectedCount]로 반환(INSERT…RETURNING은 rows 직접) — 라이브 테스트에서 잡음. - 라이브: 역할 전 라이프사이클(권한교체·isActive 토글 시 권한 보존) / 관리자 전 라이프사이클(신규·변경 비번 로그인 실증) + audit_logs 11건 후 정리.
- ✅ 쓰기 배치 6 이관·검증 (2026-08-21) — 쓰기 이관 완료: provision 3종 + preview → API-경유. raw SQL 복제본을 버리고
provisionWorkspace(조직 생성의 단일 진입점) 재사용 — 서비스 주석의 '후속 통합 대상' 이행. 법정공제 요율 시드·알림 기본값·SUPER_ADMIN 배정까지 가입 경로와 동일.- 서비스 확장(하위호환):
owner.employeeNumber(미지정 시 자동) ·organization.linkOwnerAccount(콘솔 createOwnerAccount 의미) · legalName/medicalField/timezone. - seed-defaults 는
seedRolesAndPermissions(정본) 재사용 — 기존 역할 권한을 카탈로그로 동기화(기존 콘솔의 add-missing에서 의미 통일, 주의). seed-item-codes 는 콘솔 확장 카탈로그 유지(skip-if-exists).DEFAULT_PERMISSIONS/ROLES를 @hereby/database 에서 export. - national-holidays 연도별 목록(마지막 raw-pg 읽기)도 이관.
- 라이브: preview(106/6) / WT_PROV 프로비저닝(조직·관리자·관리팀·SUPER_ADMIN·항목코드·statutory 10, 원문 비번 로그인 OK) / 중복 409 / seed 2종 / 공휴일 목록 19건 + audit 3건 후 정리.
- 서비스 확장(하위호환):
- Phase 2 이관 완료. 읽기 전부 + 쓰기 배치 1~6 전부 API-경유, 전 쓰기 감사됨. routes 디렉터리의 db.js 참조 0 — 남은 raw pg 는
db.ts자체와 서버 부팅부(search_path 설정)뿐이라 Phase 3 의 db.ts 제거가 기계적 작업이 됐다.
Phase 3 — 인증 전환 마무리 + 환경변수 인증 제거 ✅ (2026-08-21 완료)
- ✅ 콘솔 로그인 운영자 본인 계정 기반 전환: env 단일 계정(SYSADMIN_USERNAME/PASSWORD) 제거. 로그인 = 대상 엔드포인트(미지정 시 첫 엔드포인트)
account.login검증 +platform.systemWhoAmI(신설 systemProcedure)로 OPERATOR 확인 → 콘솔 JWT 발급 + 그 엔드포인트 세션 자동 등록. IP allowlist 는 심층 방어로 유지. 로그인 화면은 이메일 기반으로 전환. - ✅ Endpoints 탭 신설: 엔드포인트 목록 + 상태(
?probe=true→ systemWhoAmI 라이브 확인: ok/인증필요/연결실패) + 엔드포인트별 자격증명 등록(연결)·해제 UI. §2.1의 "운영자가 콘솔에서 엔드포인트별 본인 자격증명 입력"이 이것으로 구현됨(인메모리 — 영속 vault 는 §5-2 대기). - ✅ 검증-후-등록 원칙:
verifyOperatorCredential헬퍼가 SessionManager 밖에서 로그인+OPERATOR 를 검증하고 성공 시에만setCredentials. 실패한 로그인/재등록 시도가 기존 등록 세션을 파괴하던 버그를 라이브 테스트에서 발견·교정. - ✅ raw pg 완전 제거:
db.ts삭제, search_path 부팅 코드 제거 — system-admin 은 순수 API 클라이언트(DB_* env 불필요). - 라이브 검증: env 계정 401 / OPERATOR 로그인→자동 연결(orgs 162) / 일반회원 403 / 틀린 비번 401 / 실패 시도 후 세션 보존 / probe·해제·재연결 전부 정상. UI 빌드 + DS 게이트(icons·modals·dialogs·spinners) 통과.
- ✅ 최초 OPERATOR 부트스트랩 (2026-08-21 후속): 계정 기반 전환으로 생긴 닭-달걀 공백(부여 UI 가 OPERATOR 로그인 뒤에 있음)을 API 부팅 시 env 부트스트랩으로 해소 —
OPERATOR_BOOTSTRAP_EMAIL/PASSWORD가 있고 OPERATOR 가 전무할 때만 계정 생성(Grafana 패턴, 멱등·재부여 경로 차단, 기존 이메일이면 role 만 부여). meta-account §7 "콘솔 수동 부여"는 2번째 스태프부터의 규칙으로 유지. 라이브: 존재 시 skip / 전무 시 생성→그 계정으로 콘솔 로그인 검증.
Phase 4 — 첫 소비 기능: 법정공제 모니터
design-system-admin-statutory-monitor.md§4를 이 토대 위에 구현(statutoryHealth등). 재설계가 이 기능의 선행조건이었으므로 여기서 접합.
5. 결정 필요 (착수 전)
- 엔드포인트 레지스트리 저장 위치 — system-admin 로컬 소형 DB(신규) vs 설정파일/시크릿 vs 별도 "메타 API". (권고: 소형 로컬 DB — 자격증명·서버버전·중앙 감사 기록을 함께 둘 자리가 필요)
- 엔드포인트별 세션·자격증명 (★ 핵심 — §2.1) — 본인 계정 사용 확정. ⑵⑶ 결정됨(2026-08-21, 구현 완료): ⑵ 1회 입력→암호화 파일 볼트 저장(opt-in —
SYSADMIN_VAULT_KEY32바이트 base64 설정 시; 미설정이면 인메모리). 저장소는 로컬 파일(콘솔이 DB 없는 순수 API 클라이언트가 됐으므로; EKS 는SYSADMIN_VAULT_PATH로 PV 경로). 키는 API 의 FIELD_ENCRYPTION_KEY 와 반드시 별개 — "암호화 키는 API 만 보유" 원칙 유지. 볼트 복원값이 env dev 시드보다 우선. ⑶ 리프레시 토큰은 저장하지 않음 — 자격증명이 있으면 재로그인이 자동이라 토큰 영속은 공격면만 늘림(세션 토큰은 항상 인메모리). ⑴도 결정·구현됨(2026-08-21): Platform Staff 탭에 엔드포인트 축 추가 — 대상 엔드포인트 셀렉터(기본 = 로그인 엔드포인트) +systemCreatePlatformStaff(계정 생성+역할 부여 한 번에, 중복 409, org-less 감사)로 새 엔드포인트에 벤더 스태프 계정을 최초 프로비저닝. env 부트스트랩(OPERATOR 전무 시 1명) → 콘솔 생성(2번째부터) 2단 구조 완성. §5-2 전체 종결. (SSO/단일 자격증명은 realm 독립이라 불가 — 엔드포인트별 세션이 원칙.) 전 org 진입 시 기본 스코프→ 결정됨(2026-08-20): OPERATOR(시스템관리자)는 전 org 전권(고객사 요청 루프 없음), SUPPORT는 발급받은 per-org grant로 스코프. grant 발급 권한은 벤더(OPERATOR) 유지, org 동의는 계약 약관. 남은 미세 결정: 더 세분화된 read-only 계층(예: VIEWER)이 필요한지 여부만 후속.- 중앙 릴리스/감사 기록 — 엔드포인트별 릴리스 이력을 system-admin 중앙에 남길지(“어느 서버에 언제 요율 갔나”), 각 엔드포인트 분산에 맡길지. (모니터 문서 §5-6과 정합)
- 이관 중 이중 운영 — Phase 2 진행 동안 raw-pg 라우트와 API-경유 라우트가 공존한다. 엔드포인트 1개(현행 단일 배포)를 레지스트리의 기본 엔드포인트로 등록해 기존 동작을 깨지 않고 점진 전환.
6. 리스크 / 원칙
- 점진 이관: 축 3개를 한 번에 바꾸지 않는다. Phase 0(API 스코프)·Phase 1(fan-out)은 기존 raw-pg와 병존 가능 → 라우트별로 안전하게 교체.
- 화면 계약 보존:
systemProcedure응답 shape을 기존 라우트 응답에 맞춰, Alpine UI 변경을 마지막으로 미룬다. - 멀티테넌시 예외의 명시성: 전 조직 조회는 의도된 예외 — 반드시 계정 속성(
platformRole)으로 게이트하고 전량 감사. 토큰 위조로 뚫리지 않게 Phase 0에서 못 박는다. - 부분 실패 정상화: 일부 엔드포인트 다운/구버전은 오류가 아니라 상태다. fan-out·UI 모두 3분류(정상/실패/미지원)를 1급 개념으로.