/** * API 통신 유틸리티 */ const API_BASE_URL = 'http://localhost:9000/api'; class ApiClient { constructor() { this.token = localStorage.getItem('authToken'); } async request(endpoint, options = {}) { const url = `${API_BASE_URL}${endpoint}`; const config = { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }; // 인증 토큰 추가 if (this.token) { config.headers['Authorization'] = `Bearer ${this.token}`; } try { const response = await fetch(url, config); if (!response.ok) { if (response.status === 401) { // 토큰 만료 시 로그아웃 this.logout(); throw new Error('인증이 만료되었습니다. 다시 로그인해주세요.'); } throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } return await response.text(); } catch (error) { console.error('API 요청 실패:', error); throw error; } } // GET 요청 async get(endpoint) { return this.request(endpoint, { method: 'GET' }); } // POST 요청 async post(endpoint, data) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data) }); } // PUT 요청 async put(endpoint, data) { return this.request(endpoint, { method: 'PUT', body: JSON.stringify(data) }); } // DELETE 요청 async delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); } // 파일 업로드 async uploadFile(endpoint, formData) { return this.request(endpoint, { method: 'POST', headers: { // Content-Type을 설정하지 않음 (FormData가 자동으로 설정) }, body: formData }); } // 토큰 설정 setToken(token) { this.token = token; localStorage.setItem('authToken', token); } // 로그아웃 logout() { this.token = null; localStorage.removeItem('authToken'); localStorage.removeItem('currentUser'); window.location.reload(); } } // 전역 API 클라이언트 인스턴스 const api = new ApiClient(); // 인증 관련 API const AuthAPI = { async login(username, password) { const response = await api.post('/auth/login', { username, password }); if (response.access_token) { api.setToken(response.access_token); localStorage.setItem('currentUser', JSON.stringify(response.user)); } return response; }, async logout() { try { await api.post('/auth/logout'); } catch (error) { console.error('로그아웃 API 호출 실패:', error); } finally { api.logout(); } }, async getCurrentUser() { return api.get('/auth/me'); } }; // Todo 관련 API const TodoAPI = { async getTodos(filter = 'all') { const params = filter !== 'all' ? `?status=${filter}` : ''; return api.get(`/todos${params}`); }, async createTodo(todoData) { return api.post('/todos', todoData); }, async updateTodo(id, todoData) { return api.put(`/todos/${id}`, todoData); }, async deleteTodo(id) { return api.delete(`/todos/${id}`); }, async uploadImage(imageFile) { const formData = new FormData(); formData.append('image', imageFile); return api.uploadFile('/todos/upload-image', formData); } }; // 전역으로 사용 가능하도록 export window.api = api; window.AuthAPI = AuthAPI; window.TodoAPI = TodoAPI;