본문으로 건너뛰기

Workspace W2 Rollout — 다중 워크스페이스 멤버십 운영

Epic #1362 종결 후의 실제 운영 절차. 한 사용자가 finance-invest (금융 인텔리전스) + energy-kogas (한국가스공사) 두 워크스페이스에 동시 소속하고 UI 드롭다운에서 자유롭게 전환.

W1 단일 워크스페이스 인프라 (workspace-isolation.md) 의 후속 — DB·FK·Keycloak group sync 는 무변경, JWT 다중 멤버십 + UI 전환 흐름만 추가.

토폴로지

Keycloak realm: gend
├── /tenants/finance-invest ← 기존 /tenants/internal 에서 rename
│ ├── ttagu99@gmail.com
│ ├── analyst-alice@company.com
│ └── ... (기존 사용자 전원 자동 잔류 — group rename 만)

└── /tenants/energy-kogas ← 신규
├── ttagu99@gmail.com ← 동시 멤버십 (W2 핵심)
└── kogas-analyst@company.com

데이터 격리 매트릭스

테이블 그룹finance-invest 사용자energy-kogas 사용자정책
intel_articles, intel_ohlcv_* (Fact)NULL 공유
intel_briefings, intel_risk_reportsfinance 만NOT NULL 격리
onto_* instance, KOGAS dashboardskogas 만NOT NULL 격리
query_history, saved_queries, data_martsfinance 만kogas 만M2 FK 전파

시드 절차

Pre-flight 점검

# 1) AKS prod context 확인
kubectl config use-context aks-genos-prod
kubectl get pods -n gend | grep gend-api

# 2) workspace_id IS NULL row 수 (격리 의도 어긋남 사전 진단)
kubectl exec -n gend deploy/postgresql -- psql -U gend -c "
SELECT 'intel_briefings' AS t, COUNT(*) FROM intel_briefings WHERE workspace_id IS NULL
UNION ALL
SELECT 'query_history', COUNT(*) FROM query_history WHERE workspace_id IS NULL
UNION ALL
SELECT 'saved_queries', COUNT(*) FROM saved_queries WHERE workspace_id IS NULL
UNION ALL
SELECT 'data_marts', COUNT(*) FROM data_marts WHERE workspace_id IS NULL;"

시드 실행

# admin JWT 발급 (Keycloak)
export GEND_ADMIN_TOKEN=$(curl -s -X POST \
https://gend.genon.ai/auth/realms/gend/protocol/openid-connect/token \
-d "grant_type=password" \
-d "client_id=gend-ui" \
-d "username=admin" \
-d "password=$ADMIN_PASS" \
-d "scope=openid" \
| jq -r .access_token)

# 사용자 본인 Keycloak sub 조회
export GEND_W2_ADMIN_USER_ID=$(curl -s \
https://gend.genon.ai/auth/admin/realms/gend/users?username=ttagu99 \
-H "Authorization: Bearer $GEND_ADMIN_TOKEN" \
| jq -r '.[0].id')

# 시드 실행 (idempotent — 여러 번 실행해도 안전)
GEND_API_BASE=https://gend.genon.ai \
python3 scripts/seed_w2_workspaces.py

Post-flight 검증

# 1) Workspace row 2건 (finance-invest + energy-kogas)
kubectl exec -n gend deploy/postgresql -- psql -U gend -c \
"SELECT id, slug, name FROM workspaces;"

# 2) Keycloak group rename + 신규 group 확인
curl https://gend.genon.ai/auth/admin/realms/gend/groups \
-H "Authorization: Bearer $GEND_ADMIN_TOKEN" \
| jq '.[] | select(.name == "tenants") | .subGroups[] | {name, path}'

# 3) JWT 재로그인 후 멤버십 확인
TOKEN=$(curl ... password grant ...)
echo $TOKEN | cut -d. -f2 | base64 -d | jq '.groups, .workspace_slugs'

UI 동작 검증

  1. 로그아웃 → 재로그인 (JWT 갱신 필수)
  2. TopBar WorkspaceSwitcher 드롭다운에 2개 항목 노출 확인
  3. 클릭으로 전환 → toast ... 으로 전환했습니다 → 모든 데이터 화면 자동 재조회
  4. /intel/briefings/dashboards/kogas/lng 두 페이지에서 격리 가시 확인

롤백 절차

internalfinance-invest rename 을 되돌려야 할 경우:

# Step 2 워크플로우로 slug 역방향 변경
FINANCE_ID=$(kubectl exec -n gend deploy/postgresql -- psql -U gend -t -c \
"SELECT id FROM workspaces WHERE slug='finance-invest';" | tr -d ' ')

curl -X PUT https://gend.genon.ai/api/v1/workspaces/$FINANCE_ID \
-H "Authorization: Bearer $GEND_ADMIN_TOKEN" \
-d '{"slug":"internal","name":"사내"}'

UUID 보존이라 FK 영향 0건. energy-kogasstatus=deleted 로 전환:

curl -X PUT https://gend.genon.ai/api/v1/workspaces/$KOGAS_ID \
-d '{"status":"deleted"}' ...

알려진 제약 (W3 에서 해소)

  • 위임 트리 (본사 → 지사 → 협력사) 는 별도 Epic #1149
  • 워크스페이스 별 audit chain 분리 (W3 GA)
  • 워크스페이스 전환 시 토큰 자체 갱신 (현재는 헤더만, Keycloak token-exchange 는 미사용)

관련

  • 선행: W1 가이드, Epic #1018
  • 설계 문서: docs/DESIGN_WORKSPACE_W2_UI.md
  • Sub-issues: #1363, #1364, #1365, #1366, #1367, #1368, #1369