// frontend/lib/auth.ts — phase-15 인증 클라이언트 헬퍼. // 데모 무손상: 이 모듈은 호출되기 전엔 부작용이 없다. 게이트는 proxy.ts(미들웨어)가 담당. import type { Me } from "@/lib/types"; const BASE = process.env.NEXT_PUBLIC_API_BASE ?? (typeof window === "undefined" ? "http://localhost:31800" : ""); const AUTH_ENABLED = process.env.NEXT_PUBLIC_AUTH_ENABLED === "true"; /** 로그인. 성공 시 MeOut, 실패(401)면 에러를 던진다(쿠키는 백엔드 Set-Cookie). */ export async function login(email: string, password: string): Promise { const res = await fetch(`${BASE}/api/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ email, password }), }); if (!res.ok) { throw new Error("이메일 또는 비밀번호가 올바르지 않아요"); } return res.json() as Promise; } /** 로그아웃. 쿠키를 비운다. 실패해도 조용히 무시(이미 로그아웃 상태일 수 있음). */ export async function logout(): Promise { try { await fetch(`${BASE}/api/auth/logout`, { method: "POST", credentials: "include", }); } catch { // 네트워크 오류는 무시 — UI는 로그인 화면으로 전이하면 충분. } } /** 현재 사용자. 세션 없으면(401) null. */ export async function getMe(signal?: AbortSignal): Promise { const res = await fetch(`${BASE}/api/me`, { credentials: "include", cache: "no-store", signal, }); if (res.status === 401) return null; if (!res.ok) return null; return res.json() as Promise; } /** * 인증 켜짐일 때만 동작하는 얇은 fetch 래퍼. 401이면 /login?next=<현재경로>로 보낸다. * 데모(AUTH_ENABLED=false)에선 일반 fetch와 동일 — 부작용 없음. * 중앙 래퍼가 없으므로 전 호출을 개조하지 않는다(라우트 보호는 proxy.ts가 담당, 이건 보조). */ export async function apiFetch(input: string, init?: RequestInit): Promise { const url = input.startsWith("http") ? input : `${BASE}${input.startsWith("/") ? "" : "/"}${input}`; const res = await fetch(url, { credentials: "include", ...init }); if (AUTH_ENABLED && res.status === 401 && typeof window !== "undefined") { const next = window.location.pathname + window.location.search; window.location.assign(`/login?next=${encodeURIComponent(next)}`); } return res; }