From 8b8c7a423ea4cb74546b994ff11ffc1bfffb9d4b Mon Sep 17 00:00:00 2001 From: shuim Date: Tue, 22 Sep 2026 11:16:30 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=84=B8=EC=85=98=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=20=ED=8F=AC=ED=8A=B8=ED=8F=B4=EB=A6=AC?= =?UTF-8?q?=EC=98=A4=20=ED=99=94=EB=A9=B4=20=EA=B0=B1=EC=8B=A0=20=EC=95=88?= =?UTF-8?q?=20=EB=90=A8=20=E2=80=94=20MULTI=5FTENANT=5FENABLED=3Dfalse?= =?UTF-8?q?=EB=A9=B4=20v1=20API=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 세션 로그인 사용자는 프론트가 v2(/portfolio/v2)를 쓰는데, 멀티테넌트가 꺼져 있으면 v2 컨텍스트가 legacy로 떨어져 KIS 동기화 없이 DB 스냅숏만 반환 → 현재가·손익이 갱신되지 않음. /auth/me 에 multi_tenant_enabled 를 추가하고, 프론트는 세션 로그인 AND 플래그 true 일 때만 v2 사용. Co-Authored-By: Claude Opus 5 (1M context) --- backend/app/api/endpoints/auth.py | 8 ++++++-- frontend/src/lib/tenant.ts | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/backend/app/api/endpoints/auth.py b/backend/app/api/endpoints/auth.py index a32db4e..7888fef 100644 --- a/backend/app/api/endpoints/auth.py +++ b/backend/app/api/endpoints/auth.py @@ -257,12 +257,16 @@ async def login(body: LoginRequest, request: Request, db: AsyncSession = Depends @router.get("/me") async def me(credential: str = Depends(require_api_key), db: AsyncSession = Depends(get_db)): """현재 자격 증명 확인 — 세션 토큰이면 계정 정보, API 키면 'api-key'(관리자).""" + # multi_tenant_enabled: 프론트가 v2(사용자 스코프) API를 쓸지 결정하는 근거. + # False면 세션 로그인이라도 v2 컨텍스트가 legacy로 떨어져 /portfolio/v2 는 + # KIS 동기화 없는 DB 스냅숏만 반환하므로(화면 갱신 안 됨) v1을 써야 한다. + _mt = bool(settings.MULTI_TENANT_ENABLED) if hmac.compare_digest(credential.encode(), _get_api_key().encode()): - return {"username": "api-key", "auth_type": "api_key", "is_admin": True} + return {"username": "api-key", "auth_type": "api_key", "is_admin": True, "multi_tenant_enabled": _mt} user = await _resolve_user(credential, db) if user is None or not user.is_active: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="세션이 유효하지 않습니다.") - return {"username": user.username, "auth_type": "session", "is_admin": user.is_admin} + return {"username": user.username, "auth_type": "session", "is_admin": user.is_admin, "multi_tenant_enabled": _mt} @router.get("/users", response_model=list[UserOut], dependencies=[Depends(require_admin)]) diff --git a/frontend/src/lib/tenant.ts b/frontend/src/lib/tenant.ts index 9a92309..cf3ee32 100644 --- a/frontend/src/lib/tenant.ts +++ b/frontend/src/lib/tenant.ts @@ -5,7 +5,7 @@ * - `GET /auth/me`로 호출 주체(auth_type)를 확인해 포트폴리오·거래이력·매수/매도가 * 사용자 스코프 v2 API(`/portfolio/v2`, `/trades/v2`)를 쓸지, 기존 v1 API를 * 쓸지 결정한다. - * - auth_type === 'session' (로그인 사용자) → v2 + * - auth_type === 'session' (로그인 사용자) AND 백엔드 multi_tenant_enabled=true → v2 * - auth_type === 'api_key' (레거시 운영자 API 키, MULTI_TENANT_ENABLED=false 환경 포함) * → legacy(v1). 백엔드도 이 경우 `/portfolio/v2/sync`를 스킵 응답하므로 v1 그대로 사용해야 한다. * @@ -24,6 +24,8 @@ export interface AuthMe { username: string auth_type: 'session' | 'api_key' is_admin: boolean + /** 백엔드 MULTI_TENANT_ENABLED. 구버전 백엔드는 필드가 없음(undefined) → legacy 취급 */ + multi_tenant_enabled?: boolean } /** 'v2' | 'legacy' 확정 전(로딩 중·조회 실패)에는 undefined */ @@ -46,7 +48,10 @@ export const useTenantMode = () => { const mode: TenantMode = isLoading || error || !me ? undefined - : me.auth_type === 'session' ? 'v2' : 'legacy' + // 세션 로그인이어도 백엔드 멀티테넌트가 꺼져 있으면 v2 컨텍스트는 legacy로 + // 떨어져 /portfolio/v2 가 KIS 동기화 없는 DB 스냅숏만 돌려준다(화면 갱신 안 됨). + // → MULTI_TENANT_ENABLED=true 인 경우에만 v2 사용. + : me.auth_type === 'session' && me.multi_tenant_enabled === true ? 'v2' : 'legacy' return { mode, me, isLoading, error } }