Skip to content

Conversation

@kimnamheeee
Copy link
Contributor

@kimnamheeee kimnamheeee commented Dec 31, 2025

📝 설명

인증 관련 흐름을 수정했습니다

🛠️ 주요 변경 사항

인증 관련 버그 해결

  • 기존에는 isAuthenticated가 완전히 클라이언트 상태라 source of truth인 서버와 값이 불일치하는 경우가 있었는데, 이 문제를 해결하기 위해서 CSR 단계에서의 bootstrap 로직을 추가하여 인증 상태는 언제나 서버가 내려주는 값을 따라가도록 만들었습니다
  • useAuthStore의 불필요한 로직을 제거했습니다
  • email 정보를 persist에서 제거했습니다
  • 사용하지 않는 파일, 쿠키 설정과 맞지 않는 로직을 제거했습니다

소소한 변화

  • 로그인 버튼 클릭 -> 로그인 진행 -> 기존 화면 이동
  • 로그아웃 버튼 클릭 -> 홈 이동
    두 흐름을 반영했습니다. 게임 플레이 페이지에서 사용했던 로직을 재활용했습니다

앞으로 반영이 필요한 부분

  • 현재는 세션 유효성을 검증하는 /me 같은 엔드포인트가 없어서 그냥 인증이 필요한 엔드포인트 중 하나인 '내 게임 불러오기'를 validateSession에서 사용 중입니다... 엔드포인트가 완성되는대로 반영 필요합니다
  • 마찬가지로 유저 정보를 받아오는 엔드포인트가 없기 때문에 불가피하게 유저 정보를 persist에다가 넣어두었습니다. 이후 제거 필요합니다
  • 현재는 쿠키 도메인이 프론트엔드 도메인과 불일치하여 nextjs 미들웨어가 쿠키를 검사할 수 없습니다. 가능하면 이 문제를 해결하여 CSR이 아닌 SSR 단에서 인증 상태를 검사하고 렌더링 전에 접근 불가 페이지 리다이렉트 등을 처리하는 게 좋을 것 같습니다.

리뷰 시 고려해야 할 사항

Summary by CodeRabbit

  • New Features

    • 세션 검증 API와 앱 전역 인증 공급자(AuthProvider) 추가
    • 카카오 로그인 클라이언트와 복귀 경로(returnTo) 지원
  • Refactor

    • 인증 상태를 3단계(미확인/인증됨/미인증)로 전환해 인증 흐름 개선 및 자동 부트스트랩 적용
    • 로그인 인터랙션을 링크 기반으로 전환하고 아바타·로그아웃 흐름 도입
    • 복귀 경로 관리용 저장소 키(게임/인증) 및 저장/조회/삭제 유틸 개선
    • 세션 만료 처리를 이벤트 기반으로 단순화
  • Tests

    • 기존 세션 검증 관련 테스트 파일 삭제

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

Walkthrough

앱 인증이 쿠키/주기검증 방식에서 부트스트랩 기반 tri-state(authStatus) 관리로 전환되고, AuthProvider가 앱 트리에 추가되었습니다. 관련된 쿠키·세션 유틸 및 테스트가 제거되고, 로그인 네비게이션과 UI 컴포넌트들이 Link/returnTo 기반으로 변경되었습니다.

Changes

Cohort / File(s) 변경 요약
인증 상태 관리
service/app/src/entities/auth/store/useAuthStore.ts, service/app/src/entities/auth/api/validateSession.ts
boolean 기반 persisted → tri-state authStatus 도입, hasBootstrappedbootstrap()·setUnauthenticated() 추가. validateSession API 신규 파일 추가.
인증 제공자 및 훅
service/app/src/entities/auth/ui/authProvider.tsx, service/app/src/entities/auth/model/hooks/useAuthGuard.ts
AuthProvider 컴포넌트 추가(마운트 시 bootstrap(), "auth:session-expired" 구독). useAuthGuard가 hydration 대신 authStatus 기반으로 동작하도록 수정.
앱 제공자 구성
service/app/src/app/providers.tsx
Providers 트리에서 자식들을 AuthProvider로 래핑하도록 변경(렌더 트리 변경).
세션/쿠키 유틸 및 검사 제거
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
cookieUtils와 sessionValidator 모듈 및 관련 테스트 삭제(주기검증·쿠키 헬퍼 제거).
fetchClient 401 처리 변경
service/app/src/shared/api/fetchClient.ts
401 처리 후 로컬스토리지·쿠키 직접 삭제 로직 제거 — 브라우저에서 auth:session-expired 이벤트만 디스패치하도록 변경.
MSW 핸들러 업데이트
service/app/src/mocks/handlers/auth.ts
로그인/로그아웃 응답에 Set-Cookie 헤더 추가(세션 쿠키 관련 응답 헤더 변경).
UI/네비게이션 변경
service/app/src/shared/authButton.tsx, service/app/src/widgets/components/kakaoLoginButton.tsx, service/app/src/widgets/components/HomeNavigationRightContent.tsx, service/app/src/widgets/GamesNavigation.tsx
인증 검사 isAuthenticatedauthStatus === "authenticated"로 변경. KakaoLoginButton에서 onClick 제거, Link 기반 네비게이션(returnTo)으로 전환. Avatar/로그아웃 흐름 도입.
엔트리 저장소 변경
service/app/src/shared/lib/saveEntry.ts, service/app/src/entities/game/hooks/useGameEntryNavigation.ts, service/app/src/app/login/kakao/page.tsx, service/app/src/app/login/page.tsx, service/app/src/app/login/kakaoLoginClient.tsx
saveEntry가 key/value 파라미터를 받도록 변화, getEntry·clearEntry·ENTRY_KEYS 추가. 로그인 흐름에서 returnTo 저장·복구 및 콜백 일회 실행 가드 추가.
테스트/PO M 업데이트
service/app/src/app/__tests__/helpers/auth.setup.ts, service/app/src/app/__tests__/homePOM.ts, service/app/src/app/__tests__/home.spec.ts, service/app/src/app/game/[gameId]/setup/__tests__/team-management.spec.ts
Playwright 셀렉터/대기 조건을 button→link 및 URL 패턴/commit 대기로 변경. sessionStorage 키를 entryentry:game으로 변경.

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 렌더
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 분

Possibly related PRs

Suggested labels

🐛bug, 🪄refactor

Suggested reviewers

  • whdgur5717

Poem

"나는 토끼, 부트스트랩에 깡충 뛰어,
authStatus 심고 쿠키는 놓아주네,
링크로 길을 바꿔 네비게이션 춤추고,
이벤트로 속삭여 세션을 깨우네. 🐇✨"

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning PR 설명에 템플릿 섹션은 있지만 주요 변경사항 섹션이 비어있고 구체적인 내용이 없습니다. 주요 변경사항 섹션에 커밋 SHA와 함께 상세한 변경 내용(인증 상태 관리 개선, 컴포넌트 분리, 쿠키 기반에서 세션 검증으로 전환 등)을 작성하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive 제목 'Fix authentication'은 너무 일반적이어서 구체적인 변경 내용을 파악하기 어렵습니다. 인증 시스템의 구체적인 개선 사항을 반영한 더 상세한 제목으로 변경하세요. 예: 'Refactor auth: separate client component and state management'
✨ Finishing touches
  • 📝 Generate docstrings

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.

@github-actions
Copy link

테스트 실패

테스트가 실패했습니다. Actions 로그를 확인해주세요.

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: 4

🧹 Nitpick comments (3)
service/app/src/mocks/handlers/auth.ts (1)

10-23: 프로덕션 보안 속성을 반영하여 쿠키 보안을 강화하세요.

로그인 응답의 Set-Cookie 헤더에 HttpOnlySecure 플래그가 누락되었습니다. 이 파일은 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

📥 Commits

Reviewing files that changed from the base of the PR and between b19a1b1 and ab55e25.

⛔ Files ignored due to path filters (1)
  • service/app/public/avatar.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • service/app/src/app/__tests__/helpers/auth.setup.ts
  • service/app/src/app/__tests__/homePOM.ts
  • service/app/src/app/providers.tsx
  • service/app/src/entities/auth/api/validateSession.ts
  • service/app/src/entities/auth/model/hooks/useAuthGuard.ts
  • service/app/src/entities/auth/store/useAuthStore.ts
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/entities/auth/utils/__tests__/sessionValidator.test.ts
  • service/app/src/entities/auth/utils/cookieUtils.ts
  • service/app/src/entities/auth/utils/sessionValidator.ts
  • service/app/src/mocks/handlers/auth.ts
  • service/app/src/shared/api/fetchClient.ts
  • service/app/src/shared/authButton.tsx
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/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.ts
  • service/app/src/app/__tests__/helpers/auth.setup.ts
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/entities/auth/api/validateSession.ts
  • service/app/src/entities/auth/store/useAuthStore.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/app/providers.tsx
  • service/app/src/entities/auth/model/hooks/useAuthGuard.ts
  • service/app/src/shared/authButton.tsx
  • service/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 each entities/<domain> module
Run ESLint using lint scripts; linting is enforced via lint-staged

Files:

  • service/app/src/app/__tests__/homePOM.ts
  • service/app/src/app/__tests__/helpers/auth.setup.ts
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/entities/auth/api/validateSession.ts
  • service/app/src/entities/auth/store/useAuthStore.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/app/providers.tsx
  • service/app/src/entities/auth/model/hooks/useAuthGuard.ts
  • service/app/src/shared/authButton.tsx
  • service/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.ts
  • service/app/src/app/__tests__/helpers/auth.setup.ts
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/entities/auth/api/validateSession.ts
  • service/app/src/entities/auth/store/useAuthStore.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/app/providers.tsx
  • service/app/src/entities/auth/model/hooks/useAuthGuard.ts
  • service/app/src/shared/authButton.tsx
  • service/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.ts
  • service/app/src/app/__tests__/helpers/auth.setup.ts
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/entities/auth/api/validateSession.ts
  • service/app/src/entities/auth/store/useAuthStore.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/app/providers.tsx
  • service/app/src/entities/auth/model/hooks/useAuthGuard.ts
  • service/app/src/shared/authButton.tsx
  • service/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.ts
  • service/app/src/app/__tests__/helpers/auth.setup.ts
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/entities/auth/api/validateSession.ts
  • service/app/src/entities/auth/store/useAuthStore.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/app/providers.tsx
  • service/app/src/entities/auth/model/hooks/useAuthGuard.ts
  • service/app/src/shared/authButton.tsx
  • service/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.ts
  • service/app/src/app/__tests__/helpers/auth.setup.ts
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/entities/auth/api/validateSession.ts
  • service/app/src/entities/auth/store/useAuthStore.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/app/providers.tsx
  • service/app/src/entities/auth/model/hooks/useAuthGuard.ts
  • service/app/src/shared/authButton.tsx
  • service/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.tsx
  • service/app/src/entities/auth/ui/authProvider.tsx
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/app/providers.tsx
  • service/app/src/shared/authButton.tsx
**/src/entities/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

In the Next.js app workspace, place feature logic under src/entities and 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.ts
  • 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 **/*.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을 사용한 쿠키 삭제는 표준 방식이며, PathSameSite 속성이 원본 쿠키와 일치하여 올바르게 동작합니다.

service/app/src/widgets/components/kakaoLoginButton.tsx (1)

9-23: LGTM!

onClick 핸들러를 제거하고 Next.js Link를 사용한 선언적 네비게이션으로 전환한 것이 적절합니다. 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 컴포넌트가 버튼에서 링크로 변경됨에 따라 테스트 셀렉터를 올바르게 업데이트했습니다. 모든 인터랙션 메서드는 그대로 유지되어 테스트 안정성이 보장됩니다.

Comment on lines 10 to +16
useEffect(() => {
if (!_hasHydrated) return

if (!isAuthenticated) {
if (authStatus === "unknown") return
if (authStatus !== "authenticated") {
alert("로그인이 필요합니다.")
router.replace("/")
}
}, [isAuthenticated, _hasHydrated, router])
}, [authStatus, router])
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find and read the useAuthGuard implementation
find . -path "*/entities/auth/model/hooks/useAuthGuard.ts" -type f | head -5

Repository: 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 -20

Repository: 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.ts

Repository: 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.ts

Repository: 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.tsx

Repository: 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 -20

Repository: 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 5

Repository: 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 5

Repository: 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.tsx

Repository: 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.tsx

Repository: 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 -10

Repository: 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.tsx

Repository: 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.tsx

Repository: 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 -5

Repository: 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.tsx

Repository: JECT-Study/JECT-5-FE

Length of output: 1609


🏁 Script executed:

# Check the middleware implementation
cat -n service/app/src/middleware.ts

Repository: 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 -10

Repository: 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.ts

Repository: 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 -20

Repository: 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.tsx

Repository: 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 -50

Repository: 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 -20

Repository: 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 -20

Repository: 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 -10

Repository: 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 3

Repository: 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 -3

Repository: 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 -5

Repository: 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 -30

Repository: JECT-Study/JECT-5-FE

Length of output: 366


🏁 Script executed:

# Check the getMyGames implementation
cat -n service/app/src/entities/game/api/getMyGames.ts

Repository: 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 -100

Repository: 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.ts

Repository: 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.ts

Repository: 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.tsx

Repository: 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.tsx

Repository: 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.tsx

Repository: 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".

Comment on lines 25 to 34
bootstrap: async () => {
if (get().hasBootstrapped) return
try {
await validateSession()
set({ authStatus: "authenticated" })
} catch (error) {
set({ authStatus: "unauthenticated" })
}
set({ hasBootstrapped: 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 | 🔴 Critical

치명적: 부트스트랩 시 사용자 데이터 미조회

bootstrap()이 성공하면 authStatus"authenticated"로 설정되지만 usernull로 유지됩니다. 페이지 새로고침 후 인증된 사용자는 프로필 이미지와 닉네임 정보 없이 인증 상태만 표시됩니다.

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.

Comment on lines 45 to 48
logout: async () => {
await logout()
set({ authStatus: "unauthenticated", user: null })
},
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

로그아웃 실패 시 상태 불일치 발생 가능

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.

Suggested change
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).

Comment on lines 18 to 33
{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 />
)}
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

초기 인증 상태 처리 개선 필요

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.

Suggested change
{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.

@github-actions
Copy link

github-actions bot commented Dec 31, 2025

Deploy preview for re-creation ready!

✅ Preview
https://re-creation-k4l8njoo6-whdgur5717s-projects.vercel.app

Built with commit 9f3e643.
This pull request is being automatically deployed with vercel-action

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: 1

♻️ Duplicate comments (2)
service/app/src/entities/auth/store/useAuthStore.ts (2)

29-38: 치명적: 부트스트랩 시 사용자 데이터 미조회 (중복)

bootstrap()이 성공해도 usernull로 유지됩니다. 이는 이전 리뷰에서 지적된 치명적 이슈와 동일합니다.


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

📥 Commits

Reviewing files that changed from the base of the PR and between ab55e25 and 57a5753.

📒 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 each entities/<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 미들웨어와 validateSession API 추가, 그리고 AuthStatusAuthUser 타입 정의가 적절합니다. 인증 상태를 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() 메서드는 세션 만료 이벤트 처리를 위해 authStatususer를 모두 정리하는 명확한 헬퍼입니다. AuthProvider에서 session-expired 이벤트 핸들링에 적절히 사용됩니다.


60-67: LGTM: Persist 설정이 부트스트랩 패턴에 적합함

sessionStorage를 사용하고 user만 영속화하는 것은 적절한 설계입니다. authStatushasBootstrapped를 영속화하지 않음으로써 매 페이지 로드 시 세션을 재검증하는 부트스트랩 패턴을 올바르게 구현했습니다.

Comment on lines +34 to +36
} catch (error) {
set({ authStatus: "unauthenticated" })
}
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

주요: 세션 무효 시 사용자 데이터 미삭제

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.

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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 57a5753 and 14bc8ef.

📒 Files selected for processing (7)
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/app/login/page.tsx
  • service/app/src/entities/game/hooks/useGameEntryNavigation.ts
  • service/app/src/shared/lib/saveEntry.ts
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/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.tsx
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/shared/lib/saveEntry.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/entities/game/hooks/useGameEntryNavigation.ts
  • service/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 each entities/<domain> module
Run ESLint using lint scripts; linting is enforced via lint-staged

Files:

  • service/app/src/widgets/components/HomeNavigationRightContent.tsx
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/shared/lib/saveEntry.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/entities/game/hooks/useGameEntryNavigation.ts
  • service/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.tsx
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/shared/lib/saveEntry.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/entities/game/hooks/useGameEntryNavigation.ts
  • service/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.tsx
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/shared/lib/saveEntry.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/entities/game/hooks/useGameEntryNavigation.ts
  • service/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.tsx
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/shared/lib/saveEntry.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/entities/game/hooks/useGameEntryNavigation.ts
  • service/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.tsx
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/shared/lib/saveEntry.ts
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/app/src/entities/game/hooks/useGameEntryNavigation.ts
  • service/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.tsx
  • service/app/src/widgets/GamesNavigation.tsx
  • service/app/src/app/login/kakao/page.tsx
  • service/app/src/widgets/components/kakaoLoginButton.tsx
  • service/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.tsx
  • service/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.js Link 컴포넌트로 교체하고 returnTo prop을 추가하여 로그인 후 리다이렉션을 지원합니다. asChild 패턴을 사용하여 버튼 스타일링을 유지하면서 링크 동작을 위임하는 것은 올바른 접근입니다.

service/app/src/shared/lib/saveEntry.ts (1)

1-22: 키 기반 스토리지 API로 리팩토링

엔트리 저장 로직을 키 기반 API로 리팩토링하여 여러 타입의 엔트리(게임, 인증)를 독립적으로 관리할 수 있게 되었습니다. ENTRY_KEYS 상수를 통해 타입 안전한 키 참조를 제공하고, 브라우저 환경 가드를 적절히 구현했습니다.

@github-actions
Copy link

테스트 실패

테스트가 실패했습니다. Actions 로그를 확인해주세요.

@github-actions
Copy link

테스트 실패

테스트가 실패했습니다. Actions 로그를 확인해주세요.

1 similar comment
@github-actions
Copy link

테스트 실패

테스트가 실패했습니다. Actions 로그를 확인해주세요.

@github-actions
Copy link

github-actions bot commented Jan 1, 2026

테스트 실패

테스트가 실패했습니다. Actions 로그를 확인해주세요.

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: 1

🧹 Nitpick comments (2)
service/app/src/app/login/page.tsx (1)

13-17: Suspense fallback과 KakaoLoginClient의 스피너 스타일이 다릅니다.

Fallback UI는 size-8border-gray-900을 사용하고, KakaoLoginClientsize-32border-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

📥 Commits

Reviewing files that changed from the base of the PR and between f9aaa86 and 9f3e643.

📒 Files selected for processing (2)
  • service/app/src/app/login/kakaoLoginClient.tsx
  • service/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.tsx
  • service/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 each entities/<domain> module
Run ESLint using lint scripts; linting is enforced via lint-staged

Files:

  • service/app/src/app/login/kakaoLoginClient.tsx
  • service/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.tsx
  • service/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.tsx
  • service/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.tsx
  • service/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.tsx
  • service/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.tsx
  • service/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.tsx
  • service/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"
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

프로덕션 코드에서 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.

@kimnamheeee kimnamheeee merged commit ac3ca47 into dev Jan 1, 2026
2 checks passed
@kimnamheeee kimnamheeee changed the title Fix authentication 인증 로직 수정 Jan 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants