|
| 1 | +import { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies"; |
1 | 2 | import { cookies } from "next/headers"; |
2 | 3 | import { NextResponse } from "next/server"; |
3 | 4 |
|
4 | 5 | import { API_URL } from "@/data/constants"; |
| 6 | +import { ERROR_CODE } from "@/lib/axios/utils/errorCode"; |
5 | 7 |
|
6 | | -/** |
7 | | - * 로그아웃 요청 API |
8 | | - */ |
9 | | -export async function POST(req: Request) { |
10 | | - const cookiesStore = await cookies(); |
11 | | - |
12 | | - const apiRes = await fetch(process.env.NEXT_PUBLIC_TICKET_API_BASE_URL + API_URL.USER.LOGOUT, { |
| 8 | +async function requestLogout(cookiesStore: ReadonlyRequestCookies) { |
| 9 | + return fetch(process.env.NEXT_PUBLIC_TICKET_API_BASE_URL + API_URL.USER.LOGOUT, { |
13 | 10 | method: "POST", |
14 | 11 | headers: { |
15 | 12 | "Content-Type": "application/json", |
16 | 13 | Cookie: `accessToken=${cookiesStore.get("accessToken")?.value}; refreshToken=${cookiesStore.get("refreshToken")?.value}`, |
17 | 14 | }, |
18 | 15 | credentials: "include", |
19 | 16 | }); |
| 17 | +} |
20 | 18 |
|
21 | | - const setCookies = apiRes.headers.getSetCookie(); |
| 19 | +export async function POST() { |
| 20 | + let apiRes: Response | null = null; |
22 | 21 |
|
23 | | - const res = NextResponse.json(await apiRes.json(), { |
24 | | - status: apiRes.status, |
25 | | - }); |
| 22 | + try { |
| 23 | + const cookiesStore = await cookies(); |
| 24 | + |
| 25 | + apiRes = await requestLogout(cookiesStore); |
| 26 | + |
| 27 | + const data = await apiRes.clone().json(); |
26 | 28 |
|
27 | | - // API 서버가 내려준 모든 Set-Cookie를 그대로 전달 |
28 | | - for (const cookie of setCookies) { |
29 | | - // 기존 쿠키 그대로 전달 |
30 | | - // SSR 환경에서 브라우저 쿠키 사용할 수 있도록 하기 위함 |
31 | | - res.headers.append("Set-Cookie", cookie); |
| 29 | + if (data?.code === ERROR_CODE.ACCESS_TOKEN_EXPIRED) { |
| 30 | + await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/reissue`, { |
| 31 | + method: "POST", |
| 32 | + credentials: "include", |
| 33 | + }); |
32 | 34 |
|
33 | | - // Domain 추가한 쿠키 한 번 더 전달 |
34 | | - // 브라우저 단에서 자동으로 쿠키가 포함한 요청이 갈 수 있도록 하기 위함 |
35 | | - res.headers.append("Set-Cookie", `${cookie}; Domain=.permitseoul.com`); |
| 35 | + apiRes = await requestLogout(await cookies()); |
| 36 | + } |
| 37 | + } catch (e) { |
| 38 | + // ❗ 실패해도 무시 |
36 | 39 | } |
37 | 40 |
|
| 41 | + const res = NextResponse.json({ success: true }, { status: 200 }); |
| 42 | + |
| 43 | + // 성공/실패 무관하게 쿠키 제거 |
| 44 | + clearAuthCookies(res); |
| 45 | + |
38 | 46 | return res; |
39 | 47 | } |
| 48 | + |
| 49 | +function clearAuthCookies(res: NextResponse) { |
| 50 | + for (const name of ["accessToken", "refreshToken"]) { |
| 51 | + res.headers.append( |
| 52 | + "Set-Cookie", |
| 53 | + `${name}=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=None`, |
| 54 | + ); |
| 55 | + |
| 56 | + // 도메인 쿠키까지 같이 제거 |
| 57 | + res.headers.append( |
| 58 | + "Set-Cookie", |
| 59 | + `${name}=; Path=/; Domain=.permitseoul.com; Max-Age=0; HttpOnly; Secure; SameSite=None`, |
| 60 | + ); |
| 61 | + } |
| 62 | +} |
0 commit comments