forked from baron/baron-sso
77 lines
4.1 KiB
Markdown
77 lines
4.1 KiB
Markdown
# Issue #277/#302 트러블슈팅 기록: 로그인 후 공백 화면 + 새로고침 시 signin 회귀
|
|
|
|
## 기준 시점
|
|
- 2026-02-23 KST
|
|
- 재현 환경: `https://sss.hmac.kr` (WASM 배포)
|
|
|
|
## 증상
|
|
- 로그인 직후 URL은 `/{locale}` 또는 `/{locale}/dashboard`로 보이지만 화면이 렌더링되지 않음
|
|
- 이후 새로고침하면 `/{locale}/signin`으로 되돌아감
|
|
- 콘솔/백엔드 수집 로그:
|
|
- `Null check operator used on a null value`
|
|
- `wasm-function[765]` 포함 스택 반복
|
|
|
|
## 스택 매핑 결과 (source-map + no-strip-wasm)
|
|
- 매핑 커맨드:
|
|
- `python3 scripts/map_wasm_stack.py --wasm userfront/build/web/main.dart.wasm --sourcemap userfront/build/web/main.dart.wasm.map --frame ...`
|
|
- 핵심 프레임:
|
|
- `wasm-function[765]` -> `_TypeError._throwNullCheckErrorWithCurrentStack`
|
|
- 상위 프레임 -> Flutter `NavigatorState.didUpdateWidget/_updatePages` 경로
|
|
- 결론:
|
|
- 단일 위젯 null 접근보다, 라우트 갱신 타이밍/중복 네비게이션 경쟁에서 `Navigator` 내부에서 터지는 양상
|
|
|
|
## 지금까지 시행착오와 실패 내역
|
|
1. `LocaleGate`, `LanguageSelector`의 `EasyLocalization.of(context)` null 방어만 적용
|
|
- 결과: 동일 예외 재발
|
|
- 이유: 루트 원인은 로케일 위젯 단일 null 접근이 아니라 네비게이션 경쟁 구간
|
|
|
|
2. `/ko` 루트에서 signin 강제 리다이렉트만 강화
|
|
- 결과: 최초 진입은 일부 개선됐지만 로그인 직후/새로고침 회귀 지속
|
|
- 이유: 로그인 성공 경로가 루트(`/{locale}`)와 엮이면서 라우트 재평가가 중첩
|
|
|
|
3. 로그인 화면에서 `AuthNotifier.notify()` + `context.go(...)` 동시 수행
|
|
- 결과: 간헐적 경쟁 상태 유발 가능성 확인
|
|
- 조치: 로컬 네비게이션 1회 가드 도입(`_goLocalizedHomeOnce`)
|
|
|
|
4. cookie 세션 승격이 토큰 저장 이후 덮어쓰는 경합
|
|
- 결과: 일부 흐름에서 저장 상태 불안정 가능성
|
|
- 조치: `cookie_session_policy` 추가, 토큰 존재 시 불필요한 cookie 승격 차단
|
|
|
|
5. `/:locale` 엔트리가 redirect 없이 매칭되는 구조
|
|
- 결과: `/ko` 직접 진입 시 페이지 스택 재계산 과정에서 `NavigatorState.didUpdateWidget/_updatePages` 경로 null check 재발
|
|
- 이유: `/ko`는 실질 화면이 아닌 분기 지점인데, 명시적 redirect 경로가 없으면 라우트 갱신 타이밍 경쟁에 취약
|
|
- 조치: `/:locale`를 redirect 전용 엔트리로 확정(비로그인 `/{locale}/signin`, 로그인 `/{locale}/dashboard`)
|
|
|
|
## 최종 반영 방향 (이번 패치)
|
|
1. 로그인 성공 기본 경로를 명시적으로 `/{locale}/dashboard`로 고정
|
|
- `buildLocalizedHomePath()` 반환값을 `/{locale}/dashboard`로 변경
|
|
- `/:locale` 엔트리는 `/:locale/dashboard`로 redirect 전용 처리
|
|
|
|
2. 라우터/화면 역할 분리
|
|
- 보호 경로 검사는 router redirect에서 수행
|
|
- 대시보드는 필요 시 cookie 세션 복구를 1회 시도 후 signin 이동
|
|
|
|
3. 중복 네비게이션 억제
|
|
- 로그인 성공 시 내부 이동은 1회만 수행
|
|
|
|
## 검증
|
|
- 추가 테스트:
|
|
- `userfront/test/login_navigation_race_test.dart`
|
|
- `userfront/test/cookie_session_policy_test.dart`
|
|
- `userfront/test/router_redirect_widget_test.dart` (`/{locale}` 직접 진입 시 signin/dashboard 분기 검증)
|
|
- 갱신 테스트:
|
|
- `userfront/test/locale_utils_test.dart` (home path `/{locale}/dashboard` 기준)
|
|
- 실행:
|
|
- `flutter test`
|
|
- `flutter test --platform chrome test/router_redirect_widget_test.dart test/login_navigation_race_test.dart test/cookie_session_policy_test.dart`
|
|
|
|
## 남은 리스크
|
|
- 실제 브라우저 저장소 정책(localStorage 차단/쿠키 정책)에 따라 세션 판정이 달라질 수 있음
|
|
- 운영 검증 시 네트워크/스토리지 상태를 함께 수집해야 원인 분리 가능
|
|
|
|
## 운영 확인 체크리스트
|
|
1. 비로그인으로 `/{locale}` 접속 시 즉시 `/{locale}/signin` 이동
|
|
2. 로그인 성공 시 `/{locale}/dashboard` 진입
|
|
3. `/{locale}/dashboard`에서 새로고침 후 세션 유지 (동일 브라우저)
|
|
4. 실패 시 `RECOVERY_NAV_NULL_CHECK`와 wasm frame 동시 수집
|