import axios from "axios"; import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; import Cookies from "js-cookie"; import { AUTH_USER_ID, CLAIM_NAME } from "./env"; import { errorInterceptor } from "./error-interceptor"; interface ClientConfig { baseURL: string; siteId: string; } class Client { private static instance: Client; private axiosInstance: AxiosInstance; private constructor(config: ClientConfig) { this.axiosInstance = axios.create({ baseURL: config.baseURL, headers: { "Content-Type": "application/json", "X-Site-Id": config.siteId, }, withCredentials: true, }); this.axiosInstance.interceptors.request.use((config) => { // X-Device-Id 자동 주입 로직 (기기에 고유한 UUID 세팅 및 쿠키/로컬스토리지 보관) let deviceId = Cookies.get("device-id"); if (!deviceId && typeof window !== "undefined") { deviceId = localStorage.getItem("device-id") || undefined; if (!deviceId) { deviceId = generateUUID(); localStorage.setItem("device-id", deviceId); } Cookies.set("device-id", deviceId, { expires: 365, path: "/" }); } if (deviceId) { config.headers["X-Device-Id"] = deviceId; } const token = Cookies.get(CLAIM_NAME); const userId = Cookies.get(AUTH_USER_ID); if (!token || !userId) { return config; } config.headers[CLAIM_NAME] = token; config.headers[AUTH_USER_ID] = userId; return config; }); this.axiosInstance.interceptors.response.use( (response) => response, (error) => errorInterceptor(error), ); } public static getInstance(config: ClientConfig): Client { if (!Client.instance) { Client.instance = new Client(config); } return Client.instance; } public async get>( url: string, config?: AxiosRequestConfig, ): Promise { const response = await this.axiosInstance.get(url, config); return response; } public async post, D = unknown>( url: string, data?: D, config?: AxiosRequestConfig, ): Promise { const response = await this.axiosInstance.post(url, data, config); return response; } public async put, D = unknown>( url: string, data?: D, config?: AxiosRequestConfig, ): Promise { const response = await this.axiosInstance.put(url, data, config); return response; } public async delete>( url: string, config?: AxiosRequestConfig, ): Promise { const response = await this.axiosInstance.delete(url, config); return response; } } export type { ClientConfig }; export { Client }; // 기기 식별을 위한 안전한 v4 UUID 생성 폴백 헬퍼 함수 function generateUUID(): string { if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { return crypto.randomUUID(); } return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { const r = (Math.random() * 16) | 0; const v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); }