Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bb5777f
chore: zustand 라이브러리 추가
kwonsaebom Sep 12, 2025
467589a
feat: 유저 & 함수 타입 지정
kwonsaebom Sep 12, 2025
3969e9f
feat: zuestand 기반 useAuthStore 구현
kwonsaebom Sep 12, 2025
5a640d9
chore: 함수 이름 통일
kwonsaebom Sep 12, 2025
02c7186
refactor: 중복된 타입 파일 제거
kwonsaebom Sep 12, 2025
d9af138
feat: useAuthStore 훅 적용
kwonsaebom Sep 12, 2025
45593c1
fix: 에러 수정
kwonsaebom Sep 12, 2025
4c11908
feat: userModal 컴포넌트에 user 상태 적용
kwonsaebom Sep 12, 2025
769697e
feat: 상태 관리에 isLoggedIn 추가
kwonsaebom Sep 15, 2025
1575b23
feat: 테스트 코드
kwonsaebom Sep 15, 2025
5e1ec24
refactor: user의 기본 값 변경
kwonsaebom Sep 15, 2025
14d8eb4
feat: 분기 처리 조건 수정
kwonsaebom Sep 15, 2025
1e48f00
refactor: 유저 데이터가 존재할 때만 로컬 스토리지에 저장하도록 수정
kwonsaebom Sep 15, 2025
bda0d57
fix: 린트 에러 해결
kwonsaebom Sep 15, 2025
94f1d18
refactor: 객체 형식으로 상태 useAuthStore 호출
kwonsaebom Sep 17, 2025
839e240
refactor: button으로 img 감싸서 접근성 높이기
kwonsaebom Sep 17, 2025
27f87b6
refactor: 토큰을 기준으로 로그인 기준 처리
kwonsaebom Sep 17, 2025
1def14c
fix: 주석 제거
kwonsaebom Sep 17, 2025
8a711b3
feat: 목표 작성 상태에 따른 분기 처리
kwonsaebom Sep 17, 2025
193ddb4
feat: 리프레시 토큰 발급 추가 & axiosInstance 응답 변경
kwonsaebom Sep 17, 2025
44b39f3
fix: 충돌 해결
kwonsaebom Sep 17, 2025
2e641c6
Merge branch 'develop' into feat/#187/loginState
kwonsaebom Sep 19, 2025
38d38a8
fix: 리뷰 수정
kwonsaebom Sep 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.6.3"
"react-router-dom": "^7.6.3",
"zustand": "^5.0.8"
},
"devDependencies": {
"@chromatic-com/storybook": "^4.0.1",
Expand Down
28 changes: 28 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/api/domain/signup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { END_POINT } from '@/api/constant/endPoint';
import type { JobResponse } from '@/api/domain/signup/type/JobResponse';
import type { PersonaResponse } from '@/api/domain/signup/type/PersonaResponse';
import type { SignupResponse } from '@/api/domain/signup/type/SignupResponse';
import type { UserInfoResponse } from '@/api/domain/signup/type/UserInfoResponse';
import type { UserType } from '@/store/types/authTypes';
import type { BaseResponse } from '@/type/api';

export const getJobList = async () => {
Expand All @@ -21,7 +21,7 @@ export const postSignUp = async (payload: SignupResponse) => {
};

export const getUser = async () => {
const { data } = await axiosInstance.get<BaseResponse<UserInfoResponse>>('/users/info');
const { data } = await axiosInstance.get<BaseResponse<UserType>>('/users/info');
return data.data;
};

Expand Down
6 changes: 0 additions & 6 deletions src/api/domain/signup/type/UserInfoResponse.ts

This file was deleted.

15 changes: 11 additions & 4 deletions src/common/component/UserModal/UserModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@

import { IcDivider } from '@/assets/svg';
import * as styles from '@/common/component/UserModal/UserModal.css';
import { useGetUser } from '@/api/domain/signup/hook/useGetUser';
import { usePostLogout } from '@/api/domain/signup/hook/usePostLogout';
import { useAuthStore } from '@/store/useAuthStore';

Check warning on line 6 in src/common/component/UserModal/UserModal.tsx

View workflow job for this annotation

GitHub Actions / build

There should be at least one empty line between import groups

Check warning on line 6 in src/common/component/UserModal/UserModal.tsx

View workflow job for this annotation

GitHub Actions / build

There should be at least one empty line between import groups
import { useNavigate } from 'react-router-dom';

Check warning on line 7 in src/common/component/UserModal/UserModal.tsx

View workflow job for this annotation

GitHub Actions / build

`react-router-dom` import should occur before import of `@/assets/svg`

Check warning on line 7 in src/common/component/UserModal/UserModal.tsx

View workflow job for this annotation

GitHub Actions / build

There should be at least one empty line between import groups

Check warning on line 7 in src/common/component/UserModal/UserModal.tsx

View workflow job for this annotation

GitHub Actions / build

`react-router-dom` import should occur before import of `@/assets/svg`

Check warning on line 7 in src/common/component/UserModal/UserModal.tsx

View workflow job for this annotation

GitHub Actions / build

There should be at least one empty line between import groups
import { PATH } from '@/route';

interface UserModalProps {
onClose: () => void;
}

const UserModal = ({ onClose }: UserModalProps) => {
const modalRef = useRef<HTMLDivElement>(null);
const { data: user, isLoading, isError } = useGetUser();
const navigate = useNavigate();

const user = useAuthStore((state) => state.user);
const resetUser = useAuthStore((state) => state.resetUser);

const { mutate: logoutMutate } = usePostLogout();

const handleClickOutside = (e: MouseEvent) => {
Expand All @@ -24,8 +30,9 @@
logoutMutate(undefined, {
onSuccess: () => {
localStorage.removeItem('accessToken');
resetUser();
onClose();
window.location.reload();
navigate(PATH.ROOT);
},
onError: (error) => {
console.error('로그아웃 실패:', error);
Expand All @@ -41,7 +48,7 @@
};
}, []);

if (isLoading || isError || !user) {
if (!user) {
return null;
}

Expand Down
7 changes: 5 additions & 2 deletions src/page/callback/GoogleCallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ const GoogleCallback = () => {
state: { isWritten: userData.onboardingCompleted },
});
} else {
navigate(PATH.SIGNUP, {
state: { userData },
navigate(PATH.INTRO, {
state: { isWritten: userData.onboardingCompleted },
});
// navigate(PATH.SIGNUP, {
// state: { userData },
// });
}
}, [userData, navigate]);

Expand Down
43 changes: 29 additions & 14 deletions src/shared/component/Layout/layoutHeader/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useNavigate, useLocation, Link } from 'react-router-dom';

import * as styles from './Header.css';
Expand All @@ -9,6 +9,7 @@
import { useModal } from '@/common/hook/useModal';
import { useGetUser } from '@/api/domain/signup/hook/useGetUser';
import UserModal from '@/common/component/UserModal/UserModal';
import { useAuthStore } from '@/store/useAuthStore';

const MENUS = [
{ label: '나의 할 일', path: PATH.TODO },
Expand All @@ -20,7 +21,13 @@
const navigate = useNavigate();
const location = useLocation();

const { data: user, isLoading } = useGetUser();
const { data: userData, isLoading } = useGetUser();

const setUser = useAuthStore((state) => state.setUser);
const resetUser = useAuthStore((state) => state.resetUser);

const user = useAuthStore((state) => state.user);
const isLoggedIn = useAuthStore((state) => state.isLoggedIn);

const findActiveMenu = MENUS.find((menu) => location.pathname.startsWith(menu.path));
const initialMenu = findActiveMenu ? findActiveMenu.label : '';
Expand All @@ -30,6 +37,18 @@

const { openModal, closeModal, ModalWrapper } = useModal();

useEffect(() => {
if (isLoading) {
return;
}

if (userData) {
setUser(userData);
} else {
resetUser();
}
}, [userData, setUser, resetUser]);

Check warning on line 50 in src/shared/component/Layout/layoutHeader/Header.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'isLoading'. Either include it or remove the dependency array

Check warning on line 50 in src/shared/component/Layout/layoutHeader/Header.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useEffect has a missing dependency: 'isLoading'. Either include it or remove the dependency array

const handleLogin = () => {
openModal(<LoginModal onClose={closeModal} />);
};
Expand Down Expand Up @@ -62,17 +81,13 @@
);
})}
</nav>
{!isLoading && user && (
<>
<img
src={user.profileImageUrl}
alt="유저 프로필 이미지"
className={styles.profilePlaceholder}
onClick={handleProfile}
/>
{openProfile && <UserModal onClose={handleProfile} />}
</>
)}
<img
src={user.profileImageUrl}
alt="유저 프로필 이미지"
className={styles.profilePlaceholder}
onClick={handleProfile}
/>
{openProfile && <UserModal onClose={handleProfile} />}
</>
);

Expand All @@ -83,7 +98,7 @@
<IcLogo className={styles.logoImage} />
</Link>

{!isLoading && user ? (
{isLoggedIn ? (
renderNavMenu()
) : (
<button className={styles.loginButton} onClick={handleLogin} type="button">
Expand Down
21 changes: 21 additions & 0 deletions src/store/types/authTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export interface AnswerType {
questionId: number;
choiceId: number;
}

export interface UserType {
id?: number;
name: string;
email: string;
birthday?: string;
job?: string;
profileImageUrl: string;
answers?: AnswerType[];
}

export interface AuthStoreType {
user: UserType;
isLoggedIn: boolean;
setUser: (newUser: UserType) => void;
resetUser: () => void;
}
37 changes: 37 additions & 0 deletions src/store/useAuthStore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { create } from 'zustand';
import { createJSONStorage, persist } from 'zustand/middleware';

import type { AuthStoreType, UserType } from '@/store/types/authTypes';

const defaultUser: UserType = {
name: '',
email: '',
profileImageUrl: '',
};
const token = localStorage.getItem('accessToken');

export const useAuthStore = create<AuthStoreType>()(
persist(
(set) => ({
user: defaultUser,
isLoggedIn: !!token,
setUser: (newUser) => set({ user: newUser, isLoggedIn: true }),
resetUser: () => set({ user: defaultUser, isLoggedIn: false }),
}),
{
name: 'auth-storage',
storage: createJSONStorage(() => ({
getItem: (name) => localStorage.getItem(name),
setItem: (name, value) => {
const { state } = JSON.parse(value) as { state: AuthStoreType };
if (!state.isLoggedIn) {
localStorage.removeItem(name);
return;
}
localStorage.setItem(name, value);
},
removeItem: (name) => localStorage.removeItem(name),
})),
},
),
);