Skip to content

chore(web): 로그인 불안정 원인 분석용 인증 디버그 계측 추가#471

Closed
manNomi wants to merge 1 commit intomainfrom
codex/investigate-login-instability
Closed

chore(web): 로그인 불안정 원인 분석용 인증 디버그 계측 추가#471
manNomi wants to merge 1 commit intomainfrom
codex/investigate-login-instability

Conversation

@manNomi
Copy link
Contributor

@manNomi manNomi commented Mar 11, 2026

개요

로그인 불안정(간헐적 로그아웃/401/인증 상태 튕김) 이슈의 원인 분석을 위해 인증 흐름 디버그 계측을 추가했습니다.
기능 로직 변경 없이, 디버그를 켠 경우에만 타임라인 로그를 수집합니다.

변경 사항

  • 인증 디버그 유틸 추가
    • localStorage.setItem("authDebug", "1") 또는 NEXT_PUBLIC_AUTH_DEBUG=true 일 때만 동작
    • 브라우저 메모리 로그 버퍼 window.__AUTH_DEBUG_LOGS__ 제공
  • Axios 인증 인터셉터 계측
    • 요청 시작/토큰 부착/재발급 시작-성공-실패/401 응답/로그인 리다이렉트 로그 추가
  • Auth Store 계측
    • setAccessToken, clearAccessToken, refreshStatus, isLoading, isInitialized 전이 로그 추가
  • Middleware 계측(옵션)
    • 보호 라우트 판정 및 리다이렉트 사유 로그 출력
  • 조사 가이드 문서 추가
    • 재현 매트릭스, 로그 추출 방법, 판독 규칙 정리

주요 파일

  • apps/web/src/utils/authDebug.ts
  • apps/web/src/utils/axiosInstance.ts
  • apps/web/src/lib/zustand/useAuthStore.ts
  • apps/web/src/middleware.ts
  • apps/web/LOGIN_INSTABILITY_INVESTIGATION.md

검증

  • pnpm --filter @solid-connect/web run lint:check
  • pnpm --filter @solid-connect/web run typecheck:ci
  • pre-push hook에서 ci:check + next build 통과

후속 분석 절차

  1. 재현 매트릭스(하드 리프레시/토큰 만료 직후/보호 라우트 진입) 실행
  2. window.__AUTH_DEBUG_LOGS__ + HAR 수집
  3. 원인 카테고리(만료 토큰 처리/리이슈 실패/경합/가드 불일치) 확정

@github-actions github-actions bot added the web label Mar 11, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Walkthrough

이번 변경사항은 로그인 불안정성 문제를 추적하고 디버깅하기 위한 기반 시설을 구축하는 작업입니다:

  1. 로그인 불안정성 조사 가이드 — 리다이렉트, 401 오류, 상태 불일치를 다루는 종합 문서 추가
  2. 인증 저장소 확장useAuthStore에 디버그 로깅과 새로운 상태 관리 메서드 및 속성 추가
  3. 미들웨어 디버그 로깅 — 인증 경로 판단 과정에 조건부 로깅 추가
  4. 디버그 유틸리티 모듈 — 토큰 만료 파싱과 환경 변수 기반 로깅 활성화 기능 제공
  5. Axios 인스턴스 계측 — 토큰 갱신 및 인증 오류 흐름에 디버그 포인트 추가

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • wibaek
  • enunsnv
  • khwww
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 핵심을 정확하게 반영하고 있으며, 인증 디버그 계측 추가라는 주요 목표를 명확하게 전달합니다.
Description check ✅ Passed PR 설명이 템플릿의 필수 섹션(개요, 변경사항, 주요파일, 검증, 후속절차)을 모두 포함하고 있으며, 작업 내용을 기능 단위로 상세하게 설명하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/investigate-login-instability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link

vercel bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
solid-connection-web Ready Ready Preview, Comment Mar 11, 2026 2:33pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
solid-connect-web-admin Skipped Skipped Mar 11, 2026 2:33pm

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/lib/zustand/useAuthStore.ts`:
- Around line 123-131: In onRehydrateStorage, avoid mutating the incoming state
directly; instead update the store via its actions or setState so subscribers
are notified: call the existing action(s) (e.g.,
setUserRole(parseUserRoleFromToken(state.accessToken)) and setInitialized(true))
or invoke store.setState({ userRole: parseUserRoleFromToken(state.accessToken),
isInitialized: true }) rather than assigning to
state.userRole/state.isInitialized directly; keep the authDebugLog call but
perform updates through setUserRole/setInitialized or store.setState to ensure
Zustand notifies subscribers.

In `@apps/web/src/utils/authDebug.ts`:
- Around line 41-43: The in-memory window.__AUTH_DEBUG_LOGS__ is lost on
full-page navigations (e.g., redirectToLogin in axiosInstance.ts); modify
apps/web/src/utils/authDebug.ts to mirror the last N events into sessionStorage
and restore them on module initialization: on every push to
window.__AUTH_DEBUG_LOGS__ write a truncated JSON array (limit N) to a fixed
sessionStorage key (e.g., "__AUTH_DEBUG_LOGS_SESSION__"), and when the module
loads, hydrate window.__AUTH_DEBUG_LOGS__ from that sessionStorage key if
present; ensure serialization/deserialization with try/catch to avoid exceptions
and keep the same push logic so existing callers need no change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 356f9cf0-35d4-4f81-bb55-3721e3286c74

📥 Commits

Reviewing files that changed from the base of the PR and between 8472772 and d8ce315.

📒 Files selected for processing (5)
  • apps/web/LOGIN_INSTABILITY_INVESTIGATION.md
  • apps/web/src/lib/zustand/useAuthStore.ts
  • apps/web/src/middleware.ts
  • apps/web/src/utils/authDebug.ts
  • apps/web/src/utils/axiosInstance.ts

Comment on lines 123 to 131
onRehydrateStorage: () => (state) => {
// hydration 완료 후 isInitialized를 true로 설정
if (state) {
authDebugLog("store.rehydrate.complete", {
hasToken: !!state.accessToken,
isAuthenticated: state.isAuthenticated,
});
state.userRole = parseUserRoleFromToken(state.accessToken);
state.isInitialized = true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Zustand persist middleware, does mutating statedirectly insideonRehydrateStoragenotify subscribers, or shouldsetState / store actions be used instead?

💡 Result:

Directly mutating the state object you receive in onRehydrateStorage will not notify Zustand subscribers. Zustand only notifies subscribers when the store’s setState runs (it updates the internal state reference and then calls all listeners.forEach(...)).[1]

So inside onRehydrateStorage, you should update via store actions (which call set) or call store.setState(...)—not by mutating state in-place.[2][3]

Sources:
[1] Zustand core setState implementation calls listeners only from setState (src/vanilla.ts) (github.com)
[2] Persist middleware describes onRehydrateStorage as a hook for custom logic around rehydration (zustand.docs.pmnd.rs)
[3] Example showing using an action inside onRehydrateStorage works (GitHub issue #830) (github.com)

Citations:


Zustand 하이드레이션 완료 상태를 구독자에게 올바르게 알려주세요.

onRehydrateStorage 콜백 내에서 state 객체를 직접 변경하면 Zustand의 구독자 알림이 작동하지 않습니다. 현재 코드처럼 state.userRolestate.isInitialized를 직접 대입하면, 이 값들을 구독하는 컴포넌트가 하이드레이션 완료를 감지하지 못해 UI가 업데이트되지 않을 수 있습니다.

하이드레이션 로직을 다음 중 하나로 개선해주세요:

  1. store 액션 활용: 기존 액션 함수(예: setUserRole, setInitialized)를 onRehydrateStorage 내에서 호출
  2. setState 경로 사용: store.setState()를 통해 상태 업데이트

이렇게 하면 Zustand가 올바르게 모든 구독자에게 변경을 알릴 수 있고, 로그인 안정성 조사 시 상태 전이를 명확하게 추적할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/zustand/useAuthStore.ts` around lines 123 - 131, In
onRehydrateStorage, avoid mutating the incoming state directly; instead update
the store via its actions or setState so subscribers are notified: call the
existing action(s) (e.g., setUserRole(parseUserRoleFromToken(state.accessToken))
and setInitialized(true)) or invoke store.setState({ userRole:
parseUserRoleFromToken(state.accessToken), isInitialized: true }) rather than
assigning to state.userRole/state.isInitialized directly; keep the authDebugLog
call but perform updates through setUserRole/setInitialized or store.setState to
ensure Zustand notifies subscribers.

Comment on lines +41 to +43
if (typeof window !== "undefined") {
window.__AUTH_DEBUG_LOGS__ = window.__AUTH_DEBUG_LOGS__ ?? [];
window.__AUTH_DEBUG_LOGS__.push(payload);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Do JavaScript globals attached to windowsurvive a full-page navigation triggered bywindow.location.href, or are they reset with the new document?

💡 Result:

They’re reset.

A navigation triggered by window.location.href loads a new document, which gets a fresh Window (global object) and JavaScript realm, so any globals you previously attached (e.g. window.myVar = ...) won’t be there in the new page. What stays “the same” is the stable reference (WindowProxy) that forwards to whichever document’s Window is currently active. (stackoverflow.com)

One important exception: if the user later returns via Back/Forward Cache (bfcache), the browser may restore the prior page from memory and preserve its JS state, including globals—but that’s a page restoration, not a normal full reload. (developer.mozilla.org)

Citations:


/login 리다이렉트 순간에 로그가 사라지는 문제

현재 debug buffer가 window 메모리에만 보관되어 있어서, axiosInstance.tsredirectToLogin처럼 window.location.href로 전체 페이지 네비게이션이 발생하면 __AUTH_DEBUG_LOGS__ 전체가 초기화됩니다. 웹 브라우저에서 새 문서가 로드될 때 새로운 Window 객체와 JavaScript realm이 생기기 때문에, 이전에 부착한 전역 변수들이 모두 리셋되는 것입니다.

이번 PR이 정확히 해결하려는 "갑작스러운 /login 리다이렉트" 시나리오에서 오히려 가장 필요한 로그를 건지지 못하게 되므로, 다음과 같이 개선하면 좋겠습니다:

  1. 마지막 N개 이벤트를 sessionStorage에 미러링해두기
  2. 새 문서 로드 후 복구 로직 추가하기
  3. 리다이렉트 직전의 이벤트 타임라인을 보존할 수 있게 하기

이렇게 하면 /login 페이지에서도 직전의 인증 관련 로그를 확인할 수 있어서, 디버깅이 훨씬 더 효과적이 될 것입니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/utils/authDebug.ts` around lines 41 - 43, The in-memory
window.__AUTH_DEBUG_LOGS__ is lost on full-page navigations (e.g.,
redirectToLogin in axiosInstance.ts); modify apps/web/src/utils/authDebug.ts to
mirror the last N events into sessionStorage and restore them on module
initialization: on every push to window.__AUTH_DEBUG_LOGS__ write a truncated
JSON array (limit N) to a fixed sessionStorage key (e.g.,
"__AUTH_DEBUG_LOGS_SESSION__"), and when the module loads, hydrate
window.__AUTH_DEBUG_LOGS__ from that sessionStorage key if present; ensure
serialization/deserialization with try/catch to avoid exceptions and keep the
same push logic so existing callers need no change.

@manNomi manNomi closed this Mar 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant