158 lines
4.7 KiB
TypeScript
158 lines
4.7 KiB
TypeScript
import axios, { AxiosResponse, AxiosError } from "axios";
|
|
|
|
// API 기본 URL 설정
|
|
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080/api";
|
|
|
|
// JWT 토큰 관리 유틸리티
|
|
const TokenManager = {
|
|
getToken: (): string | null => {
|
|
if (typeof window !== "undefined") {
|
|
return localStorage.getItem("authToken");
|
|
}
|
|
return null;
|
|
},
|
|
|
|
isTokenExpired: (token: string): boolean => {
|
|
try {
|
|
const payload = JSON.parse(atob(token.split(".")[1]));
|
|
return payload.exp * 1000 < Date.now();
|
|
} catch {
|
|
return true;
|
|
}
|
|
},
|
|
};
|
|
|
|
// Axios 인스턴스 생성
|
|
export const apiClient = axios.create({
|
|
baseURL: API_BASE_URL,
|
|
timeout: 10000,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
withCredentials: true, // 쿠키 포함
|
|
});
|
|
|
|
// 요청 인터셉터
|
|
apiClient.interceptors.request.use(
|
|
(config) => {
|
|
// JWT 토큰 추가
|
|
const token = TokenManager.getToken();
|
|
console.log("🔍 API 요청 토큰 확인:", {
|
|
hasToken: !!token,
|
|
tokenLength: token ? token.length : 0,
|
|
tokenStart: token ? token.substring(0, 30) + "..." : "없음",
|
|
url: config.url,
|
|
method: config.method,
|
|
});
|
|
|
|
if (token && !TokenManager.isTokenExpired(token)) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
console.log("✅ JWT 토큰 추가됨:", token.substring(0, 50) + "...");
|
|
console.log("🔑 Authorization 헤더:", `Bearer ${token.substring(0, 30)}...`);
|
|
} else if (token && TokenManager.isTokenExpired(token)) {
|
|
console.warn("❌ 토큰이 만료되었습니다.");
|
|
// 토큰 제거
|
|
if (typeof window !== "undefined") {
|
|
localStorage.removeItem("authToken");
|
|
}
|
|
} else {
|
|
console.warn("⚠️ 토큰이 없습니다.");
|
|
}
|
|
|
|
// 언어 정보를 쿼리 파라미터에 추가
|
|
if (config.method?.toUpperCase() === "GET") {
|
|
// 전역 언어 상태에서 현재 언어 가져오기
|
|
const currentLang = typeof window !== "undefined" ? (window as any).__GLOBAL_USER_LANG || "ko" : "ko";
|
|
console.log("🌐 API 요청 시 언어 정보:", currentLang);
|
|
|
|
if (config.params) {
|
|
config.params.userLang = currentLang;
|
|
} else {
|
|
config.params = { userLang: currentLang };
|
|
}
|
|
}
|
|
|
|
console.log("📡 API 요청:", config.method?.toUpperCase(), config.url, config.params, config.data);
|
|
console.log("📋 요청 헤더:", config.headers);
|
|
return config;
|
|
},
|
|
(error) => {
|
|
console.error("❌ API 요청 오류:", error);
|
|
return Promise.reject(error);
|
|
},
|
|
);
|
|
|
|
// 응답 인터셉터
|
|
apiClient.interceptors.response.use(
|
|
(response: AxiosResponse) => {
|
|
console.log("✅ API 응답:", response.status, response.config.url, response.data);
|
|
return response;
|
|
},
|
|
(error: AxiosError) => {
|
|
console.error("❌ API 응답 오류:", {
|
|
status: error.response?.status,
|
|
statusText: error.response?.statusText,
|
|
url: error.config?.url,
|
|
data: error.response?.data,
|
|
message: error.message,
|
|
headers: error.config?.headers,
|
|
});
|
|
|
|
// 401 에러 시 상세 정보 출력
|
|
if (error.response?.status === 401) {
|
|
console.error("🚨 401 Unauthorized 오류 상세 정보:", {
|
|
url: error.config?.url,
|
|
method: error.config?.method,
|
|
headers: error.config?.headers,
|
|
requestData: error.config?.data,
|
|
responseData: error.response?.data,
|
|
token: TokenManager.getToken() ? "존재" : "없음",
|
|
});
|
|
}
|
|
|
|
// 401 에러 시 토큰 제거 및 로그인 페이지로 리다이렉트
|
|
if (error.response?.status === 401 && typeof window !== "undefined") {
|
|
console.log("🔄 401 에러 감지 - 토큰 제거 및 로그인 페이지로 리다이렉트");
|
|
localStorage.removeItem("authToken");
|
|
|
|
// 로그인 페이지가 아닌 경우에만 리다이렉트
|
|
if (window.location.pathname !== "/login") {
|
|
window.location.href = "/login";
|
|
}
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
},
|
|
);
|
|
|
|
// 공통 응답 타입
|
|
export interface ApiResponse<T = any> {
|
|
success: boolean;
|
|
data?: T;
|
|
message?: string;
|
|
errorCode?: string;
|
|
}
|
|
|
|
// API 호출 헬퍼 함수
|
|
export const apiCall = async <T>(
|
|
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
url: string,
|
|
data?: any,
|
|
): Promise<ApiResponse<T>> => {
|
|
try {
|
|
const response = await apiClient.request({
|
|
method,
|
|
url,
|
|
data,
|
|
});
|
|
return response.data;
|
|
} catch (error: any) {
|
|
console.error("API 호출 실패:", error);
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.message || error.message || "알 수 없는 오류가 발생했습니다.",
|
|
errorCode: error.response?.data?.errorCode,
|
|
};
|
|
}
|
|
};
|