(QUERY_KEYS.banner())
+ const queryClient = useQueryClient();
+ useEffect(() => {
+ if (userInfo) {
+ queryClient.setQueryData(QUERY_KEYS.banner(), userInfo);
+ }
+ }, [userInfo]);
return (
diff --git a/src/hooks/mutations/useUserMutation.ts b/src/hooks/mutations/useUserMutation.ts
index 52bb981..31fcd31 100644
--- a/src/hooks/mutations/useUserMutation.ts
+++ b/src/hooks/mutations/useUserMutation.ts
@@ -27,9 +27,8 @@ const useLoginMutation = ({
mutationFn: ({ email, password }: { email: string; password: string }) =>
postLogin({ email, password }),
onSuccess: async (res) => {
- // 유저 정보 불러오기
- console.log('유저 정보 invalidate');
- // queryClient.invalidateQueries({ queryKey: QUERY_KEYS.banner() });
+ // accessToken 저장
+ await setAccessToken(res.accessToken);
// refreshToken 저장
await setRefreshToken(res.refreshToken);
diff --git a/src/lib/axios/axiosInstance.ts b/src/lib/axios/axiosInstance.ts
index a519cc8..6f42328 100644
--- a/src/lib/axios/axiosInstance.ts
+++ b/src/lib/axios/axiosInstance.ts
@@ -1,9 +1,11 @@
import axios from 'axios';
import {
+ getAccessToken,
getRefreshToken,
removeAccessToken,
removeRefreshToken,
+ setAccessToken,
} from '../serverActions';
export const baseURL = process.env.NEXT_PUBLIC_API_URL;
@@ -26,9 +28,19 @@ const onAccessTokenFetched = () => {
refreshSubscribers.forEach((callback) => callback());
refreshSubscribers = []; // 모든 요청이 처리되었기에 배열 초기화
};
+
+// 해당 요청이 서버 사이드인지 클라이언트 사이드인지 판별
+const isServer = typeof window === 'undefined';
+
axiosInstance.interceptors.request.use(
async (config) => {
- console.log(`[API Request] ${config.method?.toUpperCase()} ${config.url}`);
+ if (isServer) {
+ const accessToken = await getAccessToken();
+ if (accessToken) {
+ config.headers.Cookie = `access_token= ${accessToken}`;
+ }
+ }
+
return config;
},
async (error) => {
@@ -39,21 +51,15 @@ axiosInstance.interceptors.request.use(
axiosInstance.interceptors.response.use(
(response) => response,
async (error) => {
- /**
- * TODO:(refresh 토큰 발급 이후)
- * - 토근 재발급 로직
- */
-
- /**
- * - 401 에러로 실패하면, 로그인 페이지로 리다이렉트하는 로직
- * - 리다이렉트 전에 사용자에게 경고 메시지
- */
+ // 서버 사이드 환경이면 바로 리턴
+ if (isServer) {
+ return Promise.reject(error);
+ }
+
if (error.response?.status === 401) {
- // await removeAccessToken();
console.log('401 Unauthorized - 토큰 재발급 시도');
const refreshToken = await getRefreshToken();
- console.log('refreshToken: ', refreshToken);
if (!refreshToken) {
console.log('Refresh Token 없음 -> 강제 로그아웃');
@@ -68,7 +74,7 @@ axiosInstance.interceptors.response.use(
try {
// Refresh Token으로 Access Token 재발급 시도
- await axios.post(
+ const res = await axios.post(
'https://deving.shop/api/v1/auths/refresh',
{
refreshToken,
@@ -78,6 +84,10 @@ axiosInstance.interceptors.response.use(
isRefreshing = false;
+ // accessToken 서버 쿠키로 다시 저장
+ const accessToken = res.data.data.accessToken;
+ await setAccessToken(accessToken);
+
// 대기중인 요청들을 새로운 access token으로 실행
onAccessTokenFetched();
// ✅ 기존 요청 다시 실행 (Access Token 갱신 후)
@@ -93,7 +103,6 @@ axiosInstance.interceptors.response.use(
}
} else {
// refresh token 요청이 진행 중이라면 대기 (Promise를 반환)
-
return new Promise((resolve) => {
refreshSubscribers.push(() => {
resolve(axiosInstance(error.config)); // 기존의 요청을 새로운 토큰으로 재시도
diff --git a/src/lib/serverActions.ts b/src/lib/serverActions.ts
index 122b01f..9b947bc 100644
--- a/src/lib/serverActions.ts
+++ b/src/lib/serverActions.ts
@@ -4,7 +4,12 @@ import { cookies } from 'next/headers';
export async function getAccessToken() {
const cookieStore = cookies();
- return cookieStore.get('token')?.value || null;
+ return cookieStore.get('accessToken')?.value || null;
+}
+
+export async function getAllToken() {
+ const cookieStore = cookies();
+ return cookieStore.getAll();
}
export async function getRefreshToken() {
@@ -14,7 +19,17 @@ export async function getRefreshToken() {
export async function removeAccessToken() {
const cookieStore = cookies();
- cookieStore.delete('access_token');
+ // cookieStore.delete('access_token');
+
+ // ✅ Set-Cookie를 통해 access_token을 삭제 (Max-Age=0)
+ cookieStore.set('access_token', '', {
+ path: '/',
+ maxAge: 0, // 쿠키 만료
+ httpOnly: true, // 백엔드와 일관성 유지
+ secure: true,
+ domain: 'deving.shop',
+ sameSite: 'none',
+ });
}
export async function removeRefreshToken() {
@@ -25,7 +40,7 @@ export async function removeRefreshToken() {
export async function setAccessToken(token: string) {
const cookieStore = cookies();
const isProd = process.env.NODE_ENV === 'production';
- cookieStore.set('token', token, {
+ cookieStore.set('accessToken', token, {
httpOnly: true,
sameSite: 'none',
secure: true,
diff --git a/src/util/searchFilter.ts b/src/util/searchFilter.ts
index ad0767d..b3d1a07 100644
--- a/src/util/searchFilter.ts
+++ b/src/util/searchFilter.ts
@@ -2,8 +2,8 @@ import { CategoryTitle } from 'types/meeting';
export const filterOptions = [
{
- value: 'NEW',
- label: '최신순',
+ value: 'CREATED',
+ label: '생성순',
},
{
value: 'OLD',