Skip to content

[FE] SISC1-214 [FEAT] : 로그아웃#150

Merged
DongEun02 merged 6 commits intomainfrom
SISC1-214-FE-logout-api
Nov 27, 2025
Merged

[FE] SISC1-214 [FEAT] : 로그아웃#150
DongEun02 merged 6 commits intomainfrom
SISC1-214-FE-logout-api

Conversation

@DongEun02
Copy link
Contributor

@DongEun02 DongEun02 commented Nov 27, 2025

1) 작업한 이슈번호

SISC1-214

2) 변경 요약 (What & Why)

  • 무엇을 변경했는지: 로그아웃 기능 추가, 사이드바 로그인/로그아웃, 회원가입/마이페이지 토글, 베팅/마이페이지 로그인 정보 없을 시 경고 후 로그인 페이지로 이동
  • 변경했는지(문제/목표):

3) 스크린샷/동영상 (UI 변경 시)

전/후 비교, 반응형(모바일/데스크톱) 캡쳐

  • Before:
  • After:

4) 상세 변경사항 (전부 다)

  • 라우팅/페이지:
  • 컴포넌트:
  • 상태관리:
  • API 호출:
  • 스타일:
  • 기타:

5) 참고사항

출석, 게시판, 백테스팅, 퀀트봇 페이지는 출동 위험이 있어 로그인 여부 검사 로직을 추가하지 않았습니다.

Summary by CodeRabbit

  • 새로운 기능

    • 사이드바에 로그인 상태 기반 메뉴(마이페이지/로그아웃 또는 로그인/회원가입) 추가 및 로그아웃 동작 구현
  • 보안·접근 제어

    • 마이페이지와 스톡게임에 인증 가드 적용 — 미인증 시 로그인으로 자동 리다이렉트 및 경고 알림
  • UI

    • 전역 토스트 알림 컨테이너 추가 및 위치·자동닫힘 등 동작 설정
  • 오류 처리

    • 로그아웃 시 실패 알림 표시 및 항상 토큰 제거/로그아웃 처리

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

@DongEun02 DongEun02 requested a review from gxuoo as a code owner November 27, 2025 13:22
@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

Walkthrough

사이드바에 토큰 기반 로그아웃 동작과 로그인 상태 추적을 추가하고, 새 훅 useAuthGuard를 도입해 MypageStockGame 진입 시 로컬스토리지의 accessToken이 없으면 /login으로 리다이렉트합니다. 또한 앱 루트에 ToastContainer를 추가했습니다.

Changes

Cohort / File(s) 변경 요약
사이드바 — 인증 상태 및 로그아웃
frontend/src/components/Sidebar.jsx
../utils/axiosapireact-toastifytoast 임포트. localStorage accessToken 기반 isLoggedIn 상태 추가. logout() 구현: 토큰 읽기 → api.post('/logout') 호출(Authorization: Bearer) → 에러 시 toast.error 표시 → finally에서 토큰 제거, 상태 false 설정, navigate('/'), 로그아웃 성공 toast 표시. 계정 메뉴를 로그인 여부에 따라 조건부 렌더링(마이페이지/로그아웃 ↔ 로그인/회원가입).
인증 가드 훅 추가
frontend/src/hooks/useAuthGuard.js
새 훅 useAuthGuard 추가: 마운트 시 localStorage의 accessToken 확인, 없으면 toast.error(한국어) 후 navigate('/login') 호출. export const useAuthGuard.
페이지들 — 가드 적용
frontend/src/pages/Mypage.jsx, frontend/src/pages/StockGame.jsx
컴포넌트 바디에서 useAuthGuard() 호출 추가(렌더링 구조 변경 없음).
글로벌 토스트 컨테이너 추가
frontend/src/App.jsx
ToastContainer와 CSS 임포트 및 애플리케이션 루트에 ToastContainer 삽입(상단 중앙, autoClose 2000ms, pauseOnHover 등 설정).

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant Sidebar as Sidebar 컴포넌트
    participant Storage as localStorage
    participant API as 인증 서버
    participant Router as 라우터

    User->>Sidebar: 로그아웃 클릭
    Sidebar->>Storage: accessToken 읽기
    Sidebar->>API: POST /logout (Authorization: Bearer <token>)
    API-->>Sidebar: 응답 (성공/에러)
    alt 응답 수신 (성공 또는 에러)
        Sidebar->>Storage: accessToken, refreshToken 제거
        Sidebar->>Sidebar: isLoggedIn = false
        Sidebar->>Router: navigate("/")
        Sidebar-->>User: toast로 로그아웃 알림
    end
Loading
sequenceDiagram
    participant User as 사용자
    participant Page as Mypage / StockGame
    participant Storage as localStorage
    participant Router as 라우터
    participant Login as /login 페이지

    User->>Page: 페이지 접근
    Page->>Storage: accessToken 확인 (useAuthGuard)
    alt 토큰 없음
        Page-->>User: toast.error(한국어) 표시
        Page->>Router: navigate("/login")
        Router->>Login: 리다이렉트
    else 토큰 존재
        Page->>Page: 정상 렌더링
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주의점:
    • Sidebar.logout()의 네트워크 에러 처리와 finally 흐름(토큰 삭제/내비게이션) 검증
    • localStorage 접근의 null/타입 안전성 및 훅이 실행되는 시점(클라이언트 전용 보장)
    • useAuthGuard에서 중복된 toast 알림 발생 가능성(여러 페이지 진입 시)

Possibly related PRs

Suggested reviewers

  • gxuoo

🐰 토큰 털고 달려가네,
문은 닫히고 알림은 반짝,
가드가 길목을 지키니,
다시 올 땐 자격을 가져와,
당근 한 움큼으로 축하해요 🥕✨

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 풀 리퀘스트 제목은 로그아웃 기능 추가라는 주요 변경사항을 명확하게 요약하고 있으며, 제공된 코드 변경사항과 완벽하게 일치합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-214-FE-logout-api

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a19b0cd and 36c834a.

📒 Files selected for processing (1)
  • frontend/src/components/Sidebar.jsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/Sidebar.jsx

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

♻️ Duplicate comments (1)
frontend/src/pages/Mypage.jsx (1)

9-15: 동일한 문제: 의존성 누락 및 코드 중복.

StockGame.jsx와 동일한 문제가 있습니다:

  1. useEffect 의존성 배열에 nav가 누락되었습니다.
  2. 동일한 로그인 가드 로직이 반복됩니다.

StockGame.jsx에 제안한 것과 동일하게 커스텀 훅으로 추출하여 두 파일 모두에서 재사용하세요:

  const Mypage = () => {
-   const nav = useNavigate();
-   useEffect(() => {
-     if (!localStorage.getItem('accessToken')) {
-       alert('로그인 후 이용하실 수 있습니다.');
-       nav('/login');
-     }
-   }, []);
+   useAuthGuard();
🧹 Nitpick comments (1)
frontend/src/components/Sidebar.jsx (1)

169-172: NavLinkto 속성이 로그아웃 동작과 불일치.

로그아웃 링크가 to="/"로 설정되어 있지만, 실제로는 onClick 핸들러에서 navigate('/')를 호출합니다. 이는 약간 중복적이며, 사용자가 링크를 중간 클릭하거나 우클릭하여 새 탭에서 열 경우 로그아웃 없이 홈으로 이동할 수 있습니다.

다음 diff를 적용하여 개선하세요:

  <li>
-   <NavLink
-     to="/"
-     className={styles['inactive-link']}
-     onClick={logout}
-   >
+   <button
+     className={styles['inactive-link']}
+     onClick={logout}
+     style={{ background: 'none', border: 'none', cursor: 'pointer', textAlign: 'left', width: '100%' }}
+   >
      로그아웃
-   </NavLink>
+   </button>
  </li>

또는 이벤트를 막으려면:

  <NavLink
    to="/"
    className={styles['inactive-link']}
-   onClick={logout}
+   onClick={(e) => {
+     e.preventDefault();
+     logout();
+   }}
  >
    로그아웃
  </NavLink>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec77fba and bdbb4c1.

📒 Files selected for processing (3)
  • frontend/src/components/Sidebar.jsx (3 hunks)
  • frontend/src/pages/Mypage.jsx (1 hunks)
  • frontend/src/pages/StockGame.jsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/pages/StockGame.jsx (2)
frontend/src/pages/Mypage.jsx (1)
  • nav (9-9)
frontend/src/pages/Attendance.jsx (1)
  • nav (15-15)
frontend/src/pages/Mypage.jsx (2)
frontend/src/pages/StockGame.jsx (1)
  • nav (10-10)
frontend/src/pages/Attendance.jsx (1)
  • nav (15-15)
🔇 Additional comments (4)
frontend/src/pages/StockGame.jsx (1)

4-5: 임포트 추가 확인.

useEffectuseNavigate 임포트가 적절하게 추가되었습니다.

frontend/src/pages/Mypage.jsx (1)

5-6: 임포트 추가 확인.

useEffectuseNavigate 임포트가 적절하게 추가되었습니다.

frontend/src/components/Sidebar.jsx (2)

4-4: API 임포트 확인.

axios 유틸리티에서 api 임포트가 적절하게 추가되었습니다.


153-206: 조건부 렌더링 구현 확인.

로그인 상태에 따른 계정 메뉴의 조건부 렌더링이 적절하게 구현되었습니다. 로그인 시 마이페이지와 로그아웃, 비로그인 시 로그인과 회원가입 링크가 표시됩니다.

다만 Lines 27-29에서 언급한 상태 동기화 문제로 인해, 다른 탭에서 로그인/로그아웃 시 이 UI가 자동으로 업데이트되지 않을 수 있습니다.

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)
frontend/src/hooks/useAuthGuard.js (1)

7-12: 마운트 시점에만 인증 체크가 수행됩니다.

현재 구현은 컴포넌트 마운트 시 한 번만 토큰을 확인합니다. 사용자가 페이지에 머무는 동안 토큰이 삭제되거나 만료되어도 리디렉션이 발생하지 않습니다.

현재 구현도 기본적인 route guard로 충분히 작동하지만, 더 강력한 보호가 필요하다면 다음을 고려하세요:

  • 토큰 만료 시간 검증
  • storage 이벤트 리스너로 다른 탭에서의 로그아웃 감지
  • 주기적인 토큰 유효성 검증

선택적으로 다음과 같이 개선할 수 있습니다:

  useEffect(() => {
+   const checkAuth = () => {
+     if (!localStorage.getItem('accessToken')) {
+       alert('로그인 후 이용하실 수 있습니다.');
+       nav('/login');
+     }
+   };
+   
+   checkAuth();
+   
+   // 다른 탭에서 로그아웃 시 감지
+   window.addEventListener('storage', checkAuth);
+   return () => window.removeEventListener('storage', checkAuth);
-   if (!localStorage.getItem('accessToken')) {
-     alert('로그인 후 이용하실 수 있습니다.');
-     nav('/login');
-   }
  }, [nav]);
frontend/src/components/Sidebar.jsx (1)

169-177: 로그아웃 링크에서 중복 네비게이션이 발생합니다.

Line 171의 to="/"와 Line 173의 onClick={logout} (내부에서 navigate('/') 호출)가 중복으로 홈으로 이동시킵니다.

현재도 정상 작동하지만, 다음과 같이 개선하면 더 명확합니다:

방법 1: to 속성 제거

  <li>
    <NavLink
-     to="/"
+     to="#"
      className={styles['inactive-link']}
      onClick={logout}
    >
      로그아웃
    </NavLink>
  </li>

방법 2: preventDefault 사용

  <li>
    <NavLink
      to="/"
      className={styles['inactive-link']}
-     onClick={logout}
+     onClick={(e) => {
+       e.preventDefault();
+       logout();
+     }}
    >
      로그아웃
    </NavLink>
  </li>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdbb4c1 and c17f727.

📒 Files selected for processing (4)
  • frontend/src/components/Sidebar.jsx (3 hunks)
  • frontend/src/hooks/useAuthGuard.js (1 hunks)
  • frontend/src/pages/Mypage.jsx (1 hunks)
  • frontend/src/pages/StockGame.jsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/pages/StockGame.jsx
  • frontend/src/pages/Mypage.jsx
🔇 Additional comments (3)
frontend/src/hooks/useAuthGuard.js (1)

1-13: 전반적인 구조가 명확합니다.

인증 가드 훅의 구현이 간결하고 목적에 부합합니다. 기본적인 클라이언트 사이드 라우트 보호 기능을 잘 수행합니다.

frontend/src/components/Sidebar.jsx (2)

27-29: Boolean 변환 개선 완료, 크로스탭 동기화는 여전히 누락.

!!localStorage.getItem('accessToken')로 변경하여 타입 일관성 문제가 해결되었습니다.

다만 이전 리뷰에서 제안된 storage 이벤트 리스너를 통한 크로스탭 동기화는 아직 구현되지 않았습니다. 다른 탭에서 로그인/로그아웃 시 이 컴포넌트의 상태가 자동으로 업데이트되지 않습니다. 이미 제안된 사항이므로 우선순위에 따라 적용 여부를 결정하시면 됩니다.


154-207: 조건부 렌더링 로직이 명확하고 일관성 있습니다.

인증 상태에 따른 계정 섹션 메뉴 항목 표시가 깔끔하게 구현되었습니다. NavLink의 isActive를 활용한 스타일링도 다른 메뉴 항목들과 일관성을 유지하고 있습니다.

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 (2)
frontend/src/components/Sidebar.jsx (2)

28-30: isLoggedIn 불린 초기화는 개선 완료, 스토리지 변경 동기화는 추후 고려 가능

!!localStorage.getItem('accessToken')로 초기화한 부분 덕분에 상태 타입이 명확해진 점 좋습니다.

다만 여전히 다른 탭에서 로그인/로그아웃을 했을 때 이 컴포넌트의 상태가 자동으로 갱신되지는 않으니, 필요하다면 storage 이벤트를 통해 동기화를 거는 것도 고려해 볼 수 있습니다 (필수는 아니고 선택 사항).

useEffect(() => {
  const handleStorageChange = () => {
    setIsLoggedIn(!!localStorage.getItem('accessToken'));
  };

  window.addEventListener('storage', handleStorageChange);
  return () => window.removeEventListener('storage', handleStorageChange);
}, []);

32-53: 로그아웃 실패 시 오류/성공 토스트가 동시에 떠서 UX가 헷갈릴 수 있습니다

현재 구현에서는 로그아웃 API 실패 시:

  1. catch에서 toast.error('오류가 발생했습니다.')가 뜨고
  2. finally에서 항상 toast.success('로그아웃 되었습니다.')가 한 번 더 뜹니다.

사용자 입장에서는 실패인지 성공인지 판단하기 어려운 흐름이라, 실패 시에는 한 종류의 메시지만 보이는 게 더 자연스러워 보입니다.

예를 들어 아래처럼 에러 여부 플래그를 두고 성공 토스트를 조건부로 띄우면 UX가 개선됩니다:

  const logout = async () => {
     const accessToken = localStorage.getItem('accessToken');
+    let isError = false;
     try {
       await api.post(
         '/api/auth/logout',
         {},
         {
           headers: {
             Authorization: `Bearer ${accessToken}`,
           },
         }
       );
     } catch {
-      toast.error('오류가 발생했습니다.');
+      isError = true;
+      toast.error('오류가 발생했습니다.');
     } finally {
       localStorage.removeItem('accessToken');
       localStorage.removeItem('refreshToken');
       setIsLoggedIn(false);
       navigate('/');
-      toast.success('로그아웃 되었습니다.');
+      if (!isError) {
+        toast.success('로그아웃 되었습니다.');
+      }
     }
   };

원하신다면 실패 케이스에서 "서버 통신에는 문제가 있었지만 로컬 로그아웃은 완료되었습니다." 같은 문구로 단일 토스트만 노출하는 방향도 고려해 볼 수 있습니다.

🧹 Nitpick comments (2)
frontend/src/App.jsx (1)

23-25: ToastContainer 전역 배치 좋습니다. (선택 사항: 중복 토스트 제한 옵션 고려)

루트(App) 레벨에 ToastContainer를 둔 선택이 적절해서, useAuthGuard나 사이드바 로그아웃에서 발생하는 토스트가 모든 라우트에서 잘 표시될 것 같습니다.

다만, 인증 관련 토스트가 짧은 시간 안에 여러 번 떠서 UX가 번잡해질 수 있으니, 필요하다면 limit 등의 옵션으로 동시에 보여줄 토스트 개수를 제한하는 것도 한 번 고려해 보세요.

       <ToastContainer
         position="top-center"
         autoClose={2000}
         hideProgressBar={false}
         pauseOnHover
         theme="light"
+        limit={2}
       />

Also applies to: 50-56

frontend/src/components/Sidebar.jsx (1)

155-208: 계정 섹션 조건부 렌더링은 적절하며, 로그아웃은 버튼 시맨틱도 고려 가능

isLoggedIn에 따라 ‘마이페이지/로그아웃’ vs ‘로그인/회원가입’을 토글하는 구조는 직관적이고, 전체 UX 흐름도 목적에 잘 맞습니다.

다만 로그아웃 항목은 실제로는 “페이지 이동”보다는 “액션(상태 변경)”에 가까워서, 접근성/시맨틱을 신경 쓴다면 NavLink 대신 <button> 또는 역할이 명확한 요소로 구현하고, 스타일만 링크처럼 맞추는 방안도 고려해 볼 수 있습니다. 필수는 아니고, 팀 스타일 가이드를 보고 결정하셔도 될 것 같습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c17f727 and a19b0cd.

📒 Files selected for processing (3)
  • frontend/src/App.jsx (2 hunks)
  • frontend/src/components/Sidebar.jsx (3 hunks)
  • frontend/src/hooks/useAuthGuard.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/hooks/useAuthGuard.js
🔇 Additional comments (1)
frontend/src/components/Sidebar.jsx (1)

4-5: axios 래퍼와 toast import 방향 괜찮습니다

공용 api 인스턴스와 react-toastifytoast를 여기서 직접 사용하는 구조는 사이드바의 로그아웃 책임에 잘 맞습니다. 별도 우려되는 부분은 없습니다.

Copy link
Contributor

@gxuoo gxuoo left a comment

Choose a reason for hiding this comment

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

한 가지 코멘트 드렸습니다!

@DongEun02 DongEun02 merged commit 0281bfb into main Nov 27, 2025
1 check passed
@DongEun02 DongEun02 deleted the SISC1-214-FE-logout-api branch November 27, 2025 14:53
@coderabbitai coderabbitai bot mentioned this pull request Jan 29, 2026
22 tasks
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