-
Notifications
You must be signed in to change notification settings - Fork 0
인증 로직 수정 #257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
인증 로직 수정 #257
Conversation
Walkthrough앱 인증이 쿠키/주기검증 방식에서 부트스트랩 기반 tri-state( Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App as App (루트)
participant AuthProv as AuthProvider (클라이언트)
participant Store as useAuthStore
participant API as validateSession()
participant Server as Backend
App->>AuthProv: 마운트
AuthProv->>Store: bootstrap()
activate Store
Store->>API: validateSession() 호출 (GET /users/me/games?limit=1)
activate API
API->>Server: HTTP GET
alt 200 OK
Server-->>API: 200 OK
API-->>Store: 성공 응답
Store->>Store: authStatus = "authenticated"
else 401 Unauthorized
Server-->>API: 401
API-->>Store: 실패 응답
Store->>Store: authStatus = "unauthenticated"
Store->>Window: dispatchEvent("auth:session-expired")
else 기타 오류
Server-->>API: 에러
API-->>Store: 에러
Store->>Store: authStatus = "unknown"
end
deactivate API
Store-->>AuthProv: bootstrap 완료
deactivate Store
AuthProv->>App: children 렌더
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 분 Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✨ Finishing touches
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. Comment |
|
❌ 테스트 실패 테스트가 실패했습니다. Actions 로그를 확인해주세요. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (3)
service/app/src/mocks/handlers/auth.ts (1)
10-23: 프로덕션 보안 속성을 반영하여 쿠키 보안을 강화하세요.로그인 응답의
Set-Cookie헤더에HttpOnly와Secure플래그가 누락되었습니다. 이 파일은 MSW 목 핸들러이지만, 프로덕션 환경과 동일한 쿠키 속성을 사용하는 것이 테스트 정확도를 높이고 보안 문제를 조기에 발견하는 데 도움이 됩니다.
HttpOnly: JavaScript를 통한 쿠키 접근을 차단하여 XSS 공격을 방어합니다Secure: HTTPS 연결에서만 쿠키가 전송되도록 보장합니다🔎 제안된 수정
headers: { "Content-Type": "application/json", - "Set-Cookie": "JSESSIONID=test-session-123; Path=/; SameSite=Lax", + "Set-Cookie": "JSESSIONID=test-session-123; Path=/; HttpOnly; Secure; SameSite=Lax", },service/app/src/entities/auth/api/validateSession.ts (1)
3-6: 임시 엔드포인트를 사용한 세션 검증 방식을 추적해주세요.게임 목록 엔드포인트를 세션 검증용으로 사용하는 것은 간접적이며 취약합니다. 엔드포인트 동작이 변경되면 인증 플로우가 예기치 않게 중단될 수 있습니다. TODO 주석에 명시되어 있지만, 전용 세션 검증 엔드포인트(예:
GET /users/me/session) 생성을 우선순위화하여 추적하는 것을 권장합니다.전용 세션 검증 엔드포인트가 생성되면 다음과 같이 업데이트하세요:
export const validateSession = async () => { const response = await fetchClient.get<{ isValid: boolean }>("users/me/session") return response.json() }이 TODO가 이슈 트래커에 등록되어 있는지 확인해주세요. 등록되어 있지 않다면 이슈를 생성하는 것을 도와드릴까요?
service/app/src/entities/auth/store/useAuthStore.ts (1)
36-43: 에러 처리 개선 권장로그인 실패 시 원본 에러 정보가 손실됩니다. 디버깅과 에러 추적을 위해 원본 에러를 보존하거나 더 구체적인 컨텍스트를 제공하는 것이 좋습니다.
🔎 제안하는 개선 사항
login: async (code: string) => { try { const { profileImageUrl, nickname } = (await kakaoLogin(code)).data set({ user: { profileImageUrl, nickname }, authStatus: "authenticated" }) } catch (error) { - throw new Error("login failed") + throw error instanceof Error ? error : new Error("카카오 로그인에 실패했습니다") } },또는 에러를 로깅하고 원본을 보존:
login: async (code: string) => { try { const { profileImageUrl, nickname } = (await kakaoLogin(code)).data set({ user: { profileImageUrl, nickname }, authStatus: "authenticated" }) } catch (error) { + console.error("Kakao login failed:", error) - throw new Error("login failed") + throw error } },
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
service/app/public/avatar.svgis excluded by!**/*.svg
📒 Files selected for processing (15)
service/app/src/app/__tests__/helpers/auth.setup.tsservice/app/src/app/__tests__/homePOM.tsservice/app/src/app/providers.tsxservice/app/src/entities/auth/api/validateSession.tsservice/app/src/entities/auth/model/hooks/useAuthGuard.tsservice/app/src/entities/auth/store/useAuthStore.tsservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/entities/auth/utils/__tests__/sessionValidator.test.tsservice/app/src/entities/auth/utils/cookieUtils.tsservice/app/src/entities/auth/utils/sessionValidator.tsservice/app/src/mocks/handlers/auth.tsservice/app/src/shared/api/fetchClient.tsservice/app/src/shared/authButton.tsxservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/components/kakaoLoginButton.tsx
💤 Files with no reviewable changes (4)
- service/app/src/entities/auth/utils/sessionValidator.ts
- service/app/src/entities/auth/utils/cookieUtils.ts
- service/app/src/entities/auth/utils/tests/sessionValidator.test.ts
- service/app/src/shared/api/fetchClient.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
All file names must be camelCase (not PascalCase or kebab-case)
Files:
service/app/src/app/__tests__/homePOM.tsservice/app/src/app/__tests__/helpers/auth.setup.tsservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/entities/auth/api/validateSession.tsservice/app/src/entities/auth/store/useAuthStore.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/providers.tsxservice/app/src/entities/auth/model/hooks/useAuthGuard.tsservice/app/src/shared/authButton.tsxservice/app/src/mocks/handlers/auth.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Component names must use PascalCase in the code (while file names use camelCase)
Use Zustand for client-side state management
Use TanStack Query for server state and API caching
Use async/await for promises instead of .then() chains
Use custom fetch client with interceptors from shared/lib/fetchClient.ts for API requests
**/*.{ts,tsx}: Use TypeScript with ES modules; keep files lower-case kebab or camel per existing folder conventions (e.g.,gameLibraryGrid.tsx)
Favor explicit domain names (e.g.,GameCardOptions,useInfiniteMyGames) and colocate UI, API, and hooks within eachentities/<domain>module
Run ESLint using lint scripts; linting is enforced via lint-staged
Files:
service/app/src/app/__tests__/homePOM.tsservice/app/src/app/__tests__/helpers/auth.setup.tsservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/entities/auth/api/validateSession.tsservice/app/src/entities/auth/store/useAuthStore.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/providers.tsxservice/app/src/entities/auth/model/hooks/useAuthGuard.tsservice/app/src/shared/authButton.tsxservice/app/src/mocks/handlers/auth.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use shared ESLint configuration across all workspaces
Files:
service/app/src/app/__tests__/homePOM.tsservice/app/src/app/__tests__/helpers/auth.setup.tsservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/entities/auth/api/validateSession.tsservice/app/src/entities/auth/store/useAuthStore.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/providers.tsxservice/app/src/entities/auth/model/hooks/useAuthGuard.tsservice/app/src/shared/authButton.tsxservice/app/src/mocks/handlers/auth.ts
**/*.{js,ts,jsx,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Prettier with Tailwind CSS plugin for formatting
Files:
service/app/src/app/__tests__/homePOM.tsservice/app/src/app/__tests__/helpers/auth.setup.tsservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/entities/auth/api/validateSession.tsservice/app/src/entities/auth/store/useAuthStore.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/providers.tsxservice/app/src/entities/auth/model/hooks/useAuthGuard.tsservice/app/src/shared/authButton.tsxservice/app/src/mocks/handlers/auth.ts
**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use 2-space indentation throughout the codebase
Files:
service/app/src/app/__tests__/homePOM.tsservice/app/src/app/__tests__/helpers/auth.setup.tsservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/entities/auth/api/validateSession.tsservice/app/src/entities/auth/store/useAuthStore.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/providers.tsxservice/app/src/entities/auth/model/hooks/useAuthGuard.tsservice/app/src/shared/authButton.tsxservice/app/src/mocks/handlers/auth.ts
**/*.{ts,tsx,css,json,md}
📄 CodeRabbit inference engine (AGENTS.md)
Apply Prettier formatting using
yarn workspace <pkg> format; this is enforced via lint-staged
Files:
service/app/src/app/__tests__/homePOM.tsservice/app/src/app/__tests__/helpers/auth.setup.tsservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/entities/auth/api/validateSession.tsservice/app/src/entities/auth/store/useAuthStore.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/providers.tsxservice/app/src/entities/auth/model/hooks/useAuthGuard.tsservice/app/src/shared/authButton.tsxservice/app/src/mocks/handlers/auth.ts
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Tailwind CSS utilities should be ordered via the Prettier Tailwind plugin; avoid inline style objects unless necessary
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/entities/auth/ui/authProvider.tsxservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/providers.tsxservice/app/src/shared/authButton.tsx
**/src/entities/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
In the Next.js app workspace, place feature logic under
src/entitiesand colocate Vitest specs in feature folders
Files:
service/app/src/entities/auth/ui/authProvider.tsx
**/src/app/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
In the Next.js app workspace, UI routes live in
src/app
Files:
service/app/src/app/providers.tsx
🧠 Learnings (6)
📚 Learning: 2025-11-27T13:45:59.426Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T13:45:59.426Z
Learning: Applies to **/__tests__/**/*.spec.tsx : Use Playwright for E2E testing
Applied to files:
service/app/src/app/__tests__/homePOM.tsservice/app/src/app/__tests__/helpers/auth.setup.ts
📚 Learning: 2025-11-27T13:45:59.426Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T13:45:59.426Z
Learning: Applies to **/*.test.ts : Use Vitest for unit testing with browser mode using Playwright
Applied to files:
service/app/src/app/__tests__/helpers/auth.setup.ts
📚 Learning: 2025-11-27T13:46:12.950Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-27T13:46:12.950Z
Learning: Applies to **/src/app/__tests__/**/*.{spec,test}.{ts,tsx} : In the Next.js app workspace, co-locate Playwright specs in `src/app/__tests__`
Applied to files:
service/app/src/app/__tests__/helpers/auth.setup.ts
📚 Learning: 2025-11-27T13:45:59.426Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T13:45:59.426Z
Learning: Applies to **/__tests__/**/*.spec.tsx : E2E tests must use __tests__/**/*.spec.tsx file naming convention
Applied to files:
service/app/src/app/__tests__/helpers/auth.setup.ts
📚 Learning: 2025-11-27T13:45:59.426Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T13:45:59.426Z
Learning: Applies to entities/*/api/**/*.{ts,tsx} : Organize API functions by entity in entities/*/api/
Applied to files:
service/app/src/entities/auth/api/validateSession.ts
📚 Learning: 2025-11-27T13:45:59.426Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T13:45:59.426Z
Learning: Applies to **/*.{ts,tsx} : Use Zustand for client-side state management
Applied to files:
service/app/src/entities/auth/store/useAuthStore.ts
🧬 Code graph analysis (8)
service/app/src/widgets/components/HomeNavigationRightContent.tsx (5)
service/app/src/entities/auth/store/useAuthStore.ts (1)
useAuthStore(20-51)shared/design/src/components/button/primaryBoxButton.tsx (1)
PrimaryBoxButton(53-66)service/app/src/widgets/components/avatarButton.tsx (1)
AvatarButton(15-50)service/app/src/entities/auth/api/logout.ts (1)
logout(3-6)service/app/src/widgets/components/kakaoLoginButton.tsx (1)
KakaoLoginButton(9-24)
service/app/src/entities/auth/ui/authProvider.tsx (1)
service/app/src/entities/auth/store/useAuthStore.ts (1)
useAuthStore(20-51)
service/app/src/entities/auth/api/validateSession.ts (2)
service/app/public/mockServiceWorker.js (1)
response(128-128)service/app/src/shared/api/fetchClient.ts (1)
fetchClient(58-73)
service/app/src/entities/auth/store/useAuthStore.ts (3)
service/app/src/entities/auth/api/validateSession.ts (1)
validateSession(3-6)service/app/src/entities/auth/api/kakaoLogin.ts (1)
kakaoLogin(5-11)service/app/src/entities/auth/api/logout.ts (1)
logout(3-6)
service/app/src/widgets/components/kakaoLoginButton.tsx (1)
shared/design/src/components/button/secondaryOutlineBoxButton.tsx (1)
SecondaryOutlineBoxButton(32-45)
service/app/src/app/providers.tsx (1)
service/app/src/entities/auth/ui/authProvider.tsx (1)
AuthProvider(7-21)
service/app/src/entities/auth/model/hooks/useAuthGuard.ts (1)
service/app/src/entities/auth/store/useAuthStore.ts (1)
useAuthStore(20-51)
service/app/src/shared/authButton.tsx (3)
service/app/src/entities/auth/store/useAuthStore.ts (1)
useAuthStore(20-51)service/app/src/widgets/components/avatarButton.tsx (1)
AvatarButton(15-50)service/app/src/widgets/components/kakaoLoginButton.tsx (1)
KakaoLoginButton(9-24)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci
🔇 Additional comments (7)
service/app/src/mocks/handlers/auth.ts (1)
24-38: 로그아웃 시 쿠키 삭제 로직이 올바릅니다!
Set-Cookie헤더를 추가하여JSESSIONID쿠키를 삭제하는 로직이 정확합니다.Max-Age=0을 사용한 쿠키 삭제는 표준 방식이며,Path와SameSite속성이 원본 쿠키와 일치하여 올바르게 동작합니다.service/app/src/widgets/components/kakaoLoginButton.tsx (1)
9-23: LGTM!
onClick핸들러를 제거하고 Next.jsLink를 사용한 선언적 네비게이션으로 전환한 것이 적절합니다.asChild패턴을 활용하여 버튼 스타일을 유지하면서 링크 동작을 구현했습니다.service/app/src/app/__tests__/helpers/auth.setup.ts (1)
17-17: LGTM!UI 컴포넌트 변경사항에 맞춰 셀렉터를 버튼에서 링크로 올바르게 업데이트했습니다.
service/app/src/app/providers.tsx (1)
27-29: LGTM!
AuthProvider를 적절한 위치에 추가하여 인증 컨텍스트를 제공하고 있습니다. MSW 프로바이더 이후에 배치되어 목 데이터를 활용할 수 있으며, 앱 렌더링 전에 인증 상태를 초기화합니다.service/app/src/entities/auth/ui/authProvider.tsx (1)
7-21: LGTM!인증 프로바이더 구현이 올바릅니다:
- 마운트 시
bootstrap()을 호출하여 세션 검증auth:session-expired커스텀 이벤트 리스너 등록 및 정리- Zustand 스토어 메서드는 안정적인 참조를 가지므로 의존성 배열이 적절함
service/app/src/shared/authButton.tsx (1)
11-31: LGTM!
isAuthenticated불리언에서authStatus삼항 상태로의 마이그레이션이 올바르게 적용되었습니다.KakaoLoginButton이 이제Link기반 네비게이션을 사용하므로onClick없이 렌더링되는 것이 적절합니다.service/app/src/app/__tests__/homePOM.ts (1)
42-51: LGTM!UI 컴포넌트가 버튼에서 링크로 변경됨에 따라 테스트 셀렉터를 올바르게 업데이트했습니다. 모든 인터랙션 메서드는 그대로 유지되어 테스트 안정성이 보장됩니다.
| useEffect(() => { | ||
| if (!_hasHydrated) return | ||
|
|
||
| if (!isAuthenticated) { | ||
| if (authStatus === "unknown") return | ||
| if (authStatus !== "authenticated") { | ||
| alert("로그인이 필요합니다.") | ||
| router.replace("/") | ||
| } | ||
| }, [isAuthenticated, _hasHydrated, router]) | ||
| }, [authStatus, router]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find and read the useAuthGuard implementation
find . -path "*/entities/auth/model/hooks/useAuthGuard.ts" -type f | head -5Repository: JECT-Study/JECT-5-FE
Length of output: 123
🏁 Script executed:
# Also check the structure of the auth module
fd -e ts -e tsx . service/app/src/entities/auth --type f | head -20Repository: JECT-Study/JECT-5-FE
Length of output: 502
🏁 Script executed:
# Read the useAuthGuard implementation
cat -n service/app/src/entities/auth/model/hooks/useAuthGuard.tsRepository: JECT-Study/JECT-5-FE
Length of output: 615
🏁 Script executed:
# Read the useAuthStore to understand the auth state structure
cat -n service/app/src/entities/auth/store/useAuthStore.tsRepository: JECT-Study/JECT-5-FE
Length of output: 1814
🏁 Script executed:
# Read the authProvider to see how auth state is initialized
cat -n service/app/src/entities/auth/ui/authProvider.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 755
🏁 Script executed:
# Find all files that import useAuthGuard
rg -n "useAuthGuard" --type=ts --type=tsx -g '!*.test.*' -g '!*.spec.*'Repository: JECT-Study/JECT-5-FE
Length of output: 91
🏁 Script executed:
# Also check if there's a protected route layout or wrapper
fd -e tsx -e ts . service/app/src --type f | xargs rg -l "useAuthGuard|authStatus.*unknown" | head -20Repository: JECT-Study/JECT-5-FE
Length of output: 248
🏁 Script executed:
# Check how useAuthGuard is used in dashboard and create pages
rg -n "useAuthGuard|authStatus" service/app/src/app/dashboard/page.tsx -A 10 -B 5Repository: JECT-Study/JECT-5-FE
Length of output: 564
🏁 Script executed:
# Check the create page as well
rg -n "useAuthGuard|authStatus" service/app/src/app/create/page.tsx -A 10 -B 5Repository: JECT-Study/JECT-5-FE
Length of output: 877
🏁 Script executed:
# Let's read the full dashboard page to understand the structure
cat -n service/app/src/app/dashboard/page.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 641
🏁 Script executed:
# Check the root layout to see if there's a higher-level auth guard
cat -n service/app/src/app/layout.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 1862
🏁 Script executed:
# Check if there's any middleware or page-level protection
find service/app/src/app -name "layout.tsx" -o -name "middleware.ts" | head -10Repository: JECT-Study/JECT-5-FE
Length of output: 264
🏁 Script executed:
# Also check what's in DashboardGameSection to see what's being rendered
wc -l service/app/src/app/dashboard/components/dashboardGameSection.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 133
🏁 Script executed:
# Check the Providers component to see how auth is initialized
cat -n service/app/src/app/providers.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 1279
🏁 Script executed:
# Check if there's a middleware file for auth
find . -name "middleware.ts" -o -name "middleware.js" | head -5Repository: JECT-Study/JECT-5-FE
Length of output: 95
🏁 Script executed:
# Let's examine the DashboardGameSection to understand what content gets rendered
head -50 service/app/src/app/dashboard/components/dashboardGameSection.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 1609
🏁 Script executed:
# Check the middleware implementation
cat -n service/app/src/middleware.tsRepository: JECT-Study/JECT-5-FE
Length of output: 1062
🏁 Script executed:
# Check if create and dashboard pages are in protected route groups
ls -la service/app/src/app/ | grep -E "dashboard|create"Repository: JECT-Study/JECT-5-FE
Length of output: 180
🏁 Script executed:
# Let's understand the page hierarchy
find service/app/src/app -type d -name "dashboard" -o -type d -name "create" | head -10Repository: JECT-Study/JECT-5-FE
Length of output: 120
🏁 Script executed:
# Check what useInfiniteMyGames does and if it makes API calls
cat -n service/app/src/entities/game/model/useInfiniteMyGames.tsRepository: JECT-Study/JECT-5-FE
Length of output: 3764
🏁 Script executed:
# Also check if there are any error boundaries or loading states
grep -n "isLoading\|isFetching\|error" service/app/src/app/dashboard/components/dashboardGameSection.tsx | head -20Repository: JECT-Study/JECT-5-FE
Length of output: 406
🏁 Script executed:
# Check the entire DashboardGameSection to see all rendering logic
cat -n service/app/src/app/dashboard/components/dashboardGameSection.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 4390
🏁 Script executed:
# Check if DashboardNavigation or other components handle auth state
cat -n service/app/src/widgets/DashboardNavigation.tsx 2>/dev/null | head -50Repository: JECT-Study/JECT-5-FE
Length of output: 920
🏁 Script executed:
# Let's check if there's any auth state handling in the page layouts
cat -n service/app/src/app/dashboard/layout.tsx 2>/dev/null || echo "No layout file"Repository: JECT-Study/JECT-5-FE
Length of output: 78
🏁 Script executed:
# Check the create layout as well
cat -n service/app/src/app/create/layout.tsx 2>/dev/null || echo "No layout file"Repository: JECT-Study/JECT-5-FE
Length of output: 607
🏁 Script executed:
# Let's verify the actual bootstrap timing by checking when AuthProvider's useEffect runs
grep -n "bootstrap\|AuthProvider\|hasBootstrapped" service/app/src/entities/auth -r --type ts --type tsx | head -20Repository: JECT-Study/JECT-5-FE
Length of output: 181
🏁 Script executed:
# Let's check the bootstrap and auth handling with rg
rg -n "bootstrap|hasBootstrapped" service/app/src/entities/auth | head -20Repository: JECT-Study/JECT-5-FE
Length of output: 830
🏁 Script executed:
# Now let's trace the flow: When does bootstrap complete relative to page render?
# Check if there's a suspense boundary or loading state in the app
find service/app/src/app -name "loading.tsx" | head -10Repository: JECT-Study/JECT-5-FE
Length of output: 102
🏁 Script executed:
# Let's also check if there are any error boundaries or other protections
rg -n "ErrorBoundary|Suspense" service/app/src/app/dashboard -A 3Repository: JECT-Study/JECT-5-FE
Length of output: 46
🏁 Script executed:
# Check the getMyGames API implementation to see error handling
cat -n service/app/src/entities/game/api/index.ts 2>/dev/null || find . -name "*.ts" -path "*game/api*" | xargs grep -l "getMyGames" | head -3Repository: JECT-Study/JECT-5-FE
Length of output: 599
🏁 Script executed:
# Find and check the getMyGames implementation
rg -n "export.*getMyGames|function.*getMyGames" --type ts | head -5Repository: JECT-Study/JECT-5-FE
Length of output: 263
🏁 Script executed:
# Let's also check what happens when API calls fail (401 responses)
rg -n "fetchClient|401\|Unauthorized" service/app/src/shared -A 3 | head -30Repository: JECT-Study/JECT-5-FE
Length of output: 366
🏁 Script executed:
# Check the getMyGames implementation
cat -n service/app/src/entities/game/api/getMyGames.tsRepository: JECT-Study/JECT-5-FE
Length of output: 683
🏁 Script executed:
# Check the fetchClient implementation for error/auth handling
cat -n service/app/src/shared/api/fetchClient.ts | head -100Repository: JECT-Study/JECT-5-FE
Length of output: 2802
🏁 Script executed:
# Let's check if bootstrap completes quickly or if there's real timing
cat -n service/app/src/entities/auth/api/validateSession.tsRepository: JECT-Study/JECT-5-FE
Length of output: 329
🏁 Script executed:
# Check if there are query dependencies that might prevent initial API calls
rg -n "enabled" service/app/src/entities/game/model/useInfiniteMyGames.tsRepository: JECT-Study/JECT-5-FE
Length of output: 123
🏁 Script executed:
# Let's create a simple timeline test - check if getMyGames call is immediate
rg -n "useInfiniteMyGames\|enabled" service/app/src/app/dashboard/components/dashboardGameSection.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 46
🏁 Script executed:
# Let me verify one more detail - does the page structure itself contain sensitive info
head -20 service/app/src/app/dashboard/components/dashboardGameSection.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 867
🏁 Script executed:
# Check what gets rendered in the initial skeleton/loading state
cat -n service/app/src/entities/game/ui/gameLibrarySkeleton.tsx 2>/dev/null || echo "Not found"Repository: JECT-Study/JECT-5-FE
Length of output: 774
🏁 Script executed:
# Final check - confirm that DashboardNavigation doesn't expose sensitive user data
grep -n "user\|profile\|email\|nickname" service/app/src/widgets/DashboardNavigation.tsxRepository: JECT-Study/JECT-5-FE
Length of output: 46
보호된 페이지가 인증 완료 전에 렌더링되지만, 실제 민감한 데이터는 API 레벨에서 보호됩니다.
authStatus === "unknown" 상태에서 조기 반환하면 부트스트랩이 진행되는 동안 페이지 구조가 렌더링됩니다. 그러나 사용자의 게임 데이터는 useInfiniteMyGames의 API 호출이 필요하며, 세션이 유효하지 않으면 401 응답이 반환되어 auth:session-expired 이벤트가 발생하고 리디렉션이 수행됩니다. 로딩 상태에서는 스켈레톤 UI만 표시되므로 실제 민감한 데이터가 노출되지는 않습니다.
authStatus === "unknown" 상태 동안 로딩 UI를 표시하는 것이 더 나은 UX 패턴입니다:
const ProtectedPage = () => {
const { authStatus } = useAuthStore()
useAuthGuard()
if (authStatus === "unknown") {
return <LoadingSpinner />
}
return <ProtectedContent />
}🤖 Prompt for AI Agents
In service/app/src/entities/auth/model/hooks/useAuthGuard.ts around lines 10–16,
update the guard so it only triggers redirect when the auth status is explicitly
unauthenticated (e.g., "unauthenticated") and do not show a blocking alert;
leave the "unknown" state untouched so pages can render their loading/skeleton
UI—adjust the effect condition to return early for "unknown", remove the alert
call, and call router.replace("/") only when authStatus === "unauthenticated" so
components can show a LoadingSpinner when authStatus === "unknown".
| bootstrap: async () => { | ||
| if (get().hasBootstrapped) return | ||
| try { | ||
| await validateSession() | ||
| set({ authStatus: "authenticated" }) | ||
| } catch (error) { | ||
| set({ authStatus: "unauthenticated" }) | ||
| } | ||
| set({ hasBootstrapped: true }) | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
치명적: 부트스트랩 시 사용자 데이터 미조회
bootstrap()이 성공하면 authStatus는 "authenticated"로 설정되지만 user는 null로 유지됩니다. 페이지 새로고침 후 인증된 사용자는 프로필 이미지와 닉네임 정보 없이 인증 상태만 표시됩니다.
validateSession API가 사용자 데이터를 반환하도록 수정하거나, 별도의 사용자 정보 조회 API를 호출하여 user 상태를 설정해야 합니다.
🔎 제안하는 수정 방안
방안 1: validateSession이 사용자 데이터를 반환하도록 수정
service/app/src/entities/auth/api/validateSession.ts를 수정:
export const validateSession = async () => {
const response = await fetchClient.get<{ nickname: string; profileImageUrl: string }>("users/me")
return response.json()
}그리고 bootstrap에서 사용:
bootstrap: async () => {
if (get().hasBootstrapped) return
try {
- await validateSession()
- set({ authStatus: "authenticated" })
+ const userData = await validateSession()
+ set({
+ authStatus: "authenticated",
+ user: { nickname: userData.data.nickname, profileImageUrl: userData.data.profileImageUrl }
+ })
} catch (error) {
set({ authStatus: "unauthenticated" })
}
set({ hasBootstrapped: true })
},방안 2: 별도의 사용자 정보 API 호출
+ import { fetchUserInfo } from "../api/fetchUserInfo"
bootstrap: async () => {
if (get().hasBootstrapped) return
try {
await validateSession()
+ const userInfo = await fetchUserInfo()
- set({ authStatus: "authenticated" })
+ set({
+ authStatus: "authenticated",
+ user: { nickname: userInfo.data.nickname, profileImageUrl: userInfo.data.profileImageUrl }
+ })
} catch (error) {
set({ authStatus: "unauthenticated" })
}
set({ hasBootstrapped: true })
},Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In service/app/src/entities/auth/store/useAuthStore.ts around lines 25 to 34,
bootstrap() currently sets authStatus to "authenticated" but leaves user null;
update bootstrap to retrieve and set the authenticated user's data before
marking bootstrapped: either (A) change validateSession to return the user
payload and assign set({ user: userData, authStatus: "authenticated" }) when the
call succeeds, or (B) call a separate users/me API from bootstrap to fetch the
current user and then set({ user: userData, authStatus: "authenticated" }) in
the success path; ensure the catch still sets authStatus to "unauthenticated"
and that hasBootstrapped is set to true after handling both paths.
| logout: async () => { | ||
| await logout() | ||
| set({ authStatus: "unauthenticated", user: null }) | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그아웃 실패 시 상태 불일치 발생 가능
logout() API 호출이 실패하면 클라이언트 상태가 업데이트되지 않아 사용자는 로그인 상태로 유지됩니다. 로그아웃은 사용자의 명시적 의도이므로, API 실패 여부와 관계없이 클라이언트 상태를 업데이트하는 것이 좋습니다.
🔎 제안하는 수정 사항
방안 1: API 결과와 무관하게 클라이언트 상태 업데이트 (권장)
logout: async () => {
- await logout()
- set({ authStatus: "unauthenticated", user: null })
+ try {
+ await logout()
+ } catch (error) {
+ console.error("Logout API failed:", error)
+ } finally {
+ set({ authStatus: "unauthenticated", user: null })
+ }
},방안 2: 에러를 상위로 전파하면서 클라이언트 상태는 업데이트
logout: async () => {
- await logout()
- set({ authStatus: "unauthenticated", user: null })
+ try {
+ await logout()
+ } catch (error) {
+ console.error("Logout API failed:", error)
+ throw error
+ } finally {
+ set({ authStatus: "unauthenticated", user: null })
+ }
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| logout: async () => { | |
| await logout() | |
| set({ authStatus: "unauthenticated", user: null }) | |
| }, | |
| logout: async () => { | |
| try { | |
| await logout() | |
| } catch (error) { | |
| console.error("Logout API failed:", error) | |
| } finally { | |
| set({ authStatus: "unauthenticated", user: null }) | |
| } | |
| }, |
🤖 Prompt for AI Agents
In service/app/src/entities/auth/store/useAuthStore.ts around lines 45 to 48,
the current logout handler awaits the logout() API and only then updates client
state, which can leave the UI inconsistent if the API call fails; change it so
the client state is updated regardless of the API result (recommended: call
set({ authStatus: "unauthenticated", user: null }) in a finally block after
attempting logout), and if you need to surface the API error rethrow it from the
catch so callers can handle it (alternative: update state before calling logout
but still catch and rethrow errors as needed).
| {authStatus === "authenticated" ? ( | ||
| <> | ||
| <PrimaryBoxButton | ||
| size="sm" | ||
| _style="solid" | ||
| onClick={handleMyGamesClick} | ||
| aria-label="내 게임 관리 페이지로 이동" | ||
| > | ||
| 내 게임 | ||
| <PrimaryBoxButton size="sm" _style="solid" asChild> | ||
| <Link href="/dashboard">내 게임</Link> | ||
| </PrimaryBoxButton> | ||
| <PrimaryBoxButton | ||
| size="sm" | ||
| _style="solid" | ||
| onClick={handleCreateGameClick} | ||
| > | ||
| <Add aria-hidden="true" /> | ||
| 게임 만들기 | ||
| <PrimaryBoxButton size="sm" _style="solid" asChild> | ||
| <Link href="/create"> | ||
| <Add aria-hidden="true" /> | ||
| 게임 만들기 | ||
| </Link> | ||
| </PrimaryBoxButton> | ||
| <AvatarButton src={user?.profileImageUrl} onClick={logout} /> | ||
| </> | ||
| ) : ( | ||
| <KakaoLoginButton /> | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
초기 인증 상태 처리 개선 필요
authStatus가 "unknown"일 때(부트스트랩 중) 로그인 버튼이 표시되어 인증된 사용자에게 깜빡임 현상이 발생할 수 있습니다. 부트스트랩이 완료될 때까지 로딩 상태를 표시하거나 아무것도 렌더링하지 않는 것이 좋습니다.
🔎 제안하는 수정 사항
return (
<>
- {authStatus === "authenticated" ? (
+ {authStatus === "unknown" ? null : authStatus === "authenticated" ? (
<>
<PrimaryBoxButton size="sm" _style="solid" asChild>
<Link href="/dashboard">내 게임</Link>
</PrimaryBoxButton>
<PrimaryBoxButton size="sm" _style="solid" asChild>
<Link href="/create">
<Add aria-hidden="true" />
게임 만들기
</Link>
</PrimaryBoxButton>
<AvatarButton src={user?.profileImageUrl} onClick={logout} />
</>
) : (
<KakaoLoginButton />
)}
<ThemeToggle />
</>
)또는 로딩 스피너를 표시:
return (
<>
- {authStatus === "authenticated" ? (
+ {authStatus === "unknown" ? (
+ <div className="flex items-center">로딩 중...</div>
+ ) : authStatus === "authenticated" ? (
<>
<PrimaryBoxButton size="sm" _style="solid" asChild>
<Link href="/dashboard">내 게임</Link>
</PrimaryBoxButton>
<PrimaryBoxButton size="sm" _style="solid" asChild>
<Link href="/create">
<Add aria-hidden="true" />
게임 만들기
</Link>
</PrimaryBoxButton>
<AvatarButton src={user?.profileImageUrl} onClick={logout} />
</>
) : (
<KakaoLoginButton />
)}
<ThemeToggle />
</>
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {authStatus === "authenticated" ? ( | |
| <> | |
| <PrimaryBoxButton | |
| size="sm" | |
| _style="solid" | |
| onClick={handleMyGamesClick} | |
| aria-label="내 게임 관리 페이지로 이동" | |
| > | |
| 내 게임 | |
| <PrimaryBoxButton size="sm" _style="solid" asChild> | |
| <Link href="/dashboard">내 게임</Link> | |
| </PrimaryBoxButton> | |
| <PrimaryBoxButton | |
| size="sm" | |
| _style="solid" | |
| onClick={handleCreateGameClick} | |
| > | |
| <Add aria-hidden="true" /> | |
| 게임 만들기 | |
| <PrimaryBoxButton size="sm" _style="solid" asChild> | |
| <Link href="/create"> | |
| <Add aria-hidden="true" /> | |
| 게임 만들기 | |
| </Link> | |
| </PrimaryBoxButton> | |
| <AvatarButton src={user?.profileImageUrl} onClick={logout} /> | |
| </> | |
| ) : ( | |
| <KakaoLoginButton /> | |
| )} | |
| {authStatus === "unknown" ? null : authStatus === "authenticated" ? ( | |
| <> | |
| <PrimaryBoxButton size="sm" _style="solid" asChild> | |
| <Link href="/dashboard">내 게임</Link> | |
| </PrimaryBoxButton> | |
| <PrimaryBoxButton size="sm" _style="solid" asChild> | |
| <Link href="/create"> | |
| <Add aria-hidden="true" /> | |
| 게임 만들기 | |
| </Link> | |
| </PrimaryBoxButton> | |
| <AvatarButton src={user?.profileImageUrl} onClick={logout} /> | |
| </> | |
| ) : ( | |
| <KakaoLoginButton /> | |
| )} |
🤖 Prompt for AI Agents
In service/app/src/widgets/components/HomeNavigationRightContent.tsx around
lines 18 to 33, handle the "unknown" authStatus during bootstrap to avoid
flashing the login button: detect if authStatus === "unknown" and return either
null or a small loading indicator (spinner) before rendering the authenticated
or unauthenticated branches so the KakaoLoginButton is not shown while bootstrap
is in progress.
|
Deploy preview for re-creation ready! ✅ Preview Built with commit 9f3e643. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
service/app/src/entities/auth/store/useAuthStore.ts (2)
29-38: 치명적: 부트스트랩 시 사용자 데이터 미조회 (중복)
bootstrap()이 성공해도user는null로 유지됩니다. 이는 이전 리뷰에서 지적된 치명적 이슈와 동일합니다.
52-55: 주요: 로그아웃 실패 시 상태 불일치 (중복)로그아웃 API 호출 실패 시 클라이언트 상태가 업데이트되지 않는 문제는 이전 리뷰에서 지적된 주요 이슈와 동일합니다.
🧹 Nitpick comments (1)
service/app/src/entities/auth/store/useAuthStore.ts (1)
40-50: 로그인 로직은 적절하나 에러 처리 개선 필요사용자 데이터 추출 및 저장 로직은 올바르게 구현되었습니다. 다만 Line 48에서 원본 에러를 감싸는 방식이 디버깅을 어렵게 만들 수 있습니다.
🔎 에러 컨텍스트 보존을 위한 제안
} catch (error) { - throw new Error("login failed") + console.error("Login failed:", error) + throw error }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
service/app/src/entities/auth/store/useAuthStore.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
All file names must be camelCase (not PascalCase or kebab-case)
Files:
service/app/src/entities/auth/store/useAuthStore.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Component names must use PascalCase in the code (while file names use camelCase)
Use Zustand for client-side state management
Use TanStack Query for server state and API caching
Use async/await for promises instead of .then() chains
Use custom fetch client with interceptors from shared/lib/fetchClient.ts for API requests
**/*.{ts,tsx}: Use TypeScript with ES modules; keep files lower-case kebab or camel per existing folder conventions (e.g.,gameLibraryGrid.tsx)
Favor explicit domain names (e.g.,GameCardOptions,useInfiniteMyGames) and colocate UI, API, and hooks within eachentities/<domain>module
Run ESLint using lint scripts; linting is enforced via lint-staged
Files:
service/app/src/entities/auth/store/useAuthStore.ts
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use shared ESLint configuration across all workspaces
Files:
service/app/src/entities/auth/store/useAuthStore.ts
**/*.{js,ts,jsx,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Prettier with Tailwind CSS plugin for formatting
Files:
service/app/src/entities/auth/store/useAuthStore.ts
**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use 2-space indentation throughout the codebase
Files:
service/app/src/entities/auth/store/useAuthStore.ts
**/*.{ts,tsx,css,json,md}
📄 CodeRabbit inference engine (AGENTS.md)
Apply Prettier formatting using
yarn workspace <pkg> format; this is enforced via lint-staged
Files:
service/app/src/entities/auth/store/useAuthStore.ts
🧠 Learnings (1)
📚 Learning: 2025-11-27T13:45:59.426Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T13:45:59.426Z
Learning: Applies to **/*.{ts,tsx} : Use Zustand for client-side state management
Applied to files:
service/app/src/entities/auth/store/useAuthStore.ts
🧬 Code graph analysis (1)
service/app/src/entities/auth/store/useAuthStore.ts (3)
service/app/src/entities/auth/api/validateSession.ts (1)
validateSession(3-6)service/app/src/entities/auth/api/kakaoLogin.ts (1)
kakaoLogin(5-11)service/app/src/entities/auth/api/logout.ts (1)
logout(3-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci
🔇 Additional comments (5)
service/app/src/entities/auth/store/useAuthStore.ts (5)
3-3: LGTM: 타입 정의 및 미들웨어 추가 적절함
immer미들웨어와validateSessionAPI 추가, 그리고AuthStatus와AuthUser타입 정의가 적절합니다. 인증 상태를 tri-state로 관리하는 것은 로딩/인증/미인증 상태를 명확히 구분하는 좋은 패턴입니다.Also applies to: 7-7, 9-10
12-20: LGTM: AuthState 타입 정의가 명확함상태 타입 정의가 명확하고 타입 안전성이 보장됩니다.
hasBootstrapped플래그로 중복 부트스트랩을 방지하는 것도 좋은 설계입니다.
24-27: LGTM: 초기 상태 설정이 적절함
immer미들웨어를 사용한 스토어 생성과 초기 상태 설정이 적절합니다.authStatus를"unknown"으로 시작하는 것은 부트스트랩 패턴에 맞는 올바른 접근입니다.Based on learnings, Zustand를 클라이언트 상태 관리에 사용하는 것이 코딩 가이드라인에 부합합니다.
57-59: LGTM: 세션 만료 처리를 위한 명확한 헬퍼 메서드
setUnauthenticated()메서드는 세션 만료 이벤트 처리를 위해authStatus와user를 모두 정리하는 명확한 헬퍼입니다. AuthProvider에서 session-expired 이벤트 핸들링에 적절히 사용됩니다.
60-67: LGTM: Persist 설정이 부트스트랩 패턴에 적합함
sessionStorage를 사용하고user만 영속화하는 것은 적절한 설계입니다.authStatus와hasBootstrapped를 영속화하지 않음으로써 매 페이지 로드 시 세션을 재검증하는 부트스트랩 패턴을 올바르게 구현했습니다.
| } catch (error) { | ||
| set({ authStatus: "unauthenticated" }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주요: 세션 무효 시 사용자 데이터 미삭제
bootstrap() 실패 시(catch 블록) authStatus만 "unauthenticated"로 설정하고 user 데이터는 정리하지 않습니다. 이로 인해 세션이 만료되었는데도 sessionStorage에 저장된 이전 사용자 데이터가 남아 있는 불일치 상태가 발생할 수 있습니다.
🔎 제안하는 수정
} catch (error) {
- set({ authStatus: "unauthenticated" })
+ set({ authStatus: "unauthenticated", user: null })
}🤖 Prompt for AI Agents
In service/app/src/entities/auth/store/useAuthStore.ts around lines 34 to 36,
the catch block only sets authStatus to "unauthenticated" but leaves the user
data and any sessionStorage/session cookies intact; update the catch to also
clear the in-memory user state (e.g., set user to null/undefined) and remove any
persisted auth/user keys from sessionStorage (and clear tokens/cookies if
applicable) so the store and storage stay consistent when bootstrap() fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
service/app/src/widgets/components/HomeNavigationRightContent.tsx (1)
29-44: 초기 인증 상태 처리 개선 필요이전 리뷰에서 지적된 사항과 동일하게,
authStatus가"unknown"일 때(부트스트랩 중) 로그인 버튼이 표시되어 인증된 사용자에게 깜빡임 현상이 발생할 수 있습니다. 부트스트랩이 완료될 때까지 로딩 상태를 표시하거나 null을 반환하는 것이 좋습니다.🔎 제안하는 수정 사항
return ( <> - {authStatus === "authenticated" ? ( + {authStatus === "unknown" ? null : authStatus === "authenticated" ? ( <> <PrimaryBoxButton size="sm" _style="solid" asChild> <Link href="/dashboard">내 게임</Link> </PrimaryBoxButton> <PrimaryBoxButton size="sm" _style="solid" asChild> <Link href="/create"> <Add aria-hidden="true" /> 게임 만들기 </Link> </PrimaryBoxButton> <AvatarButton src={user?.profileImageUrl} onClick={handleLogout} /> </> ) : ( <KakaoLoginButton returnTo={returnTo} /> )} <ThemeToggle /> </> )
🧹 Nitpick comments (3)
service/app/src/widgets/GamesNavigation.tsx (2)
69-84: 초기 인증 상태 처리 개선 권장
authStatus가"unknown"일 때(부트스트랩 중) 로그인 버튼이 표시되어 인증된 사용자에게 깜빡임 현상이 발생할 수 있습니다. 부트스트랩이 완료될 때까지 로딩 상태를 표시하거나 null을 반환하는 것을 고려해보세요.🔎 제안하는 수정 사항
rightContent={ <> - {authStatus === "authenticated" ? ( + {authStatus === "unknown" ? null : authStatus === "authenticated" ? ( <> <PrimaryBoxButton size="sm" _style="solid" asChild> <Link href="/create"> <Add aria-hidden="true" /> 게임 만들기 </Link> </PrimaryBoxButton> <AvatarButton src={user?.profileImageUrl} onClick={handleLogout} /> </> ) : ( <KakaoLoginButton returnTo={returnTo} /> )} <ThemeToggle /> </> }
33-33: returnTo 계산 로직 중복
returnTo계산 패턴이HomeNavigationRightContent.tsx와 중복됩니다. 공통 유틸리티 함수로 추출하여 재사용성을 높이고 유지보수를 개선하는 것을 고려해보세요.🔎 제안하는 리팩토링
shared/lib/navigation.ts에 유틸리티 함수를 추가:export const buildReturnToUrl = (pathname: string, searchParams: URLSearchParams) => { const search = searchParams.toString() return `${pathname}${search ? `?${search}` : ""}` }그런 다음 두 파일에서 사용:
- const returnTo = `${pathname}${searchParams.toString() ? `?${searchParams}` : ""}` + const returnTo = buildReturnToUrl(pathname, searchParams)service/app/src/widgets/components/HomeNavigationRightContent.tsx (1)
19-19: returnTo 계산 로직 중복
returnTo계산 패턴이GamesNavigation.tsx와 중복됩니다. 공통 유틸리티 함수로 추출하여 재사용성을 높이고 유지보수를 개선하는 것을 고려해보세요.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
service/app/src/app/login/kakao/page.tsxservice/app/src/app/login/page.tsxservice/app/src/entities/game/hooks/useGameEntryNavigation.tsservice/app/src/shared/lib/saveEntry.tsservice/app/src/widgets/GamesNavigation.tsxservice/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/components/kakaoLoginButton.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
All file names must be camelCase (not PascalCase or kebab-case)
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/GamesNavigation.tsxservice/app/src/app/login/kakao/page.tsxservice/app/src/shared/lib/saveEntry.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/entities/game/hooks/useGameEntryNavigation.tsservice/app/src/app/login/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Component names must use PascalCase in the code (while file names use camelCase)
Use Zustand for client-side state management
Use TanStack Query for server state and API caching
Use async/await for promises instead of .then() chains
Use custom fetch client with interceptors from shared/lib/fetchClient.ts for API requests
**/*.{ts,tsx}: Use TypeScript with ES modules; keep files lower-case kebab or camel per existing folder conventions (e.g.,gameLibraryGrid.tsx)
Favor explicit domain names (e.g.,GameCardOptions,useInfiniteMyGames) and colocate UI, API, and hooks within eachentities/<domain>module
Run ESLint using lint scripts; linting is enforced via lint-staged
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/GamesNavigation.tsxservice/app/src/app/login/kakao/page.tsxservice/app/src/shared/lib/saveEntry.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/entities/game/hooks/useGameEntryNavigation.tsservice/app/src/app/login/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use shared ESLint configuration across all workspaces
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/GamesNavigation.tsxservice/app/src/app/login/kakao/page.tsxservice/app/src/shared/lib/saveEntry.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/entities/game/hooks/useGameEntryNavigation.tsservice/app/src/app/login/page.tsx
**/*.{js,ts,jsx,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Prettier with Tailwind CSS plugin for formatting
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/GamesNavigation.tsxservice/app/src/app/login/kakao/page.tsxservice/app/src/shared/lib/saveEntry.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/entities/game/hooks/useGameEntryNavigation.tsservice/app/src/app/login/page.tsx
**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use 2-space indentation throughout the codebase
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/GamesNavigation.tsxservice/app/src/app/login/kakao/page.tsxservice/app/src/shared/lib/saveEntry.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/entities/game/hooks/useGameEntryNavigation.tsservice/app/src/app/login/page.tsx
**/*.{ts,tsx,css,json,md}
📄 CodeRabbit inference engine (AGENTS.md)
Apply Prettier formatting using
yarn workspace <pkg> format; this is enforced via lint-staged
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/GamesNavigation.tsxservice/app/src/app/login/kakao/page.tsxservice/app/src/shared/lib/saveEntry.tsservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/entities/game/hooks/useGameEntryNavigation.tsservice/app/src/app/login/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Tailwind CSS utilities should be ordered via the Prettier Tailwind plugin; avoid inline style objects unless necessary
Files:
service/app/src/widgets/components/HomeNavigationRightContent.tsxservice/app/src/widgets/GamesNavigation.tsxservice/app/src/app/login/kakao/page.tsxservice/app/src/widgets/components/kakaoLoginButton.tsxservice/app/src/app/login/page.tsx
**/src/app/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
In the Next.js app workspace, UI routes live in
src/app
Files:
service/app/src/app/login/kakao/page.tsxservice/app/src/app/login/page.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-27T13:46:12.950Z
Learning: Pull requests should summarize intent, note affected packages (`ject-5-fe/app`, `ject-5-fe/design`, etc.), link tracking issues, and mention required follow-up tasks to help reviewers
📚 Learning: 2025-11-27T13:46:12.950Z
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-27T13:46:12.950Z
Learning: Applies to **/*.{ts,tsx} : Favor explicit domain names (e.g., `GameCardOptions`, `useInfiniteMyGames`) and colocate UI, API, and hooks within each `entities/<domain>` module
Applied to files:
service/app/src/entities/game/hooks/useGameEntryNavigation.ts
🧬 Code graph analysis (5)
service/app/src/widgets/components/HomeNavigationRightContent.tsx (4)
service/app/src/entities/auth/store/useAuthStore.ts (1)
useAuthStore(22-68)service/app/src/entities/auth/api/logout.ts (1)
logout(3-6)service/app/src/widgets/components/avatarButton.tsx (1)
AvatarButton(15-50)service/app/src/widgets/components/kakaoLoginButton.tsx (1)
KakaoLoginButton(10-33)
service/app/src/widgets/GamesNavigation.tsx (5)
service/app/src/entities/auth/store/useAuthStore.ts (1)
useAuthStore(22-68)service/app/src/entities/auth/api/logout.ts (1)
logout(3-6)shared/design/src/components/button/primaryBoxButton.tsx (1)
PrimaryBoxButton(53-66)service/app/src/widgets/components/avatarButton.tsx (1)
AvatarButton(15-50)service/app/src/widgets/components/kakaoLoginButton.tsx (1)
KakaoLoginButton(10-33)
service/app/src/app/login/kakao/page.tsx (2)
service/app/src/entities/auth/store/useAuthStore.ts (1)
useAuthStore(22-68)service/app/src/shared/lib/saveEntry.ts (3)
getEntry(9-12)ENTRY_KEYS(19-22)clearEntry(14-17)
service/app/src/entities/game/hooks/useGameEntryNavigation.ts (1)
service/app/src/shared/lib/saveEntry.ts (3)
saveEntry(3-7)ENTRY_KEYS(19-22)getEntry(9-12)
service/app/src/app/login/page.tsx (1)
service/app/src/shared/lib/saveEntry.ts (2)
saveEntry(3-7)ENTRY_KEYS(19-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci
🔇 Additional comments (7)
service/app/src/entities/game/hooks/useGameEntryNavigation.ts (1)
3-17: 게임 엔트리 네비게이션 로직 개선 확인
ENTRY_KEYS.game을 사용하여 게임 엔트리를 저장하고 검색하는 로직이 올바르게 구현되었습니다. 키 기반 스토리지 패턴을 일관되게 적용하여 다른 엔트리 타입(예: auth)과의 충돌을 방지합니다.service/app/src/app/login/page.tsx (1)
3-30: 로그인 후 리다이렉션을 위한 returnTo 저장 로직 추가 확인URL 쿼리 파라미터에서
returnTo값을 읽어ENTRY_KEYS.auth키로 저장하는 로직이 올바르게 구현되었습니다. 의존성 배열에searchParams를 포함하여 쿼리 파라미터 변경 시 재실행되도록 했습니다.service/app/src/app/login/kakao/page.tsx (2)
13-17: 중복 실행 방지를 위한 가드 추가
handledRef를 사용하여 콜백 로직이 한 번만 실행되도록 보장합니다. React 18의 StrictMode에서 발생할 수 있는 중복 실행을 방지하는 좋은 패턴입니다.
27-29: 로그인 후 returnTo 경로로 리다이렉션 구현
getEntry(ENTRY_KEYS.auth)로 저장된 경로를 검색하고clearEntry로 정리한 후router.replace를 사용하여 네비게이션합니다.router.push대신router.replace를 사용하여 브라우저 히스토리에 불필요한 엔트리를 추가하지 않는 것은 올바른 접근입니다.service/app/src/widgets/GamesNavigation.tsx (1)
30-39: 인증 상태 및 로그아웃 처리 구현
useAuthStore에서 인증 상태와 사용자 정보를 가져오고, 로그아웃 후 루트 페이지로 이동하는 로직이 올바르게 구현되었습니다.service/app/src/widgets/components/kakaoLoginButton.tsx (1)
3-31: Link 기반 네비게이션으로 마이그레이션
onClick기반 네비게이션을 Next.jsLink컴포넌트로 교체하고returnToprop을 추가하여 로그인 후 리다이렉션을 지원합니다.asChild패턴을 사용하여 버튼 스타일링을 유지하면서 링크 동작을 위임하는 것은 올바른 접근입니다.service/app/src/shared/lib/saveEntry.ts (1)
1-22: 키 기반 스토리지 API로 리팩토링엔트리 저장 로직을 키 기반 API로 리팩토링하여 여러 타입의 엔트리(게임, 인증)를 독립적으로 관리할 수 있게 되었습니다.
ENTRY_KEYS상수를 통해 타입 안전한 키 참조를 제공하고, 브라우저 환경 가드를 적절히 구현했습니다.
|
❌ 테스트 실패 테스트가 실패했습니다. Actions 로그를 확인해주세요. |
|
❌ 테스트 실패 테스트가 실패했습니다. Actions 로그를 확인해주세요. |
1 similar comment
|
❌ 테스트 실패 테스트가 실패했습니다. Actions 로그를 확인해주세요. |
|
❌ 테스트 실패 테스트가 실패했습니다. Actions 로그를 확인해주세요. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
service/app/src/app/login/page.tsx (1)
13-17: Suspense fallback과 KakaoLoginClient의 스피너 스타일이 다릅니다.Fallback UI는
size-8과border-gray-900을 사용하고,KakaoLoginClient는size-32와border-blue-400을 사용합니다. 로딩 상태 전환 시 시각적 점프가 발생할 수 있습니다.🔎 일관된 스타일 적용 제안
fallback={ - <div className="flex min-h-screen items-center justify-center"> + <div className="flex min-h-screen items-center justify-center bg-background-primary"> <div className="text-center"> - <div className="mb-4">페이지를 준비하고 있습니다</div> - <div className="mx-auto size-8 animate-spin rounded-full border-b-2 border-gray-900"></div> + <div className="size-32 animate-spin rounded-full border-4 border-blue-400 border-t-transparent"></div> + <p className="mt-16 text-sm text-text-primary">페이지를 준비하고 있습니다</p> </div> </div> }service/app/src/app/login/kakaoLoginClient.tsx (1)
23-23: 환경 변수 누락 시 사용자에게 피드백이 없습니다.프로덕션에서
kakaoClientId또는redirectUri가 없으면 컴포넌트가 무한히 스피너만 표시합니다. 사용자 경험과 디버깅을 위해 에러 처리를 추가하는 것이 좋습니다.🔎 에러 상태 처리 제안
+import { useState } from "react" export function KakaoLoginClient({ returnTo }: { returnTo?: string }) { const router = useRouter() + const [error, setError] = useState<string | null>(null) const kakaoClientId = process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID const redirectUri = process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI useEffect(() => { if (returnTo) saveEntry(ENTRY_KEYS.auth, returnTo) if (process.env.NODE_ENV === "development") { router.replace(`/login/kakao/?code=${MSW_MOCK_CODE}`) return } - if (!kakaoClientId || !redirectUri) return + if (!kakaoClientId || !redirectUri) { + setError("로그인 설정이 올바르지 않습니다. 관리자에게 문의하세요.") + return + } const kakaoAuthUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${kakaoClientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code` router.replace(kakaoAuthUrl) }, [router, kakaoClientId, redirectUri, returnTo]) + if (error) { + return ( + <div className="flex min-h-screen items-center justify-center bg-background-primary"> + <p className="text-sm text-red-500">{error}</p> + </div> + ) + } + return (
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
All file names must be camelCase (not PascalCase or kebab-case)
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Component names must use PascalCase in the code (while file names use camelCase)
Use Zustand for client-side state management
Use TanStack Query for server state and API caching
Use async/await for promises instead of .then() chains
Use custom fetch client with interceptors from shared/lib/fetchClient.ts for API requests
**/*.{ts,tsx}: Use TypeScript with ES modules; keep files lower-case kebab or camel per existing folder conventions (e.g.,gameLibraryGrid.tsx)
Favor explicit domain names (e.g.,GameCardOptions,useInfiniteMyGames) and colocate UI, API, and hooks within eachentities/<domain>module
Run ESLint using lint scripts; linting is enforced via lint-staged
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use shared ESLint configuration across all workspaces
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
**/*.{js,ts,jsx,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Prettier with Tailwind CSS plugin for formatting
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use 2-space indentation throughout the codebase
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
**/*.{ts,tsx,css,json,md}
📄 CodeRabbit inference engine (AGENTS.md)
Apply Prettier formatting using
yarn workspace <pkg> format; this is enforced via lint-staged
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
**/src/app/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
In the Next.js app workspace, UI routes live in
src/app
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Tailwind CSS utilities should be ordered via the Prettier Tailwind plugin; avoid inline style objects unless necessary
Files:
service/app/src/app/login/kakaoLoginClient.tsxservice/app/src/app/login/page.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: JECT-Study/JECT-5-FE PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-27T13:46:12.950Z
Learning: Pull requests should summarize intent, note affected packages (`ject-5-fe/app`, `ject-5-fe/design`, etc.), link tracking issues, and mention required follow-up tasks to help reviewers
🧬 Code graph analysis (2)
service/app/src/app/login/kakaoLoginClient.tsx (2)
service/app/src/shared/lib/saveEntry.ts (2)
saveEntry(3-7)ENTRY_KEYS(19-22)service/app/src/mocks/handlers/auth.ts (1)
MSW_MOCK_CODE(8-8)
service/app/src/app/login/page.tsx (1)
service/app/src/app/login/kakaoLoginClient.tsx (1)
KakaoLoginClient(9-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci
🔇 Additional comments (2)
service/app/src/app/login/page.tsx (1)
1-9: LGTM!Next.js 14 페이지 컴포넌트로 적절한 구조입니다.
searchParams를 통해returnTo를 받아 클라이언트 컴포넌트에 전달하는 패턴이 올바릅니다.service/app/src/app/login/kakaoLoginClient.tsx (1)
30-39: LGTM!로딩 UI가 디자인 시스템 토큰(
bg-background-primary,text-text-primary)을 적절히 사용하고 있습니다. 카카오 로그인 리다이렉트 중임을 사용자에게 명확히 알려줍니다.
| import { useRouter } from "next/navigation" | ||
| import { useEffect } from "react" | ||
|
|
||
| import { MSW_MOCK_CODE } from "@/mocks/handlers/auth" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
프로덕션 코드에서 mock 모듈 import는 잠재적 위험이 있습니다.
MSW_MOCK_CODE는 개발 환경에서만 사용되지만, import는 항상 실행됩니다. 프로덕션 빌드에서 mock 모듈이 제외되면 런타임 오류가 발생할 수 있습니다.
🔎 조건부 import 또는 인라인 상수 사용 제안
-import { MSW_MOCK_CODE } from "@/mocks/handlers/auth"
-import { ENTRY_KEYS, saveEntry } from "@/shared/lib/saveEntry"
+import { ENTRY_KEYS, saveEntry } from "@/shared/lib/saveEntry"
+
+const MSW_MOCK_CODE = "someValidCode"또는 dynamic import를 사용할 수 있습니다:
useEffect(() => {
if (process.env.NODE_ENV === "development") {
import("@/mocks/handlers/auth").then(({ MSW_MOCK_CODE }) => {
router.replace(`/login/kakao/?code=${MSW_MOCK_CODE}`)
})
return
}
// ...
}, [])Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In service/app/src/app/login/kakaoLoginClient.tsx around line 6, the file
statically imports MSW_MOCK_CODE from a mock module which is only for
development and can cause runtime errors in production; remove the top-level
import and instead load the mock value conditionally at runtime (either inline a
dev-only constant guarded by process.env.NODE_ENV === "development" or use
dynamic import inside the effect when NODE_ENV === "development") so production
builds never reference the mock module.
📝 설명
인증 관련 흐름을 수정했습니다
🛠️ 주요 변경 사항
인증 관련 버그 해결
isAuthenticated가 완전히 클라이언트 상태라 source of truth인 서버와 값이 불일치하는 경우가 있었는데, 이 문제를 해결하기 위해서 CSR 단계에서의 bootstrap 로직을 추가하여 인증 상태는 언제나 서버가 내려주는 값을 따라가도록 만들었습니다useAuthStore의 불필요한 로직을 제거했습니다소소한 변화
두 흐름을 반영했습니다. 게임 플레이 페이지에서 사용했던 로직을 재활용했습니다
앞으로 반영이 필요한 부분
/me같은 엔드포인트가 없어서 그냥 인증이 필요한 엔드포인트 중 하나인 '내 게임 불러오기'를validateSession에서 사용 중입니다... 엔드포인트가 완성되는대로 반영 필요합니다리뷰 시 고려해야 할 사항
Summary by CodeRabbit
New Features
Refactor
Tests
✏️ Tip: You can customize this high-level summary in your review settings.