210 lines
6.1 KiB
Plaintext
210 lines
6.1 KiB
Plaintext
|
|
---
|
||
|
|
alwaysApply: true
|
||
|
|
description: API 요청 시 항상 전용 API 클라이언트를 사용하도록 강제하는 규칙
|
||
|
|
---
|
||
|
|
|
||
|
|
# API 클라이언트 사용 규칙
|
||
|
|
|
||
|
|
## 핵심 원칙
|
||
|
|
|
||
|
|
**절대 `fetch`를 직접 사용하지 않고, 반드시 전용 API 클라이언트를 사용해야 합니다.**
|
||
|
|
|
||
|
|
## 이유
|
||
|
|
|
||
|
|
1. **환경별 URL 자동 처리**: 프로덕션(`v1.vexplor.com`)과 개발(`localhost`) 환경에서 올바른 백엔드 서버로 요청
|
||
|
|
2. **일관된 에러 처리**: 모든 API 호출에서 동일한 에러 핸들링
|
||
|
|
3. **인증 토큰 자동 포함**: Authorization 헤더 자동 추가
|
||
|
|
4. **유지보수성**: API 변경 시 한 곳에서만 수정
|
||
|
|
|
||
|
|
## API 클라이언트 위치
|
||
|
|
|
||
|
|
```
|
||
|
|
frontend/lib/api/
|
||
|
|
├── client.ts # Axios 기반 공통 클라이언트
|
||
|
|
├── flow.ts # 플로우 관리 API
|
||
|
|
├── dashboard.ts # 대시보드 API
|
||
|
|
├── mail.ts # 메일 API
|
||
|
|
├── externalCall.ts # 외부 호출 API
|
||
|
|
├── company.ts # 회사 관리 API
|
||
|
|
└── file.ts # 파일 업로드/다운로드 API
|
||
|
|
```
|
||
|
|
|
||
|
|
## 올바른 사용법
|
||
|
|
|
||
|
|
### ❌ 잘못된 방법 (절대 사용 금지)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 직접 fetch 사용 - 환경별 URL이 자동 처리되지 않음
|
||
|
|
const response = await fetch("/api/flow/definitions/29/steps");
|
||
|
|
const data = await response.json();
|
||
|
|
|
||
|
|
// 상대 경로 - 프로덕션에서 잘못된 도메인으로 요청
|
||
|
|
const response = await fetch(`/api/flow/${flowId}/steps`);
|
||
|
|
```
|
||
|
|
|
||
|
|
### ✅ 올바른 방법
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 1. API 클라이언트 함수 import
|
||
|
|
import { getFlowSteps } from "@/lib/api/flow";
|
||
|
|
|
||
|
|
// 2. 함수 호출
|
||
|
|
const stepsResponse = await getFlowSteps(flowId);
|
||
|
|
if (stepsResponse.success && stepsResponse.data) {
|
||
|
|
setSteps(stepsResponse.data);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 주요 API 클라이언트 함수
|
||
|
|
|
||
|
|
### 플로우 관리 ([flow.ts](mdc:frontend/lib/api/flow.ts))
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import {
|
||
|
|
getFlowDefinitions, // 플로우 목록
|
||
|
|
getFlowById, // 플로우 상세
|
||
|
|
createFlowDefinition, // 플로우 생성
|
||
|
|
updateFlowDefinition, // 플로우 수정
|
||
|
|
deleteFlowDefinition, // 플로우 삭제
|
||
|
|
getFlowSteps, // 스텝 목록 ⭐
|
||
|
|
createFlowStep, // 스텝 생성
|
||
|
|
updateFlowStep, // 스텝 수정
|
||
|
|
deleteFlowStep, // 스텝 삭제
|
||
|
|
getFlowConnections, // 연결 목록 ⭐
|
||
|
|
createFlowConnection, // 연결 생성
|
||
|
|
deleteFlowConnection, // 연결 삭제
|
||
|
|
getStepDataCount, // 스텝 데이터 카운트
|
||
|
|
getStepDataList, // 스텝 데이터 목록
|
||
|
|
getAllStepCounts, // 모든 스텝 카운트
|
||
|
|
moveData, // 데이터 이동
|
||
|
|
moveBatchData, // 배치 데이터 이동
|
||
|
|
getAuditLogs, // 오딧 로그
|
||
|
|
} from "@/lib/api/flow";
|
||
|
|
```
|
||
|
|
|
||
|
|
### Axios 클라이언트 ([client.ts](mdc:frontend/lib/api/client.ts))
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import apiClient from "@/lib/api/client";
|
||
|
|
|
||
|
|
// GET 요청
|
||
|
|
const response = await apiClient.get("/api/endpoint");
|
||
|
|
|
||
|
|
// POST 요청
|
||
|
|
const response = await apiClient.post("/api/endpoint", { data });
|
||
|
|
|
||
|
|
// PUT 요청
|
||
|
|
const response = await apiClient.put("/api/endpoint", { data });
|
||
|
|
|
||
|
|
// DELETE 요청
|
||
|
|
const response = await apiClient.delete("/api/endpoint");
|
||
|
|
```
|
||
|
|
|
||
|
|
## 새로운 API 함수 추가 가이드
|
||
|
|
|
||
|
|
기존 API 클라이언트에 함수가 없는 경우:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// frontend/lib/api/yourModule.ts
|
||
|
|
|
||
|
|
// 1. API URL 동적 설정 (필수)
|
||
|
|
const getApiBaseUrl = (): string => {
|
||
|
|
if (process.env.NEXT_PUBLIC_API_URL) {
|
||
|
|
return process.env.NEXT_PUBLIC_API_URL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (typeof window !== "undefined") {
|
||
|
|
const currentHost = window.location.hostname;
|
||
|
|
|
||
|
|
// 프로덕션: v1.vexplor.com → api.vexplor.com
|
||
|
|
if (currentHost === "v1.vexplor.com") {
|
||
|
|
return "https://api.vexplor.com/api";
|
||
|
|
}
|
||
|
|
|
||
|
|
// 로컬 개발
|
||
|
|
if (currentHost === "localhost" || currentHost === "127.0.0.1") {
|
||
|
|
return "http://localhost:8080/api";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return "/api";
|
||
|
|
};
|
||
|
|
|
||
|
|
const API_BASE = getApiBaseUrl();
|
||
|
|
|
||
|
|
// 2. API 함수 작성
|
||
|
|
export async function getYourData(id: number): Promise<ApiResponse<YourType>> {
|
||
|
|
try {
|
||
|
|
const response = await fetch(`${API_BASE}/your-endpoint/${id}`, {
|
||
|
|
credentials: "include",
|
||
|
|
});
|
||
|
|
|
||
|
|
return await response.json();
|
||
|
|
} catch (error: any) {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
error: error.message,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 환경별 URL 매핑
|
||
|
|
|
||
|
|
API 클라이언트는 자동으로 환경을 감지합니다:
|
||
|
|
|
||
|
|
| 현재 호스트 | 백엔드 API URL |
|
||
|
|
| ---------------- | ----------------------------- |
|
||
|
|
| `v1.vexplor.com` | `https://api.vexplor.com/api` |
|
||
|
|
| `localhost:9771` | `http://localhost:8080/api` |
|
||
|
|
| `localhost:3000` | `http://localhost:8080/api` |
|
||
|
|
|
||
|
|
## 체크리스트
|
||
|
|
|
||
|
|
코드 작성 시 다음을 확인하세요:
|
||
|
|
|
||
|
|
- [ ] `fetch('/api/...')` 직접 사용하지 않음
|
||
|
|
- [ ] 적절한 API 클라이언트 함수를 import 함
|
||
|
|
- [ ] API 응답의 `success` 필드를 체크함
|
||
|
|
- [ ] 에러 처리를 구현함
|
||
|
|
- [ ] 새로운 API가 필요하면 `lib/api/` 에 함수 추가
|
||
|
|
|
||
|
|
## 예외 상황
|
||
|
|
|
||
|
|
다음 경우에만 `fetch`를 직접 사용할 수 있습니다:
|
||
|
|
|
||
|
|
1. **외부 서비스 호출**: 다른 도메인의 API 호출 시
|
||
|
|
2. **특수한 헤더가 필요한 경우**: FormData, Blob 등
|
||
|
|
|
||
|
|
이 경우에도 가능하면 전용 API 클라이언트 함수로 래핑하세요.
|
||
|
|
|
||
|
|
## 실제 적용 예시
|
||
|
|
|
||
|
|
### 플로우 위젯 ([FlowWidget.tsx](mdc:frontend/components/screen/widgets/FlowWidget.tsx))
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// ❌ 이전 코드
|
||
|
|
const stepsResponse = await fetch(`/api/flow/definitions/${flowId}/steps`);
|
||
|
|
const connectionsResponse = await fetch(`/api/flow/connections/${flowId}`);
|
||
|
|
|
||
|
|
// ✅ 수정된 코드
|
||
|
|
const stepsResponse = await getFlowSteps(flowId);
|
||
|
|
const connectionsResponse = await getFlowConnections(flowId);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 플로우 가시성 패널 ([FlowVisibilityConfigPanel.tsx](mdc:frontend/components/screen/config-panels/FlowVisibilityConfigPanel.tsx))
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// ❌ 이전 코드
|
||
|
|
const stepsResponse = await fetch(`/api/flow/definitions/${flowId}/steps`);
|
||
|
|
|
||
|
|
// ✅ 수정된 코드
|
||
|
|
const stepsResponse = await getFlowSteps(flowId);
|
||
|
|
```
|
||
|
|
|
||
|
|
## 참고 자료
|
||
|
|
|
||
|
|
- [API 클라이언트 공통 설정](mdc:frontend/lib/api/client.ts)
|
||
|
|
- [플로우 API 클라이언트](mdc:frontend/lib/api/flow.ts)
|
||
|
|
- [API URL 유틸리티](mdc:frontend/lib/utils/apiUrl.ts)
|