'use server'; import { cookies } from 'next/headers'; import { fetchJson, getAccessToken, getRefreshToken } from '@/lib/utils/server'; import { MemberResponse } from '@/types/response/account/member'; import { LoginResponse } from '@/types/response/auth'; // RefreshToken으로 AccessToken 갱신 // 백엔드가 Set-Cookie 를 보내지 않으므로 응답 body 의 새 토큰을 직접 쿠키에 set. // 이게 빠지면 cookie 의 구 refreshToken 이 갱신되지 않아 grace period(30s) 만료 시 재사용 탐지로 강제 로그아웃됨. async function refreshAccessToken(): Promise { const refreshToken = await getRefreshToken(); if (!refreshToken) { return false; } const res = await fetchJson('/api/auth/refresh-token', { method: 'POST', body: JSON.stringify({ RefreshToken: refreshToken }) }); if (!res.success || !res.data) { return false; } const cookieStore = await cookies(); const sevenDaysSec = 7 * 24 * 60 * 60; const opts = { httpOnly: true, path: '/', sameSite: 'lax' as const, secure: process.env.NODE_ENV === 'production', maxAge: sevenDaysSec }; cookieStore.set('accessToken', res.data.accessToken, opts); cookieStore.set('refreshToken', res.data.refreshToken, opts); return true; } // 로그인 확인 export async function checkAuthServer(): Promise { const accessToken = await getAccessToken(); if (accessToken) { try { const payload = JSON.parse( Buffer.from(accessToken.split('.')[1], 'base64').toString() ); if (payload.exp * 1000 > Date.now()) { return true; } } catch { } } return await refreshAccessToken(); } // SSR 초기 회원 정보 조회 (헤더 깜빡임 방지용) export async function getInitialMember(): Promise { const accessToken = await getAccessToken(); if (!accessToken) { return null; } try { const payload = JSON.parse( Buffer.from(accessToken.split('.')[1], 'base64').toString() ); if (payload.exp * 1000 <= Date.now()) { return null; } } catch { return null; } const res = await fetchJson('/api/auth/profile', { method: 'GET' }); if (res.success && res.data) { return res.data; } return null; }