// lib/api/client.ts import { AUTH_CONFIG } from './auth/auth-config'; import type { AuthMode } from './auth/types'; interface ClientConfig { mode: AuthMode; apiKey?: string; // API Key 모드용 getToken?: () => string | null; // Bearer 모드용 } interface ApiErrorResponse { message: string; errors?: Record; code?: string; } export class ApiClient { private baseURL: string; private mode: AuthMode; private apiKey?: string; private getToken?: () => string | null; constructor(config: ClientConfig) { this.baseURL = AUTH_CONFIG.apiUrl; this.mode = config.mode; this.apiKey = config.apiKey; this.getToken = config.getToken; } /** * 인증 헤더 생성 */ private getAuthHeaders(): Record { const headers: Record = { 'Accept': 'application/json', 'Content-Type': 'application/json', }; // API Key는 모든 모드에서 기본으로 포함 (PHP API 요구사항) if (this.apiKey) { headers['X-API-KEY'] = this.apiKey; } switch (this.mode) { case 'api-key': // API Key만 사용 (이미 위에서 추가됨) break; case 'bearer': const token = this.getToken?.(); if (token) { headers['Authorization'] = `Bearer ${token}`; } // API Key도 함께 전송 (이미 위에서 추가됨) break; case 'sanctum': // 쿠키 기반 - 별도 헤더 불필요 break; } return headers; } /** * HTTP 요청 실행 */ async request( endpoint: string, options?: RequestInit ): Promise { const url = `${this.baseURL}${endpoint}`; const headers = { ...this.getAuthHeaders(), ...options?.headers, }; const config: RequestInit = { ...options, headers, }; // Sanctum 모드는 쿠키 포함 if (this.mode === 'sanctum') { config.credentials = 'include'; } const response = await fetch(url, config); if (!response.ok) { await this.handleError(response); } // 204 No Content 처리 if (response.status === 204) { return undefined as T; } return await response.json(); } /** * GET 요청 */ async get(endpoint: string): Promise { return this.request(endpoint, { method: 'GET' }); } /** * POST 요청 */ async post(endpoint: string, data?: unknown): Promise { return this.request(endpoint, { method: 'POST', body: data ? JSON.stringify(data) : undefined, }); } /** * PUT 요청 */ async put(endpoint: string, data?: unknown): Promise { return this.request(endpoint, { method: 'PUT', body: data ? JSON.stringify(data) : undefined, }); } /** * DELETE 요청 */ async delete(endpoint: string): Promise { return this.request(endpoint, { method: 'DELETE' }); } /** * 에러 처리 */ private async handleError(response: Response): Promise { const data = await response.json().catch(() => ({})); const error: ApiErrorResponse = { message: data.message || 'An error occurred', errors: data.errors, code: data.code, }; throw { status: response.status, ...error, }; } }