API 재활용1
This commit is contained in:
364
src/app/lib/apiService.ts
Normal file
364
src/app/lib/apiService.ts
Normal file
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
* 중앙화된 API 서비스
|
||||
* 모든 외부 API 요청을 이 모듈에서 관리
|
||||
*/
|
||||
|
||||
interface ApiResponse<T = any> {
|
||||
data: T;
|
||||
status: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
interface RequestConfig {
|
||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
||||
headers?: Record<string, string>;
|
||||
body?: any;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
class ApiService {
|
||||
private baseURL: string;
|
||||
private defaultTimeout: number = 30000; // 30초
|
||||
|
||||
constructor(baseURL: string) {
|
||||
this.baseURL = baseURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 토큰을 가져오는 헬퍼 함수
|
||||
*/
|
||||
private getToken(): string | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
|
||||
// localStorage에서 토큰 우선 확인
|
||||
const localToken = localStorage.getItem('token');
|
||||
if (localToken) return localToken;
|
||||
|
||||
// 쿠키에서 토큰 확인
|
||||
const cookieToken = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('token='))
|
||||
?.split('=')[1];
|
||||
|
||||
return cookieToken || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 기본 헤더 생성
|
||||
*/
|
||||
private getDefaultHeaders(): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const token = this.getToken();
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 타임아웃이 있는 fetch
|
||||
*/
|
||||
private async fetchWithTimeout(url: string, options: RequestInit, timeout: number): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
signal: controller.signal,
|
||||
});
|
||||
clearTimeout(timeoutId);
|
||||
return response;
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
throw new Error('요청 시간이 초과되었습니다.');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 공통 API 요청 함수
|
||||
*/
|
||||
private async request<T = any>(
|
||||
endpoint: string,
|
||||
config: RequestConfig = {}
|
||||
): Promise<ApiResponse<T>> {
|
||||
const {
|
||||
method = 'GET',
|
||||
headers = {},
|
||||
body,
|
||||
timeout = this.defaultTimeout
|
||||
} = config;
|
||||
|
||||
const url = endpoint.startsWith('http') ? endpoint : `${this.baseURL}${endpoint}`;
|
||||
|
||||
const requestOptions: RequestInit = {
|
||||
method,
|
||||
headers: {
|
||||
...this.getDefaultHeaders(),
|
||||
...headers,
|
||||
},
|
||||
};
|
||||
|
||||
if (body && method !== 'GET') {
|
||||
requestOptions.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.fetchWithTimeout(url, requestOptions, timeout);
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
||||
|
||||
try {
|
||||
const errorData = await response.json();
|
||||
if (errorData.message) {
|
||||
errorMessage = errorData.message;
|
||||
} else if (errorData.error) {
|
||||
errorMessage = errorData.error;
|
||||
}
|
||||
} catch (parseError) {
|
||||
// JSON 파싱 실패 시 기본 에러 메시지 사용
|
||||
}
|
||||
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
data,
|
||||
status: response.status,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw error;
|
||||
}
|
||||
throw new Error('알 수 없는 오류가 발생했습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 인증 관련 API =====
|
||||
|
||||
/**
|
||||
* 로그인
|
||||
*/
|
||||
async login(email: string, password: string) {
|
||||
return this.request('/auth/login', {
|
||||
method: 'POST',
|
||||
body: { email, password },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 정보 조회
|
||||
*/
|
||||
async getCurrentUser() {
|
||||
return this.request('/auth/me');
|
||||
}
|
||||
|
||||
/**
|
||||
* 회원가입
|
||||
*/
|
||||
async register(userData: {
|
||||
email: string;
|
||||
emailCode: string;
|
||||
password: string;
|
||||
passwordConfirm: string;
|
||||
name: string;
|
||||
phone: string;
|
||||
gender: 'MALE' | 'FEMALE';
|
||||
birthDate: string;
|
||||
}) {
|
||||
return this.request('/auth/signup', {
|
||||
method: 'POST',
|
||||
body: userData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 이메일 인증번호 전송
|
||||
*/
|
||||
async sendEmailVerification(email: string) {
|
||||
return this.request('/auth/verify-email/send', {
|
||||
method: 'POST',
|
||||
body: { email },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 이메일 인증번호 확인
|
||||
*/
|
||||
async verifyEmailCode(email: string, emailCode: string) {
|
||||
return this.request('/auth/verify-email/confirm', {
|
||||
method: 'POST',
|
||||
body: { email, emailCode },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 아이디 찾기
|
||||
*/
|
||||
async findUserId(name: string, phone: string) {
|
||||
return this.request('/auth/find-id', {
|
||||
method: 'POST',
|
||||
body: { name, phone },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 비밀번호 재설정 이메일 전송
|
||||
*/
|
||||
async sendPasswordReset(email: string) {
|
||||
return this.request('/auth/password/forgot', {
|
||||
method: 'POST',
|
||||
body: { email },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 비밀번호 재설정 코드 확인
|
||||
*/
|
||||
async verifyPasswordResetCode(email: string, code: string) {
|
||||
return this.request('/auth/password/confirm', {
|
||||
method: 'POST',
|
||||
body: { email, code },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 비밀번호 재설정
|
||||
*/
|
||||
async resetPassword(email: string, code: string, newPassword: string, confirmPassword: string) {
|
||||
return this.request('/auth/password/reset', {
|
||||
method: 'POST',
|
||||
body: { email, code, newPassword, confirmPassword },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 계정 삭제
|
||||
*/
|
||||
async deleteAccount() {
|
||||
return this.request('/auth/delete/me', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 관리자 관련 API =====
|
||||
|
||||
/**
|
||||
* 과목 리스트 조회
|
||||
*/
|
||||
async getSubjects() {
|
||||
return this.request('/subjects');
|
||||
}
|
||||
|
||||
/**
|
||||
* 과목 생성
|
||||
*/
|
||||
async createSubject(subjectData: {
|
||||
courseName: string;
|
||||
instructorName: string;
|
||||
}) {
|
||||
return this.request('/subjects', {
|
||||
method: 'POST',
|
||||
body: subjectData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 과목 수정
|
||||
*/
|
||||
async updateSubject(subjectId: string, subjectData: {
|
||||
courseName: string;
|
||||
instructorName: string;
|
||||
}) {
|
||||
return this.request(`/subjects/${subjectId}`, {
|
||||
method: 'PUT',
|
||||
body: subjectData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 과목 삭제
|
||||
*/
|
||||
async deleteSubject(subjectId: string) {
|
||||
return this.request(`/subjects/${subjectId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 리스트 조회
|
||||
*/
|
||||
async getUsers() {
|
||||
return this.request('/users');
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 생성
|
||||
*/
|
||||
async createUser(userData: any) {
|
||||
return this.request('/users', {
|
||||
method: 'POST',
|
||||
body: userData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 수정
|
||||
*/
|
||||
async updateUser(userId: string, userData: any) {
|
||||
return this.request(`/users/${userId}`, {
|
||||
method: 'PUT',
|
||||
body: userData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 삭제
|
||||
*/
|
||||
async deleteUser(userId: string) {
|
||||
return this.request(`/users/${userId}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 기타 API =====
|
||||
|
||||
/**
|
||||
* 공지사항 조회
|
||||
*/
|
||||
async getNotices() {
|
||||
return this.request('/notices');
|
||||
}
|
||||
|
||||
/**
|
||||
* 강좌 조회
|
||||
*/
|
||||
async getLessons() {
|
||||
return this.request('/lessons');
|
||||
}
|
||||
|
||||
/**
|
||||
* 리소스 조회
|
||||
*/
|
||||
async getResources() {
|
||||
return this.request('/resources');
|
||||
}
|
||||
}
|
||||
|
||||
// 기본 API 서비스 인스턴스 생성
|
||||
const apiService = new ApiService(
|
||||
process.env.NEXT_PUBLIC_API_BASE_URL || 'https://hrdi.coconutmeet.net'
|
||||
);
|
||||
|
||||
export default apiService;
|
||||
export type { ApiResponse, RequestConfig };
|
||||
Reference in New Issue
Block a user