diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx
index 19d8173..f22bf8d 100644
--- a/src/app/community/page.tsx
+++ b/src/app/community/page.tsx
@@ -1,4 +1,3 @@
-
import CommunityFilter from '@/domains/community/main/CommunityFilter';
import CommunityTab from '@/domains/community/main/CommunityTab';
import PostCard from '@/domains/community/main/PostCard';
diff --git a/src/app/design-system/page.tsx b/src/app/design-system/page.tsx
index ca12035..048b3f6 100644
--- a/src/app/design-system/page.tsx
+++ b/src/app/design-system/page.tsx
@@ -6,13 +6,13 @@ import Input from '@/shared/components/InputBox/Input';
import { useState } from 'react';
import { customToast } from '@/shared/components/toast/CustomToastUtils';
import ModalLayout from '@/shared/components/modalPop/ModalLayout';
-import ConfirmPop from '@/shared/components/modalPop/ConfirmPop';
import SelectBox from '@/domains/shared/select-box/SelectBox';
import Spinner from '@/shared/components/spinner/Spinner';
import LikeBtn from '@/domains/community/components/like/LikeBtn';
import Share from '@/domains/shared/share/Share';
import Keep from '@/domains/shared/keep/Keep';
+import ConfirmModal from '@/shared/components/modalPop/ConfirmModal';
function Page() {
const [isModalOpen, setModalOpen] = useState(false);
@@ -129,7 +129,7 @@ function Page() {
모달팝업 내용
- setConfirmOpen(false)}
title="Confirm제목"
diff --git a/src/app/login/first-user/page.tsx b/src/app/login/first-user/page.tsx
deleted file mode 100644
index 6a939dc..0000000
--- a/src/app/login/first-user/page.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import LoginRedirectHandler from '@/domains/shared/auth/LoginRedirectHandler';
-
-function Page() {
- return ;
-}
-
-export default Page;
diff --git a/src/app/login/success/page.tsx b/src/app/login/success/page.tsx
index 25b93a4..48cef7d 100644
--- a/src/app/login/success/page.tsx
+++ b/src/app/login/success/page.tsx
@@ -1,4 +1,4 @@
-import LoginRedirectHandler from '@/domains/shared/auth/LoginRedirectHandler';
+import LoginRedirectHandler from '@/domains/login/components/LoginRedirectHandler';
function Page() {
return ;
diff --git a/src/app/login/user/first-user/page.tsx b/src/app/login/user/first-user/page.tsx
new file mode 100644
index 0000000..131a5e1
--- /dev/null
+++ b/src/app/login/user/first-user/page.tsx
@@ -0,0 +1,7 @@
+import LoginRedirectHandler from '@/domains/login/components/LoginRedirectHandler';
+
+function Page() {
+ return ;
+}
+
+export default Page;
diff --git a/src/app/login/user/success/page.tsx b/src/app/login/user/success/page.tsx
new file mode 100644
index 0000000..48cef7d
--- /dev/null
+++ b/src/app/login/user/success/page.tsx
@@ -0,0 +1,6 @@
+import LoginRedirectHandler from '@/domains/login/components/LoginRedirectHandler';
+
+function Page() {
+ return ;
+}
+export default Page;
diff --git a/src/domains/shared/auth/LoginRedirectHandler.tsx b/src/domains/login/components/LoginRedirectHandler.tsx
similarity index 77%
rename from src/domains/shared/auth/LoginRedirectHandler.tsx
rename to src/domains/login/components/LoginRedirectHandler.tsx
index b18d780..79d03b1 100644
--- a/src/domains/shared/auth/LoginRedirectHandler.tsx
+++ b/src/domains/login/components/LoginRedirectHandler.tsx
@@ -2,13 +2,11 @@
import { useEffect, useState } from 'react';
import { usePathname, useRouter } from 'next/navigation';
-
import { customToast } from '@/shared/components/toast/CustomToastUtils';
-
-import WelcomeModal from './WelcomeModal';
-import { getCookie, removeCookie } from './utils/cookie';
-import { useAuthStore } from '../store/auth';
+import { getCookie, removeCookie } from '@/domains/shared/auth/utils/cookie';
+import { useAuthStore } from '@/domains/shared/store/auth';
import Spinner from '@/shared/components/spinner/Spinner';
+import WelcomeModal from '@/domains/login/components/WelcomeModal';
function LoginRedirectHandler() {
const pathname = usePathname();
@@ -29,6 +27,8 @@ function LoginRedirectHandler() {
router.replace('/login');
})
.finally(() => setLoading(false));
+ } else {
+ setLoading(false);
}
}, [user, loading, updateUser, router]);
@@ -36,14 +36,19 @@ function LoginRedirectHandler() {
if (!user || loading) return;
const preLoginPath = getCookie('preLoginPath') || '/';
- console.log(preLoginPath);
+ // 로그인 상태인데 이전 페이지가 /login이면 메인으로 이동
+ if (user && preLoginPath === '/login') {
+ router.replace('/');
+ removeCookie('preLoginPath');
+ return;
+ }
// 첫 유저일 경우 모달 오픈
- if (pathname.startsWith('/login/first-user')) {
+ if (pathname.startsWith('/login/user/first-user')) {
setWelcomeModalOpen(true);
}
// 기존 유저일 경우
- else if (pathname.startsWith('/login/success')) {
+ else if (pathname.startsWith('/login/user/success')) {
customToast.success(`${user.nickname}님 \n 로그인 성공 🎉`);
router.replace(preLoginPath);
removeCookie('preLoginPath');
diff --git a/src/domains/login/components/LogoutConfirm.tsx b/src/domains/login/components/LogoutConfirm.tsx
new file mode 100644
index 0000000..b7f5f26
--- /dev/null
+++ b/src/domains/login/components/LogoutConfirm.tsx
@@ -0,0 +1,20 @@
+import ConfirmModal from '@/shared/components/modalPop/ConfirmModal';
+
+interface Props {
+ open: boolean;
+ onClose: () => void;
+ onLogout: () => void;
+}
+
+function LogoutConfirm({ open, onClose, onLogout }: Props) {
+ return (
+
+ );
+}
+export default LogoutConfirm;
diff --git a/src/domains/shared/auth/WelcomeModal.tsx b/src/domains/login/components/WelcomeModal.tsx
similarity index 100%
rename from src/domains/shared/auth/WelcomeModal.tsx
rename to src/domains/login/components/WelcomeModal.tsx
diff --git a/src/domains/shared/auth/LoginConfirm.tsx b/src/domains/shared/auth/LoginConfirm.tsx
deleted file mode 100644
index efed6f9..0000000
--- a/src/domains/shared/auth/LoginConfirm.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-function LoginConfirm() {
- return LoginConfirm
;
-}
-export default LoginConfirm;
diff --git a/src/domains/shared/store/auth.ts b/src/domains/shared/store/auth.ts
index f29a788..26f0796 100644
--- a/src/domains/shared/store/auth.ts
+++ b/src/domains/shared/store/auth.ts
@@ -1,5 +1,6 @@
import { customToast } from '@/shared/components/toast/CustomToastUtils';
import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
interface User {
id: string;
@@ -21,63 +22,62 @@ interface AuthState {
updateUser: () => Promise;
}
-export const useAuthStore = create((set) => ({
- user: null,
- accessToken: null,
- isLoggedIn: false,
+export const useAuthStore = create()(
+ persist(
+ (set) => ({
+ user: null,
+ accessToken: null,
+ isLoggedIn: false,
- loginWithProvider: (provider) => {
- window.location.href = `http://localhost:8080/oauth2/authorization/${provider}`;
- },
+ loginWithProvider: (provider) => {
+ window.location.href = `http://localhost:8080/oauth2/authorization/${provider}`;
+ },
- setUser: (user, token) => {
- const updatedUser = { ...user, abv_degree: 5.0 };
- set({ user: updatedUser, accessToken: token, isLoggedIn: true });
+ setUser: (user, token) => {
+ const updatedUser = { ...user, abv_degree: 5.0 };
+ set({ user: updatedUser, accessToken: token, isLoggedIn: true });
+ customToast.success(`${updatedUser.nickname}님, 로그인 성공 🎉`);
+ },
- customToast.success(`${updatedUser.nickname}님, 로그인 성공 🎉`);
- },
+ logout: async () => {
+ try {
+ await fetch('http://localhost:8080/user/auth/logout', {
+ method: 'POST',
+ credentials: 'include',
+ });
+ customToast.success('로그아웃 되었습니다.');
+ set({ user: null, accessToken: null, isLoggedIn: false });
+ } catch (err) {
+ customToast.error('로그아웃 실패❌ \n 다시 시도해주세요.');
+ console.error('로그아웃 실패', err);
+ }
+ },
- logout: async () => {
- try {
- await fetch('http://localhost:8080/user/auth/logout', {
- method: 'POST',
- credentials: 'include',
- });
+ updateUser: async () => {
+ try {
+ const res = await fetch('http://localhost:8080/user/auth/refresh', {
+ method: 'POST',
+ credentials: 'include',
+ headers: { 'Content-Type': 'application/json' },
+ });
- customToast.success('로그아웃 되었습니다.');
- set({ user: null, accessToken: null, isLoggedIn: false });
- } catch (err) {
- customToast.error('로그아웃 실패❌ \n 다시 시도해주세요.');
- console.error('로그아웃 실패', err);
- }
- },
+ if (!res.ok) throw new Error('토큰 갱신 실패');
+ const data = await res.json();
+ const userInfo = data?.data?.user;
+ const accessToken = data?.data?.accessToken;
- updateUser: async () => {
- try {
- const res = await fetch('http://localhost:8080/user/auth/refresh', {
- method: 'POST',
- credentials: 'include',
- headers: { 'Content-Type': 'application/json' },
- });
-
- if (!res.ok) throw new Error('토큰 갱신 실패');
- const data = await res.json();
-
- console.log('updateUser response:', data);
- const userInfo = data?.data?.user;
- const accessToken = data?.data?.accessToken;
-
- if (userInfo && accessToken) {
- set({ user: userInfo, accessToken, isLoggedIn: true });
- console.log('토큰 및 유저 정보 갱신 완료:', userInfo);
- return userInfo;
- }
-
- return null;
- } catch (err) {
- console.error('updateUser 실패', err);
- set({ accessToken: null, user: null, isLoggedIn: false });
- return null;
- }
- },
-}));
+ if (userInfo && accessToken) {
+ set({ user: userInfo, accessToken, isLoggedIn: true });
+ return userInfo;
+ }
+ return null;
+ } catch (err) {
+ console.error('updateUser 실패', err);
+ set({ accessToken: null, user: null, isLoggedIn: false });
+ return null;
+ }
+ },
+ }),
+ { name: 'auth-storage' } // localStorage key
+ )
+);
diff --git a/src/shared/components/header/DropdownMenu.tsx b/src/shared/components/header/DropdownMenu.tsx
index 045b707..b333b04 100644
--- a/src/shared/components/header/DropdownMenu.tsx
+++ b/src/shared/components/header/DropdownMenu.tsx
@@ -12,6 +12,7 @@ import gsap from 'gsap';
import { createPortal } from 'react-dom';
import { useAuthStore } from '@/domains/shared/store/auth';
import { setPreLoginPath } from '@/domains/shared/auth/utils/setPreLoginPath';
+import LogoutConfirm from '@/domains/login/components/LogoutConfirm';
interface Props {
isClicked: boolean;
@@ -28,6 +29,7 @@ function DropdownMenu({ isClicked, setIsClicked, visible, setVisible }: Props) {
const [mounted, setMounted] = useState(false);
const { isLoggedIn, logout } = useAuthStore();
+ const [logoutModalOpen, setLogoutModalOpen] = useState(false);
useEffect(() => {
setMounted(true);
@@ -62,92 +64,113 @@ function DropdownMenu({ isClicked, setIsClicked, visible, setVisible }: Props) {
return () => {
// tl.kill();
};
-
}, [isClicked, setVisible]);
if (!mounted) return null;
return createPortal(
-