Skip to content

Conversation

@Chuseok22
Copy link
Member

@Chuseok22 Chuseok22 commented Sep 17, 2025

Summary by CodeRabbit

  • 신기능
    • 로그아웃 확인 모달 추가: 한국어 안내, 취소/확인 버튼, 로딩 상태 표시로 실수 방지.
    • 재사용 가능한 알림 대화상자 UI 컴포넌트 세트 도입(트리거, 헤더/푸터, 제목/설명, 액션/취소, 오버레이/포털 등)으로 일관된 스타일 제공.
  • 리팩터링
    • 사이드바 로그아웃 흐름을 확인 모달 기반으로 변경해 사용자 확인 후에만 로그아웃 수행.
  • 기타 작업
    • Radix UI AlertDialog 패키지 의존성 추가.

@coderabbitai
Copy link

coderabbitai bot commented Sep 17, 2025

Walkthrough

로그아웃 확인 다이얼로그 기능을 도입했다. Radix UI AlertDialog 의존성을 추가하고, 공용 AlertDialog UI 래퍼를 생성했다. 로그아웃 전 확인 모달 컴포넌트(LogoutAlertDialog)를 추가하고, AppSidebar에서 직접 로그아웃 호출을 확인 다이얼로그 기반 플로우로 변경했다.

Changes

Cohort / File(s) Summary
Dependency update
package.json
@radix-ui/react-alert-dialog 의존성 추가
UI primitives: AlertDialog
src/components/ui/alert-dialog.tsx
Radix AlertDialog 프리미티브를 감싼 UI 컴포넌트 세트 추가(Portal/Overlay/Content/Header/Footer/Title/Description/Action/Cancel 등)
Feature: Logout confirmation dialog
src/app/(with-layout)/_shared/components/logout-alert-dialog.tsx
확인/취소가 가능한 LogoutAlertDialog 컴포넌트 추가(로딩 상태 반영, 비동기 onConfirm 지원)
Integration: Sidebar logout flow
src/components/common/app-sidebar.tsx
로그아웃을 모달 확인 후 실행하도록 변경; 다이얼로그 상태/핸들러 추가, 버튼 핸들러 교체, 로고 경로 /icons/logo.svg로 수정

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant S as AppSidebar
  participant D as LogoutAlertDialog
  participant A as AuthService
  participant T as TokenStore
  participant R as Router

  U->>S: 클릭: 로그아웃 버튼
  S->>D: open=true (다이얼로그 표시)
  U->>D: 확인(Logout) 클릭
  D->>S: onConfirm 호출
  S->>A: logout()
  activate A
  A-->>S: 완료/성공
  deactivate A
  S->>T: 토큰 삭제/정리
  S->>D: open=false (닫기)
  S->>R: /login로 이동

  alt 취소 선택
    U->>D: 취소(Cancel) 클릭
    D->>S: onOpenChange(false)
    S->>D: open=false
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Poem

작은 창이 톡— “정말 떠날 토끼?”
귀를 쫑긋 세우고 한 번 더 묻기.
확인이면 폴짝, 로그인 숲 밖으로!
취소면 오케이, 굴속에 그대로.
모달은 말했지, 예의가 먼저라고. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 제목은 PR의 핵심 변경점인 "로그아웃 버튼 클릭 시 확인 alert 추가"를 명확히 담고 있어 변경 내용과 일치합니다. 다만 앞의 날짜("20250917")와 이슈 번호("#16"), 'ux' 표기는 메타 정보로 다소 장황해 가독성을 떨어뜨릴 수 있습니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20250917_#16_기능개선_UX_로그아웃_버튼_클릭_시_확인_Alert_추가

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/common/app-sidebar.tsx (1)

56-71: 로그아웃 API 실패 시에도 로컬 세션 종료를 보장하세요.

현재는 API 실패 시 토큰이 남아 세션이 유지될 수 있습니다. 토큰 삭제/리다이렉트를 finally로 이동해 항상 로그아웃되도록 하세요.

   const onConfirmLogout = useCallback(async () => {
     if (loading) {
       return;
     }
     setLoading(true);
     try {
-      await logout();
-      localStorage.removeItem('accessToken');
-      localStorage.removeItem('refreshToken');
-      setDialogOpen(false);
-      router.replace('/login');
+      await logout();
     } finally {
-      setLoading(false);
+      // API 성공/실패와 무관하게 로컬 세션 종료
+      localStorage.removeItem('accessToken');
+      localStorage.removeItem('refreshToken');
+      setDialogOpen(false);
+      router.replace('/login');
+      setLoading(false);
     }
   }, [loading, router]);
🧹 Nitpick comments (4)
src/components/ui/alert-dialog.tsx (3)

121-131: Action/Cancel에 type="button" 지정 권장 (폼 내 오동작 예방).

폼 내부에서 사용될 때 기본 submit을 막기 위해 명시적으로 버튼 타입을 지정해주세요.

 function AlertDialogAction({
   className,
   ...props
 }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
   return (
     <AlertDialogPrimitive.Action
+      type="button"
       className={cn(buttonVariants(), className)}
       {...props}
     />
   )
 }

 function AlertDialogCancel({
   className,
   ...props
 }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
   return (
     <AlertDialogPrimitive.Cancel
+      type="button"
       className={cn(buttonVariants({ variant: "outline" }), className)}
       {...props}
     />
   )
 }

Also applies to: 133-143


47-64: Content는 forwardRef로 감싸는 것이 바람직합니다.

Radix 프리미티브와 일관되게 ref 전달을 지원하면 포커스 관리/측정에 유리합니다. (Title/Description/Action/Cancel도 동일 패턴 적용 권장)

-function AlertDialogContent({
-  className,
-  ...props
-}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
-  return (
-    <AlertDialogPortal>
-      <AlertDialogOverlay />
-      <AlertDialogPrimitive.Content
-        data-slot="alert-dialog-content"
-        className={cn(
-          "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
-          className
-        )}
-        {...props}
-      />
-    </AlertDialogPortal>
-  )
-}
+const AlertDialogContent = React.forwardRef<
+  React.ElementRef<typeof AlertDialogPrimitive.Content>,
+  React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
+>(({ className, ...props }, ref) => (
+  <AlertDialogPortal>
+    <AlertDialogOverlay />
+    <AlertDialogPrimitive.Content
+      ref={ref}
+      data-slot="alert-dialog-content"
+      className={cn(
+        "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
+        className
+      )}
+      {...props}
+    />
+  </AlertDialogPortal>
+));
+AlertDialogContent.displayName = "AlertDialogContent";

9-13: Root에 부여한 data-slot은 DOM에 출력되지 않습니다.

AlertDialogPrimitive.Root는 DOM을 렌더하지 않아서 테스트 셀렉터로 동작하지 않습니다. Content/Overlaydata-slot을 기준으로 테스트하는 것을 권장합니다.

src/app/(with-layout)/_shared/components/logout-alert-dialog.tsx (1)

21-21: 로딩 중에는 다이얼로그가 닫히지 않도록 onOpenChange에 가드 추가 권장.

AlertDialogAction 클릭 시 기본적으로 닫힘 이벤트가 발생합니다. 로딩 상태에서 닫힘을 무시하면 UX가 안정적입니다.

-      <AlertDialog open={props.open} onOpenChange={props.onOpenChange}>
+      <AlertDialog
+        open={props.open}
+        onOpenChange={(next) => {
+          if (!props.loading) props.onOpenChange(next)
+        }}
+      >
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc614b and be05ceb.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • package.json (1 hunks)
  • src/app/(with-layout)/_shared/components/logout-alert-dialog.tsx (1 hunks)
  • src/components/common/app-sidebar.tsx (3 hunks)
  • src/components/ui/alert-dialog.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/components/common/app-sidebar.tsx (1)
src/app/(with-layout)/_shared/components/logout-alert-dialog.tsx (1)
  • LogoutAlertDialog (19-38)
src/app/(with-layout)/_shared/components/logout-alert-dialog.tsx (1)
src/components/ui/alert-dialog.tsx (8)
  • AlertDialog (146-146)
  • AlertDialogContent (150-150)
  • AlertDialogHeader (151-151)
  • AlertDialogTitle (153-153)
  • AlertDialogDescription (154-154)
  • AlertDialogFooter (152-152)
  • AlertDialogCancel (156-156)
  • AlertDialogAction (155-155)
src/components/ui/alert-dialog.tsx (1)
src/lib/utils.ts (1)
  • cn (4-6)
🔇 Additional comments (3)
package.json (1)

13-13: LGTM — 피어 의존성(React 19) 호환 확인됨

package.json의 peerDependencies에 'react'/'react-dom'이 ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc로 선언되어 React 19을 허용하므로 Next 15(React 19 사용)과 호환됩니다.

  • PR에 lockfile(pnpm-lock.yaml / yarn.lock / package-lock.json) 업데이트 포함 여부 확인 필요.
src/components/common/app-sidebar.tsx (2)

78-79: 로고 파일(public/icons/logo.svg) 존재 여부 확인 필요

src/components/common/app-sidebar.tsx(78–79행)의 Image src가 '/icons/logo.svg'로 설정되어 있습니다. 저장소에 public/icons/logo.svg 파일이 있는지 확인하세요. 누락 시 빌드/런타임에 404가 발생합니다. 실행한 검색 명령에서 결과가 없어 직접 확인이 필요합니다.


64-65: 토큰 키 네이밍 일관성 확인 — 문제 없음. 앱 전역에서 'accessToken' / 'refreshToken' 키를 일관되게 사용합니다 (예: src/app/(without-layout)/login/_shared/components/login-form.tsx, src/app/(without-layout)/login/_shared/services/login-api.ts, src/app/api/login/route.ts, src/middleware.ts, src/components/common/app-sidebar.tsx).

@Chuseok22 Chuseok22 merged commit 06eb45f into main Sep 17, 2025
2 checks passed
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.

🚀 [기능개선][UX] 로그아웃 버튼 클릭 시 확인 Alert 추가

2 participants