15 KiB
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
로그인 화면의 실제 소스 파일 절대경로는 다음입니다.
/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 브라우저 최초 요청
- 브라우저가 다음 URL을 요청합니다.
GET http://localhost:5000/
- 요청은 Docker host port
5000으로 들어오고baron_gatewayNginx가 받습니다.
관련 파일:
/mnt/e/h_workspace/baron-sso/gateway/nginx.conf
관련 설정:
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;
}
}
/는location /에 매칭되고baron_userfront:5000으로 proxy됩니다.
browser -> localhost:5000 -> baron_gateway -> baron_userfront:5000
2.2 UserFront 정적 bootstrap 응답
- UserFront가 Flutter Web entry HTML을 반환합니다.
관련 파일:
/mnt/e/h_workspace/baron-sso/userfront/web/index.html
index.html은 처음에 bootstrap shell을 보여줍니다. 이 시점에 보이는 것은 실제 Flutter 로그인 화면이 아니라 로딩용 HTML입니다.
주요 DOM:
<main id="baron-bootstrap-shell" aria-live="polite">
<h1>Baron SW Portal</h1>
<div class="loader" aria-hidden="true"></div>
<p>Loading sign-in</p>
<div class="signin-preview" aria-hidden="true">Sign in</div>
</main>
index.html은 로컬 개발 환경에서 기존 service worker와 Flutter cache를 제거한 뒤flutter_bootstrap.js를 로드합니다.
사용 함수/동작:
loadFlutter()navigator.serviceWorker.getRegistrations()registration.unregister()caches.keys()caches.delete()
이후 브라우저는 일반적으로 다음 정적 리소스를 가져옵니다.
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 앱 시작
- Flutter runtime이
main.dart의main()을 실행합니다.
관련 파일:
/mnt/e/h_workspace/baron-sso/userfront/lib/main.dart
주요 함수:
main()WidgetsFlutterBinding.ensureInitialized()usePathUrlStrategy()EasyLocalization.ensureInitialized()LocaleRegistry.primeWithDefaults()LoggerService.init()runApp(...)
runApp()은 다음 wrapper를 구성합니다.
EasyLocalization
-> ProviderScope
-> BaronSSOApp
BaronSSOApp.build()가MaterialApp.router를 생성하고_router를 연결합니다.
주요 함수/객체:
BaronSSOApp_BaronSSOAppState.build()MaterialApp.router(...)routerConfig: _router
3. GoRouter 라우팅 흐름
라우터는 /mnt/e/h_workspace/baron-sso/userfront/lib/main.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})
관련 파일:
/mnt/e/h_workspace/baron-sso/userfront/lib/core/i18n/locale_utils.dart
locale 결정 기준:
- 저장된 locale이 있으면 사용
- 없으면 브라우저/디바이스 locale 사용
- 지원하지 않는 값이면 fallback locale 사용
따라서 한국어 환경에서는 보통 ko, 영어 환경에서는 en이 됩니다.
3.2 세션 상태 확인
라우터와 앱 초기화 코드는 로컬 토큰 또는 쿠키 기반 세션 여부를 봅니다.
사용 함수:
_hasActiveLocalSession()AuthTokenStore.getToken()AuthTokenStore.usesCookie()_shouldRunStartupSessionRecovery(Uri uri)_silentSessionRecovery()AuthProxyService.getMe()AuthProxyService.getSessionStatus()
관련 파일:
/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:
GET /api/v1/user/me
gateway 기준 흐름:
browser -> /api/v1/user/me -> baron_gateway location /api -> baron_backend:3000
3.3 미로그인 상태에서 최종 로그인 route로 이동
미로그인 상태라면 최종적으로 다음 경로로 이동합니다.
/{locale}/signin
예:
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:
http://localhost:5000/ko/signin
라우터 path:
/:locale/signin
관련 코드 구조:
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:
ScopedTheme
-> LoginScreen
로그인 화면 구현 파일:
/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()가 실행됩니다.
초기화 순서:
TabController(length: 3)생성- password/link/QR 입력 controller 준비
drySendquery parameter 확인redirect_uri또는redirect_urlquery parameter 확인login_challenge확인- magic link, short code, verification token이 있는지 확인
- QR 로그인 필요 안내 notice 확인
- OIDC login challenge가 있으면 자동 accept 시도
- tenant-info 조회로 login ID label 커스터마이징
- login challenge가 없으면 쿠키 세션 확인
초기 진입이 단순 http://localhost:5000/인 경우 보통 query parameter가 없으므로 다음 정도가 수행됩니다.
LoginScreen.initState()
-> _resolveLoginChallenge()
-> _attemptOidcAutoAccept() // login_challenge 없으면 바로 return
-> AuthProxyService.getTenantInfo()
-> _tryCookieSession()
초기 화면 렌더링에 영향을 줄 수 있는 backend API:
GET /api/v1/auth/tenant-info
GET /api/v1/user/me
6. 로그인 화면 UI 구성
LoginScreen.build()는 탭 기반 로그인 UI를 구성합니다.
탭:
1. 비밀번호
2. 로그인 링크
3. QR 코드
관련 UI 코드:
TabBarTabBarView- password tab
- link tab
- QR tab
6.1 비밀번호 탭
UI 요소:
- 로그인 ID 입력
- 비밀번호 입력
- 로그인 버튼
주요 key:
password_login_id_input
password_login_password_input
password_login_submit_button
사용 함수:
_handlePasswordLogin()AuthProxyService.loginWithPassword(...)_onLoginSuccess(...)
호출 API:
POST /api/v1/auth/password/login
6.2 로그인 링크 탭
UI 요소:
- 로그인 ID 입력
- 로그인 링크 전송 버튼
- short code 입력 UI
- 재전송 버튼
사용 함수:
_handleLinkLogin()_startEnchantedFlow(...)_pollForSession(...)_verifyShortCode(...)AuthProxyService.initEnchantedLink(...)AuthProxyService.pollEnchantedLink(...)AuthProxyService.verifyLoginShortCode(...)
호출 API:
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:
POST /api/v1/auth/qr/init
POST /api/v1/auth/qr/poll
7. OIDC login_challenge가 있는 경우
외부 RP 또는 Hydra 흐름에서 다음과 같은 URL로 들어올 수 있습니다.
http://localhost:5000/ko/signin?login_challenge=...
이 경우 LoginScreen은 login_challenge를 보관하고 다음을 시도합니다.
_attemptOidcAutoAccept()- 로컬 token 또는 cookie session이 있으면 backend에 login accept 요청
- backend가 반환한
redirectTo로 브라우저 이동
사용 함수:
_resolveLoginChallenge(...)_attemptOidcAutoAccept()_acceptOidcLoginAndRedirect(...)_redirectToOidcTarget(...)AuthProxyService.acceptOidcLogin(...)
호출 API:
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. 짧은 호출 흐름 다이어그램
sequenceDiagram
participant B as Browser
participant G as baron_gateway<br/>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. 디버깅 포인트
로그인 화면이 뜨지 않을 때 먼저 확인할 것:
baron_gateway가 떠 있는지baron_userfront가 healthy인지- gateway 로그에
baron_userfront could not be resolved또는connect() failed가 있는지 - 브라우저 Network 탭에서
/flutter_bootstrap.js,/main.dart.mjs가 200인지 - Flutter route가
/ko/signin또는/en/signin으로 이동하는지 /api/v1/auth/tenant-info,/api/v1/user/me실패가 화면 렌더링을 막는지