---
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)
