mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 06:16:24 +00:00
Participation API를 client.ts로 통합 및 환경변수 설정 개선
- api-client.ts 삭제하고 client.ts의 participationClient 사용 - 마이크로서비스별 호스트 환경변수 지원 추가 - API_VERSION 환경변수로 api prefix 관리 - .env.local 파일 생성 (개발 환경 설정) - CORS 해결을 위해 백엔드에서 직접 호출하는 방식으로 단순화 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bace9476b1
commit
37e5a76c50
@ -10,7 +10,8 @@
|
||||
"Bash(netstat:*)",
|
||||
"Bash(taskkill:*)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(git add:*)"
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@ -7,5 +7,5 @@ NEXT_PUBLIC_PARTICIPATION_HOST=http://localhost:8084
|
||||
NEXT_PUBLIC_DISTRIBUTION_HOST=http://localhost:8085
|
||||
NEXT_PUBLIC_ANALYTICS_HOST=http://localhost:8086
|
||||
|
||||
# API Version
|
||||
NEXT_PUBLIC_API_VERSION=v1
|
||||
# API Version prefix
|
||||
NEXT_PUBLIC_API_VERSION=api
|
||||
|
||||
@ -1,92 +0,0 @@
|
||||
import axios, { AxiosError, AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
|
||||
/**
|
||||
* API 베이스 URL
|
||||
* 개발 환경: Next.js 프록시를 통해 CORS 우회 (/api/proxy -> localhost:8084)
|
||||
* 프로덕션: 직접 백엔드 서버 호출
|
||||
*/
|
||||
const API_BASE_URL = process.env.NODE_ENV === 'production'
|
||||
? process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8084'
|
||||
: '/api/proxy';
|
||||
|
||||
/**
|
||||
* Axios 인스턴스 생성
|
||||
*/
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 30000, // 30초
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 요청 인터셉터
|
||||
* - 요청 전에 공통 처리 로직 추가 (예: 인증 토큰)
|
||||
*/
|
||||
apiClient.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
// TODO: 필요 시 인증 토큰 추가
|
||||
// const token = localStorage.getItem('accessToken');
|
||||
// if (token && config.headers) {
|
||||
// config.headers.Authorization = `Bearer ${token}`;
|
||||
// }
|
||||
|
||||
console.log(`[API Request] ${config.method?.toUpperCase()} ${config.url}`);
|
||||
return config;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.error('[API Request Error]', error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 응답 인터셉터
|
||||
* - 응답 후 공통 처리 로직 추가 (예: 에러 핸들링)
|
||||
*/
|
||||
apiClient.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
console.log(`[API Response] ${response.config.method?.toUpperCase()} ${response.config.url}`, response.status);
|
||||
return response;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
// 에러 응답 처리
|
||||
if (error.response) {
|
||||
const { status, data } = error.response;
|
||||
console.error(`[API Error] ${status}:`, data);
|
||||
|
||||
// 상태 코드별 처리
|
||||
switch (status) {
|
||||
case 400:
|
||||
console.error('잘못된 요청입니다.');
|
||||
break;
|
||||
case 401:
|
||||
console.error('인증이 필요합니다.');
|
||||
// TODO: 로그인 페이지로 리다이렉트
|
||||
break;
|
||||
case 403:
|
||||
console.error('접근 권한이 없습니다.');
|
||||
break;
|
||||
case 404:
|
||||
console.error('요청한 리소스를 찾을 수 없습니다.');
|
||||
break;
|
||||
case 500:
|
||||
console.error('서버 오류가 발생했습니다.');
|
||||
break;
|
||||
default:
|
||||
console.error('알 수 없는 오류가 발생했습니다.');
|
||||
}
|
||||
} else if (error.request) {
|
||||
// 요청은 전송되었으나 응답을 받지 못한 경우
|
||||
console.error('[API Network Error]', error.message);
|
||||
} else {
|
||||
// 요청 설정 중 오류 발생
|
||||
console.error('[API Setup Error]', error.message);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default apiClient;
|
||||
@ -1,67 +1,95 @@
|
||||
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://20.196.65.160:8081';
|
||||
// 마이크로서비스별 호스트 설정
|
||||
const API_HOSTS = {
|
||||
user: process.env.NEXT_PUBLIC_USER_HOST || 'http://localhost:8081',
|
||||
event: process.env.NEXT_PUBLIC_EVENT_HOST || 'http://localhost:8080',
|
||||
content: process.env.NEXT_PUBLIC_CONTENT_HOST || 'http://localhost:8082',
|
||||
ai: process.env.NEXT_PUBLIC_AI_HOST || 'http://localhost:8083',
|
||||
participation: process.env.NEXT_PUBLIC_PARTICIPATION_HOST || 'http://localhost:8084',
|
||||
distribution: process.env.NEXT_PUBLIC_DISTRIBUTION_HOST || 'http://localhost:8085',
|
||||
analytics: process.env.NEXT_PUBLIC_ANALYTICS_HOST || 'http://localhost:8086',
|
||||
};
|
||||
|
||||
const API_VERSION = process.env.NEXT_PUBLIC_API_VERSION || 'api';
|
||||
|
||||
// 기본 User API 클라이언트 (기존 호환성 유지)
|
||||
const API_BASE_URL = API_HOSTS.user;
|
||||
|
||||
export const apiClient: AxiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 90000, // 30초로 증가
|
||||
timeout: 90000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor - JWT 토큰 추가
|
||||
apiClient.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
console.log('🚀 API Request:', {
|
||||
method: config.method?.toUpperCase(),
|
||||
url: config.url,
|
||||
baseURL: config.baseURL,
|
||||
data: config.data,
|
||||
});
|
||||
|
||||
const token = localStorage.getItem('accessToken');
|
||||
if (token && config.headers) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
console.log('🔑 Token added to request');
|
||||
}
|
||||
return config;
|
||||
// Participation API 전용 클라이언트
|
||||
export const participationClient: AxiosInstance = axios.create({
|
||||
baseURL: `${API_HOSTS.participation}/${API_VERSION}`,
|
||||
timeout: 90000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.error('❌ Request Error:', error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 공통 Request interceptor 함수
|
||||
const requestInterceptor = (config: InternalAxiosRequestConfig) => {
|
||||
console.log('🚀 API Request:', {
|
||||
method: config.method?.toUpperCase(),
|
||||
url: config.url,
|
||||
baseURL: config.baseURL,
|
||||
data: config.data,
|
||||
});
|
||||
|
||||
const token = localStorage.getItem('accessToken');
|
||||
if (token && config.headers) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
console.log('🔑 Token added to request');
|
||||
}
|
||||
);
|
||||
return config;
|
||||
};
|
||||
|
||||
// Response interceptor - 에러 처리
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => {
|
||||
console.log('✅ API Response:', {
|
||||
status: response.status,
|
||||
url: response.config.url,
|
||||
data: response.data,
|
||||
});
|
||||
return response;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.error('❌ API Error:', {
|
||||
message: error.message,
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
url: error.config?.url,
|
||||
data: error.response?.data,
|
||||
});
|
||||
const requestErrorInterceptor = (error: AxiosError) => {
|
||||
console.error('❌ Request Error:', error);
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
if (error.response?.status === 401) {
|
||||
console.warn('🔒 401 Unauthorized - Redirecting to login');
|
||||
// 인증 실패 시 토큰 삭제 및 로그인 페이지로 리다이렉트
|
||||
localStorage.removeItem('accessToken');
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
// 공통 Response interceptor 함수
|
||||
const responseInterceptor = (response: any) => {
|
||||
console.log('✅ API Response:', {
|
||||
status: response.status,
|
||||
url: response.config.url,
|
||||
data: response.data,
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
const responseErrorInterceptor = (error: AxiosError) => {
|
||||
console.error('❌ API Error:', {
|
||||
message: error.message,
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
url: error.config?.url,
|
||||
data: error.response?.data,
|
||||
});
|
||||
|
||||
if (error.response?.status === 401) {
|
||||
console.warn('🔒 401 Unauthorized - Redirecting to login');
|
||||
localStorage.removeItem('accessToken');
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
return Promise.reject(error);
|
||||
};
|
||||
|
||||
// User API Client 인터셉터 적용
|
||||
apiClient.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
||||
apiClient.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
||||
|
||||
// Participation API Client 인터셉터 적용
|
||||
participationClient.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
||||
participationClient.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
||||
|
||||
export default apiClient;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import apiClient from './api-client';
|
||||
import { participationClient } from './client';
|
||||
import type {
|
||||
ApiResponse,
|
||||
PageResponse,
|
||||
@ -20,7 +20,7 @@ export const participate = async (
|
||||
eventId: string,
|
||||
data: ParticipationRequest
|
||||
): Promise<ApiResponse<ParticipationResponse>> => {
|
||||
const response = await apiClient.post<ApiResponse<ParticipationResponse>>(
|
||||
const response = await participationClient.post<ApiResponse<ParticipationResponse>>(
|
||||
`/v1/events/${eventId}/participate`,
|
||||
data
|
||||
);
|
||||
@ -36,7 +36,7 @@ export const getParticipants = async (
|
||||
): Promise<ApiResponse<PageResponse<ParticipationResponse>>> => {
|
||||
const { eventId, storeVisited, page = 0, size = 20, sort = ['createdAt,DESC'] } = params;
|
||||
|
||||
const response = await apiClient.get<ApiResponse<PageResponse<ParticipationResponse>>>(
|
||||
const response = await participationClient.get<ApiResponse<PageResponse<ParticipationResponse>>>(
|
||||
`/v1/events/${eventId}/participants`,
|
||||
{
|
||||
params: {
|
||||
@ -58,7 +58,7 @@ export const getParticipant = async (
|
||||
eventId: string,
|
||||
participantId: string
|
||||
): Promise<ApiResponse<ParticipationResponse>> => {
|
||||
const response = await apiClient.get<ApiResponse<ParticipationResponse>>(
|
||||
const response = await participationClient.get<ApiResponse<ParticipationResponse>>(
|
||||
`/v1/events/${eventId}/participants/${participantId}`
|
||||
);
|
||||
return response.data;
|
||||
@ -119,7 +119,7 @@ export const drawWinners = async (
|
||||
winnerCount: number,
|
||||
applyStoreVisitBonus?: boolean
|
||||
): Promise<ApiResponse<import('../types/api.types').DrawWinnersResponse>> => {
|
||||
const response = await apiClient.post<ApiResponse<import('../types/api.types').DrawWinnersResponse>>(
|
||||
const response = await participationClient.post<ApiResponse<import('../types/api.types').DrawWinnersResponse>>(
|
||||
`/v1/events/${eventId}/draw-winners`,
|
||||
{
|
||||
winnerCount,
|
||||
@ -139,7 +139,7 @@ export const getWinners = async (
|
||||
size = 20,
|
||||
sort: string[] = ['winnerRank,ASC']
|
||||
): Promise<ApiResponse<PageResponse<ParticipationResponse>>> => {
|
||||
const response = await apiClient.get<ApiResponse<PageResponse<ParticipationResponse>>>(
|
||||
const response = await participationClient.get<ApiResponse<PageResponse<ParticipationResponse>>>(
|
||||
`/v1/events/${eventId}/winners`,
|
||||
{
|
||||
params: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user