ky 라이브러리 선택 이유

fetch axios ky
번들 크기 0kb (내장) ~14kb ~3kb
기반 - XMLHttpRequest fetch
인터셉터 직접 래핑 필요 O O (hooks)
타임아웃 직접 구현 필요 O O
자동 재시도 X X O (ky.retry)
TypeScript 기본 타입만 O O

인스턴스 구성

const api = ky.create({
  prefix: process.env.NEXT_PUBLIC_API_URL,
  timeout: 10000,
  credentials: "include",
  headers: {
    "Content-Type": "application/json",
  },
  hooks: {
    beforeRequest: [...],
    afterResponse: [...],
  },
});

토큰 관리 전략

저장 구조

로그인 성공
├── setAccessToken(token)   → 인메모리 변수 (XSS 탈취 방지)
└── setAuthCookie(token)    → Server Action으로 쿠키 저장 (Proxy 인증용)
저장소 역할
인메모리 (accessToken 변수) API 요청 시 Authorization 헤더 주입. 페이지 새로고침 시 초기화됨
쿠키 (accessToken) Proxy에서 서버 사이드 인증 여부 확인. 새로고침 후에도 유지

accessToken은 모듈 스코프 변수로 앱 전체에서 단일 인스턴스로 공유됩니다. 단, 클라이언트 측에서만 유효하며 서버 컴포넌트 요청 시에는 항상 null이므로, 서버 사이드 인증은 쿠키 토큰으로 처리합니다.

beforeRequest — 토큰 자동 주입

beforeRequest: [
      ({ request }) => {
        const token = getAccessToken() ?? getCookieToken();
        if (token) {
          request.headers.set("Authorization", `Bearer ${token}`);
        }
      },
    ],

인메모리 토큰을 우선 사용하고, 없으면 쿠키에서 읽어 폴백합니다. 새로고침 직후처럼 인메모리가 비어있는 상황에서도 쿠키 토큰으로 요청이 정상 처리됩니다.