diff --git a/로그인과정.md b/로그인과정.md
new file mode 100644
index 0000000..56eabf8
--- /dev/null
+++ b/로그인과정.md
@@ -0,0 +1,552 @@
+# localhost:5000 로그인 화면 호출 흐름
+
+이 문서는 브라우저에서 `http://localhost:5000/`로 접근했을 때 UserFront 로그인 화면이 뜨기까지의 순차 흐름을 정리합니다.
+
+작성 기준:
+
+- 로컬 URL: `http://localhost:5000/`
+- gateway: `baron_gateway`
+- UserFront upstream: `baron_userfront:5000`
+- 로그인 화면 구현 파일: `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/login_screen.dart`
+
+## 1. 결론
+
+로그인 화면의 대표 URL은 다음입니다.
+
+- 한국어: `http://localhost:5000/ko/signin`
+- 영어: `http://localhost:5000/en/signin`
+
+로그인 화면의 실제 소스 파일 절대경로는 다음입니다.
+
+```text
+/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/login_screen.dart
+```
+
+`http://localhost:5000/`로 접근하면 gateway가 UserFront 정적 앱을 내려주고, Flutter 앱이 기동된 뒤 `GoRouter`가 locale과 세션 상태를 판단하여 미로그인 사용자를 `/{locale}/signin`으로 보냅니다.
+
+## 2. 전체 순차 흐름
+
+### 2.1 브라우저 최초 요청
+
+1. 브라우저가 다음 URL을 요청합니다.
+
+```text
+GET http://localhost:5000/
+```
+
+2. 요청은 Docker host port `5000`으로 들어오고 `baron_gateway` Nginx가 받습니다.
+
+관련 파일:
+
+```text
+/mnt/e/h_workspace/baron-sso/gateway/nginx.conf
+```
+
+관련 설정:
+
+```nginx
+server {
+ listen 5000;
+
+ set $backend_upstream http://baron_backend:3000;
+ set $userfront_upstream http://baron_userfront:5000;
+ set $oathkeeper_upstream http://oathkeeper:4455;
+
+ location / {
+ proxy_pass $userfront_upstream;
+ }
+}
+```
+
+3. `/`는 `location /`에 매칭되고 `baron_userfront:5000`으로 proxy됩니다.
+
+```text
+browser -> localhost:5000 -> baron_gateway -> baron_userfront:5000
+```
+
+### 2.2 UserFront 정적 bootstrap 응답
+
+4. UserFront가 Flutter Web entry HTML을 반환합니다.
+
+관련 파일:
+
+```text
+/mnt/e/h_workspace/baron-sso/userfront/web/index.html
+```
+
+5. `index.html`은 처음에 bootstrap shell을 보여줍니다. 이 시점에 보이는 것은 실제 Flutter 로그인 화면이 아니라 로딩용 HTML입니다.
+
+주요 DOM:
+
+```html
+
+ Baron SW Portal
+
+ Loading sign-in
+ Sign in
+
+```
+
+6. `index.html`은 로컬 개발 환경에서 기존 service worker와 Flutter cache를 제거한 뒤 `flutter_bootstrap.js`를 로드합니다.
+
+사용 함수/동작:
+
+- `loadFlutter()`
+- `navigator.serviceWorker.getRegistrations()`
+- `registration.unregister()`
+- `caches.keys()`
+- `caches.delete()`
+
+이후 브라우저는 일반적으로 다음 정적 리소스를 가져옵니다.
+
+```text
+GET /flutter_bootstrap.js
+GET /manifest.json
+GET /main.dart.mjs
+GET /canvaskit/skwasm.js
+GET /canvaskit/skwasm.wasm
+GET /favicon.ico
+```
+
+### 2.3 Flutter 앱 시작
+
+7. Flutter runtime이 `main.dart`의 `main()`을 실행합니다.
+
+관련 파일:
+
+```text
+/mnt/e/h_workspace/baron-sso/userfront/lib/main.dart
+```
+
+주요 함수:
+
+- `main()`
+- `WidgetsFlutterBinding.ensureInitialized()`
+- `usePathUrlStrategy()`
+- `EasyLocalization.ensureInitialized()`
+- `LocaleRegistry.primeWithDefaults()`
+- `LoggerService.init()`
+- `runApp(...)`
+
+8. `runApp()`은 다음 wrapper를 구성합니다.
+
+```text
+EasyLocalization
+ -> ProviderScope
+ -> BaronSSOApp
+```
+
+9. `BaronSSOApp.build()`가 `MaterialApp.router`를 생성하고 `_router`를 연결합니다.
+
+주요 함수/객체:
+
+- `BaronSSOApp`
+- `_BaronSSOAppState.build()`
+- `MaterialApp.router(...)`
+- `routerConfig: _router`
+
+## 3. GoRouter 라우팅 흐름
+
+라우터는 `/mnt/e/h_workspace/baron-sso/userfront/lib/main.dart`에 정의되어 있습니다.
+
+주요 객체:
+
+```dart
+final _router = GoRouter(
+ initialLocation: '/',
+ refreshListenable: AuthNotifier.instance,
+ routes: [...],
+ redirect: (context, state) { ... },
+);
+```
+
+### 3.1 locale 결정
+
+`http://localhost:5000/`에는 locale path segment가 없습니다. 따라서 앱은 선호 locale을 계산합니다.
+
+사용 함수:
+
+- `extractLocaleFromPath(Uri uri)`
+- `resolvePreferredLocaleCode()`
+- `normalizeLocaleCode(String? code)`
+- `buildLocalizedPath(String localeCode, Uri uri)`
+- `buildLocalizedHomePath(Uri uri, {String? preferredLocaleCode})`
+
+관련 파일:
+
+```text
+/mnt/e/h_workspace/baron-sso/userfront/lib/core/i18n/locale_utils.dart
+```
+
+locale 결정 기준:
+
+1. 저장된 locale이 있으면 사용
+2. 없으면 브라우저/디바이스 locale 사용
+3. 지원하지 않는 값이면 fallback locale 사용
+
+따라서 한국어 환경에서는 보통 `ko`, 영어 환경에서는 `en`이 됩니다.
+
+### 3.2 세션 상태 확인
+
+라우터와 앱 초기화 코드는 로컬 토큰 또는 쿠키 기반 세션 여부를 봅니다.
+
+사용 함수:
+
+- `_hasActiveLocalSession()`
+- `AuthTokenStore.getToken()`
+- `AuthTokenStore.usesCookie()`
+- `_shouldRunStartupSessionRecovery(Uri uri)`
+- `_silentSessionRecovery()`
+- `AuthProxyService.getMe()`
+- `AuthProxyService.getSessionStatus()`
+
+관련 파일:
+
+```text
+/mnt/e/h_workspace/baron-sso/userfront/lib/main.dart
+/mnt/e/h_workspace/baron-sso/userfront/lib/core/services/auth_token_store.dart
+/mnt/e/h_workspace/baron-sso/userfront/lib/core/services/auth_proxy_service.dart
+```
+
+세션 확인 시 호출될 수 있는 backend API:
+
+```text
+GET /api/v1/user/me
+```
+
+gateway 기준 흐름:
+
+```text
+browser -> /api/v1/user/me -> baron_gateway location /api -> baron_backend:3000
+```
+
+### 3.3 미로그인 상태에서 최종 로그인 route로 이동
+
+미로그인 상태라면 최종적으로 다음 경로로 이동합니다.
+
+```text
+/{locale}/signin
+```
+
+예:
+
+```text
+http://localhost:5000/ko/signin
+http://localhost:5000/en/signin
+```
+
+관련 redirect 함수:
+
+- root route redirect: `buildLocalizedHomePath(...)`
+- global router redirect: `redirect: (context, state) { ... }`
+- private route guard: `_redirectPrivateLocaleRoute(...)`
+- locale entry fallback: `LocaleEntryRedirectScreen._redirect()`
+- signin path builder: `buildSigninRedirectPath(...)`
+
+중요 분기:
+
+- locale이 없으면 locale이 붙은 path로 보정합니다.
+- public auth path는 그대로 통과합니다.
+- 로그인하지 않았고 private path 또는 locale root에 있으면 `/{locale}/signin`으로 보냅니다.
+- 로그인되어 있고 locale root에 있으면 `/{locale}/dashboard`로 보냅니다.
+
+## 4. 로그인 화면 route와 화면 생성
+
+로그인 화면 route는 `main.dart`의 `/:locale/signin`입니다.
+
+브라우저 URL:
+
+```text
+http://localhost:5000/ko/signin
+```
+
+라우터 path:
+
+```text
+/:locale/signin
+```
+
+관련 코드 구조:
+
+```dart
+GoRoute(
+ path: 'signin',
+ builder: (context, state) {
+ final loginChallenge = state.uri.queryParameters['login_challenge'];
+ final redirectUrl =
+ state.uri.queryParameters['redirect_uri'] ??
+ state.uri.queryParameters['redirect_url'];
+ return ScopedTheme(
+ controller: ThemeController.auth,
+ child: LoginScreen(
+ key: state.pageKey,
+ loginChallenge: loginChallenge,
+ redirectUrl: redirectUrl,
+ ),
+ );
+ },
+)
+```
+
+생성되는 widget:
+
+```text
+ScopedTheme
+ -> LoginScreen
+```
+
+로그인 화면 구현 파일:
+
+```text
+/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/login_screen.dart
+```
+
+주요 class/function:
+
+- `LoginScreen`
+- `_LoginScreenState`
+- `_LoginScreenState.initState()`
+- `_LoginScreenState.build()`
+- `_handlePasswordLogin()`
+- `_handleLinkLogin()`
+- `_startEnchantedFlow()`
+- `_pollForSession()`
+- `_startQrFlow()`
+- `_startQrPolling()`
+- `_tryCookieSession()`
+- `_attemptOidcAutoAccept()`
+- `_acceptOidcLoginAndRedirect()`
+- `_onLoginSuccess()`
+
+## 5. LoginScreen 초기화 흐름
+
+`LoginScreen`이 생성되면 `_LoginScreenState.initState()`가 실행됩니다.
+
+초기화 순서:
+
+1. `TabController(length: 3)` 생성
+2. password/link/QR 입력 controller 준비
+3. `drySend` query parameter 확인
+4. `redirect_uri` 또는 `redirect_url` query parameter 확인
+5. `login_challenge` 확인
+6. magic link, short code, verification token이 있는지 확인
+7. QR 로그인 필요 안내 notice 확인
+8. OIDC login challenge가 있으면 자동 accept 시도
+9. tenant-info 조회로 login ID label 커스터마이징
+10. login challenge가 없으면 쿠키 세션 확인
+
+초기 진입이 단순 `http://localhost:5000/`인 경우 보통 query parameter가 없으므로 다음 정도가 수행됩니다.
+
+```text
+LoginScreen.initState()
+ -> _resolveLoginChallenge()
+ -> _attemptOidcAutoAccept() // login_challenge 없으면 바로 return
+ -> AuthProxyService.getTenantInfo()
+ -> _tryCookieSession()
+```
+
+초기 화면 렌더링에 영향을 줄 수 있는 backend API:
+
+```text
+GET /api/v1/auth/tenant-info
+GET /api/v1/user/me
+```
+
+## 6. 로그인 화면 UI 구성
+
+`LoginScreen.build()`는 탭 기반 로그인 UI를 구성합니다.
+
+탭:
+
+```text
+1. 비밀번호
+2. 로그인 링크
+3. QR 코드
+```
+
+관련 UI 코드:
+
+- `TabBar`
+- `TabBarView`
+- password tab
+- link tab
+- QR tab
+
+### 6.1 비밀번호 탭
+
+UI 요소:
+
+- 로그인 ID 입력
+- 비밀번호 입력
+- 로그인 버튼
+
+주요 key:
+
+```text
+password_login_id_input
+password_login_password_input
+password_login_submit_button
+```
+
+사용 함수:
+
+- `_handlePasswordLogin()`
+- `AuthProxyService.loginWithPassword(...)`
+- `_onLoginSuccess(...)`
+
+호출 API:
+
+```text
+POST /api/v1/auth/password/login
+```
+
+### 6.2 로그인 링크 탭
+
+UI 요소:
+
+- 로그인 ID 입력
+- 로그인 링크 전송 버튼
+- short code 입력 UI
+- 재전송 버튼
+
+사용 함수:
+
+- `_handleLinkLogin()`
+- `_startEnchantedFlow(...)`
+- `_pollForSession(...)`
+- `_verifyShortCode(...)`
+- `AuthProxyService.initEnchantedLink(...)`
+- `AuthProxyService.pollEnchantedLink(...)`
+- `AuthProxyService.verifyLoginShortCode(...)`
+
+호출 API:
+
+```text
+POST /api/v1/auth/enchanted-link/init
+POST /api/v1/auth/enchanted-link/poll
+POST /api/v1/auth/login/code/verify-short
+```
+
+### 6.3 QR 코드 탭
+
+QR 탭은 선택될 때 QR flow를 시작합니다.
+
+사용 함수:
+
+- `_handleTabSelection()`
+- `_startQrFlow()`
+- `_startQrPolling()`
+- `_startCountdown()`
+- `AuthProxyService.initQrLogin()`
+- `AuthProxyService.pollQrStatus(...)`
+
+호출 API:
+
+```text
+POST /api/v1/auth/qr/init
+POST /api/v1/auth/qr/poll
+```
+
+## 7. OIDC login_challenge가 있는 경우
+
+외부 RP 또는 Hydra 흐름에서 다음과 같은 URL로 들어올 수 있습니다.
+
+```text
+http://localhost:5000/ko/signin?login_challenge=...
+```
+
+이 경우 `LoginScreen`은 `login_challenge`를 보관하고 다음을 시도합니다.
+
+1. `_attemptOidcAutoAccept()`
+2. 로컬 token 또는 cookie session이 있으면 backend에 login accept 요청
+3. backend가 반환한 `redirectTo`로 브라우저 이동
+
+사용 함수:
+
+- `_resolveLoginChallenge(...)`
+- `_attemptOidcAutoAccept()`
+- `_acceptOidcLoginAndRedirect(...)`
+- `_redirectToOidcTarget(...)`
+- `AuthProxyService.acceptOidcLogin(...)`
+
+호출 API:
+
+```text
+POST /api/v1/auth/oidc/login/accept
+```
+
+로그인 화면에서 사용자가 비밀번호 로그인을 완료한 뒤에도 `_onLoginSuccess()`가 `login_challenge`를 감지하면 같은 accept 흐름을 수행합니다.
+
+## 8. 페이지/route 목록
+
+UserFront auth 관련 route는 locale 아래에 붙습니다.
+
+| 브라우저 절대 URL 예시 | GoRouter path | 생성 화면 | 구현 파일 |
+| --- | --- | --- | --- |
+| `http://localhost:5000/ko/signin` | `/:locale/signin` | `LoginScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/login_screen.dart` |
+| `http://localhost:5000/ko/login` | `/:locale/login` | `LoginScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/login_screen.dart` |
+| `http://localhost:5000/ko/signup` | `/:locale/signup` | `SignupScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/signup_screen.dart` |
+| `http://localhost:5000/ko/registration` | `/:locale/registration` | `SignupScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/signup_screen.dart` |
+| `http://localhost:5000/ko/consent?consent_challenge=...` | `/:locale/consent` | `ConsentScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/consent_screen.dart` |
+| `http://localhost:5000/ko/forgot-password` | `/:locale/forgot-password` | `ForgotPasswordScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/forgot_password_screen.dart` |
+| `http://localhost:5000/ko/recovery` | `/:locale/recovery` | `ForgotPasswordScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/forgot_password_screen.dart` |
+| `http://localhost:5000/ko/reset-password` | `/:locale/reset-password` | `ResetPasswordScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/reset_password_screen.dart` |
+| `http://localhost:5000/ko/error` | `/:locale/error` | `ErrorScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/error_screen.dart` |
+| `http://localhost:5000/ko/dashboard` | `/:locale/dashboard` | `DashboardScreen` | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/dashboard/presentation/dashboard_screen.dart` |
+
+## 9. 가장 중요한 파일 요약
+
+| 목적 | 파일 절대경로 |
+| --- | --- |
+| gateway proxy 설정 | `/mnt/e/h_workspace/baron-sso/gateway/nginx.conf` |
+| Flutter Web HTML bootstrap | `/mnt/e/h_workspace/baron-sso/userfront/web/index.html` |
+| 앱 시작점과 GoRouter 정의 | `/mnt/e/h_workspace/baron-sso/userfront/lib/main.dart` |
+| 로그인 화면 | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/presentation/login_screen.dart` |
+| 로그인/API proxy client | `/mnt/e/h_workspace/baron-sso/userfront/lib/core/services/auth_proxy_service.dart` |
+| token/cookie session store adapter | `/mnt/e/h_workspace/baron-sso/userfront/lib/core/services/auth_token_store.dart` |
+| locale path helper | `/mnt/e/h_workspace/baron-sso/userfront/lib/core/i18n/locale_utils.dart` |
+| public auth path 판정 | `/mnt/e/h_workspace/baron-sso/userfront/lib/features/auth/domain/login_link_route_policy.dart` |
+
+## 10. 짧은 호출 흐름 다이어그램
+
+```mermaid
+sequenceDiagram
+ participant B as Browser
+ participant G as baron_gateway
localhost:5000
+ participant UF as baron_userfront:5000
+ participant FE as Flutter UserFront
+ participant BE as baron_backend:3000
+
+ B->>G: GET /
+ G->>UF: proxy GET /
+ UF-->>G: index.html
+ G-->>B: index.html
+ B->>G: GET /flutter_bootstrap.js
+ G->>UF: proxy static asset
+ B->>G: GET /main.dart.mjs 등
+ G->>UF: proxy static assets
+ FE->>FE: main()
+ FE->>FE: MaterialApp.router(_router)
+ FE->>FE: locale 결정
+ FE->>FE: local token/cookie 상태 확인
+ alt 미로그인
+ FE->>FE: route -> /{locale}/signin
+ FE->>BE: GET /api/v1/auth/tenant-info
+ FE->>BE: GET /api/v1/user/me (cookie/session check 가능)
+ FE->>B: LoginScreen 렌더링
+ else 로그인됨
+ FE->>FE: route -> /{locale}/dashboard
+ end
+```
+
+## 11. 디버깅 포인트
+
+로그인 화면이 뜨지 않을 때 먼저 확인할 것:
+
+1. `baron_gateway`가 떠 있는지
+2. `baron_userfront`가 healthy인지
+3. gateway 로그에 `baron_userfront could not be resolved` 또는 `connect() failed`가 있는지
+4. 브라우저 Network 탭에서 `/flutter_bootstrap.js`, `/main.dart.mjs`가 200인지
+5. Flutter route가 `/ko/signin` 또는 `/en/signin`으로 이동하는지
+6. `/api/v1/auth/tenant-info`, `/api/v1/user/me` 실패가 화면 렌더링을 막는지
+