# MCP 연동 메뉴얼

AI 에디터(Claude Code, Cursor, Google Antigravity)에 Bkend MCP를 연동하여 백엔드 인프라를 자연어로 관리하는 방법을 안내합니다.

## 🤔 MCP란?

**MCP (Model Context Protocol)**는 AI가 외부 도구를 사용할 수 있게 해주는 프로토콜입니다.

### Bkend MCP를 연동하면?

AI 에디터에서 **자연어로** Bkend 백엔드를 관리할 수 있어요:

> 사용자:  "Todo 테이블 만들어줘. 제목, 완료여부, 우선순위 필드 필요해"

**AI가 자동으로 :**

✅ MongoDB 스키마 설계

✅ REST API 4종 생성 (GET/POST/PATCH/DELETE)

✅ 필드 타입 검증 규칙 추가

✅ 인덱스 최적화

✅ 권한 설정

**...**

**결과:**

`/data/todos` API 즉시 사용 가능! ⚡

## 시작하기

### 1️⃣  처음이신가요?

---

[https://slashpage.com/bkend/dk58wg2eg4z5q2nqevxz](https://slashpage.com/bkend/dk58wg2eg4z5q2nqevxz)

**배울 내용:**

- Organization ID, API Key 발급 받기

- AI 에디터 설정하기 (3개 중 선택)

- 첫 테이블 만들어보기

- 프론트엔드 연동하기

---

**이런 분들에게 추천:**

 ✅  Bkend MCP를 처음 사용하는 분

 ✅  빠르게 실습하며 배우고 싶은 분

### 2️⃣  AI에게 어떻게 말해야 할지 모르겠어요

---

[https://slashpage.com/bkend/dk58wg2eg4w532nqevxz](https://slashpage.com/bkend/dk58wg2eg4w532nqevxz)

**배울 내용:**

- 애매한 요청 vs 명확한 요청

- 실전 예시 모음 (테이블 생성, 검색 최적화, 권한 설정)

- 피해야 할 패턴

---

**이런 분들에게 추천:**

 ✅  AI가 내 요청을 잘 이해하지 못하는 것 같아요

 ✅  더 효과적으로 요청하고 싶어요

### 3️⃣ 더 깊이 활용하고 싶어요

---

 아래 **고급 기능** 섹션으로 스크롤 ⬇️

## 고급 기능

기본 사용법을 익혔다면, 아래 기능들을 활용해보세요.

### 스키마 설계

**MongoDB 스키마 타입 가이드**

### 지원하는 필드 타입

| 타입 | 설명 | 예시 |
| --- | --- | --- |
| **String** | 문자열 | 이름, 이메일, 설명 |
| **Number** | 숫자 | 나이, 가격, 수량 |
| **Boolean** | 참/거짓 | 완료 여부, 활성화 상태 |
| **Date** | 날짜/시간 | 생성일, 마감일 |
| **Array** | 배열 | 태그 목록, 파일 목록 |
| **Object** | 객체 | 주소 (시/구/동), 설정 정보 |
| **ObjectId** | 참조 ID | 다른 테이블 참조 |

### 필드 옵션

```
{
  "fieldName": "email",
  "type": "String",
  "required": true,        // 필수 필드
  "unique": true,          // 고유 값 (중복 불가)
  "default": "user@example.com",  // 기본값
  "minLength": 5,          // 최소 길이
  "maxLength": 100,        // 최대 길이
  "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"  // 정규식
}
```

### AI에게 요청하는 방법

**좋은 예:**

```
"사용자 테이블에 이메일 필드 추가해줘.
필수 항목이고, 중복되면 안 돼.
이메일 형식이 맞는지 검증도 필요해."
```

→ AI가 자동으로 `required: true`, `unique: true`, 이메일 패턴 검증을 추가합니다.

**관계형 데이터 설계하기**

### 1:N 관계 (일대다)

**시나리오:** 한 음식점은 여러 메뉴를 가질 수 있음

**AI에게 요청:**

```
"배달 앱 만들려고 해.
음식점 정보(이름, 주소, 전화번호)랑
메뉴(이름, 가격, 카테고리)를 저장해야 해.
한 음식점은 여러 메뉴를 가질 수 있어."
```

**AI가 생성하는 구조:**

```
// restaurants 테이블
{
  "_id": ObjectId,
  "name": String,
  "address": String,
  "phone": String
}

// menus 테이블
{
  "_id": ObjectId,
  "restaurantId": ObjectId,  // restaurants 테이블 참조
  "name": String,
  "price": Number,
  "category": String
}
```

### N:M 관계 (다대다)

**시나리오:** 한 학생은 여러 수업을 들을 수 있고, 한 수업은 여러 학생이 들을 수 있음

**AI에게 요청:**

```
"학생 관리 앱 만들려고 해.
학생 정보(이름, 학번)와 수업 정보(수업명, 교수명)를 저장하고,
한 학생은 여러 수업을 들을 수 있고, 한 수업은 여러 학생이 들을 수 있어."
```

**AI가 생성하는 구조:**

```
// students 테이블
{
  "_id": ObjectId,
  "name": String,
  "studentId": String
}

// courses 테이블
{
  "_id": ObjectId,
  "courseName": String,
  "professor": String
}

// enrollments 테이블 (중간 테이블)
{
  "_id": ObjectId,
  "studentId": ObjectId,   // students 테이블 참조
  "courseId": ObjectId,    // courses 테이블 참조
  "enrolledAt": Date
}
```

특정 값만 허용하고 싶을 때 사용합니다.

**AI에게 요청:**

```
"상품 테이블 만들어줘.
상태는 '판매중', '품절', '단종' 중 하나만 가능해야 해."
```

**AI가 생성하는 구조:**

```
{
  "status": {
    "type": "String",
    "enum": ["판매중", "품절", "단종"],
    "default": "판매중"
  }
}
```

---

---

### 권한 관리

**역할 기반 권한 설정**

### 기본 역할

Bkend는 기본적으로 2개의 역할을 제공합니다:

| 역할 | 설명 | 기본 권한 |
| --- | --- | --- |
| **admin** | 관리자 | CRUD 모두 가능 |
| **user** | 일반 사용자 | CRUD 모두 가능 |
| **self** |  |  |

> **Beta 제약:** 현재는 admin, self, user 3개 역할만 제공되며, 커스텀 역할 추가는 정식 버전에서 지원 예정입니다.

### CRUD 권한 조합

각 역할은 다음 4가지 권한을 개별적으로 설정할 수 있습니다:

- **Create (C)**: 데이터 생성

- **Read (R)**: 데이터 조회

- **Update (U)**: 데이터 수정

- **Delete (D)**: 데이터 삭제

### 실전 예시

**시나리오 1: 읽기 전용 사용자**

**AI에게 요청:**

```
"공지사항 테이블 만들어줘.
관리자는 작성/수정/삭제 가능하고,
일반 사용자는 보기만 가능하게 해줘."
```

**결과:**

- admin: ✅ Create, ✅ Read, ✅ Update, ✅ Delete

- user: ❌ Create, ✅ Read, ❌ Update, ❌ Delete

---

**시나리오 2: 본인 데이터만 수정/삭제 가능**

**AI에게 요청:**

```
"게시글 테이블 만들어줘.
누구나 글을 쓰고 볼 수 있지만,
수정과 삭제는 본인이 쓴 글만 가능하게 해줘."
```

**결과:**

- admin: ✅ CRUD 모두

- user: ✅ Create, ✅ Read, ✅ Update (본인 글만), ✅ Delete (본인 글만)

> 💡 **본인 데이터 필터링은 프론트엔드에서 처리해야 합니다.**

> API 호출 시 `createdBy` 필드로 필터링:

```
// 현재 사용자 ID로 필터링
const filters = JSON.stringify({ createdBy: currentUser.id });
await apiClient.get(`/data/posts?andFilters=${encodeURIComponent(filters)}`);
```

---

---

### 성능 최적화

**인덱스 전략**

### 인덱스란?

데이터베이스에서 검색 속도를 빠르게 하기 위한 **색인**입니다.

**비유:**

- 인덱스 없음 = 책에서 특정 단어를 찾기 위해 **처음부터 끝까지** 읽기

- 인덱스 있음 = 책 **뒤쪽 색인**을 보고 바로 페이지 찾기

### 언제 인덱스를 추가해야 하나요?

✅ **추가해야 하는 경우:**

- 자주 검색하는 필드 (이메일, 이름 등)

- 정렬 기준으로 사용하는 필드 (생성일, 수정일 등)

- 중복을 방지해야 하는 필드 (unique 제약조건)

❌ **추가하지 말아야 하는 경우:**

- 거의 검색하지 않는 필드

- 데이터 입력이 빈번한 테이블 (인덱스가 많으면 입력 속도 저하)

### AI에게 요청하는 방법

**예시 1: 단일 필드 인덱스**

```
"사용자를 이메일로 검색할 때 느린데 빠르게 해줘."
```

→ AI가 `email` 필드에 인덱스를 추가합니다.

---

**예시 2: 중복 방지 (Unique 인덱스)**

```
"회원가입할 때 같은 이메일로 여러 번 가입되는 문제가 있어. 막아줘."
```

→ AI가 `email` 필드에 unique 인덱스를 추가합니다.

---

**예시 3: 복합 인덱스**

```
**쿼리 최적화 팁**

### 1. 필요한 필드만 조회하기

❌ **나쁜 예:**

```
// 모든 필드 조회 (불필요한 데이터 전송)
const users = await apiClient.get('/data/users');
```

✅ **좋은 예:**

```
// 필요한 필드만 지정 (API에서 지원 예정)
// 현재는 프론트엔드에서 필터링
const users = await apiClient.get('/data/users');
const userNames = users.data.data.items.map(u => ({ id: u._id, name: u.name }));
```

### 2. 페이지네이션 활용

❌ **나쁜 예:**

```
// 모든 데이터 한 번에 조회
const todos = await apiClient.get('/data/todos');
```

✅ **좋은 예:**

```
// 페이지 단위로 조회
const todos = await apiClient.get('/data/todos?page=1&limit=20');
```

### 3. 서버 사이드 필터링 사용

❌ **나쁜 예:**

```
// 클라이언트에서 필터링 (모든 데이터 전송)
const allTodos = await apiClient.get('/data/todos');
const completed = allTodos.data.data.items.filter(t => t.completed);
```

✅ **좋은 예:**

```
// 서버에서 필터링 (필요한 데이터만 전송)
const filters = JSON.stringify({ completed: true });
const completed = await apiClient.get(`/data/todos?andFilters=${encodeURIComponent(filters)}`);
```

### 4. 인덱스가 있는 필드로 정렬

✅ **좋은 예:**

```
// createdAt에는 시스템 인덱스가 있음
const latest = await apiClient.get('/data/todos?sortBy=createdAt&sortDirection=desc');
```"사용자를 이름과 나이로 함께 검색할 때 빠르게 해줘."
```

→ AI가 `(name, age)` 복합 인덱스를 추가합니다.

### 시스템 자동 생성 인덱스

모든 테이블에는 다음 4개의 인덱스가 자동으로 생성됩니다:

| 인덱스명 | 필드 | 용도 |
| --- | --- | --- |
| `_id_` | _id | Primary Key, 레코드 조회 |
| `idx_createdAt_desc` | createdAt | 생성일 기준 정렬 |
| `idx_updatedAt_desc` | updatedAt | 수정일 기준 정렬 |
| `idx_createdBy` | createdBy | 생성자별 조회 |

> 📌 시스템 인덱스는 삭제할 수 없습니다.

**쿼리 최적화 팁**

### 1. 필요한 필드만 조회하기

❌ **나쁜 예:**

```
// 모든 필드 조회 (불필요한 데이터 전송)
const users = await apiClient.get('/data/users');
```

✅ **좋은 예:**

```
// 필요한 필드만 지정 (API에서 지원 예정)
// 현재는 프론트엔드에서 필터링
const users = await apiClient.get('/data/users');
const userNames = users.data.data.items.map(u => ({ id: u._id, name: u.name }));
```

### 2. 페이지네이션 활용

❌ **나쁜 예:**

```
// 모든 데이터 한 번에 조회
const todos = await apiClient.get('/data/todos');
```

✅ **좋은 예:**

```
// 페이지 단위로 조회
const todos = await apiClient.get('/data/todos?page=1&limit=20');
```

### 3. 서버 사이드 필터링 사용

❌ **나쁜 예:**

```
// 클라이언트에서 필터링 (모든 데이터 전송)
const allTodos = await apiClient.get('/data/todos');
const completed = allTodos.data.data.items.filter(t => t.completed);
```

✅ **좋은 예:**

```
// 서버에서 필터링 (필요한 데이터만 전송)
const filters = JSON.stringify({ completed: true });
const completed = await apiClient.get(`/data/todos?andFilters=${encodeURIComponent(filters)}`);
```

### 4. 인덱스가 있는 필드로 정렬

✅ **좋은 예:**

```
// createdAt에는 시스템 인덱스가 있음
const latest = await apiClient.get('/data/todos?sortBy=createdAt&sortDirection=desc');
```

---

---

### 데이터 관리

**대량 데이터 작업**

### 배치 생성 (정식 버전 예정)

현재는 API를 반복 호출하여 처리해야 합니다:

```
const items = [
  { title: "할 일 1", completed: false },
  { title: "할 일 2", completed: false },
  { title: "할 일 3", completed: false }
];

// 순차 처리
for (const item of items) {
  await todosApi.create(item);
}

// 또는 병렬 처리
await Promise.all(items.map(item => todosApi.create(item)));
```

> **정식 버전 업데이트 예정:** 배치 생성 API가 추가될 예정입니다.

**데이터 마이그레이션**

### 시나리오: 기존 필드 이름 변경

**Question:** 필드 이름을 변경하면 기존 데이터는 어떻게 되나요?

**Answer:**

- ✅ 새 필드가 추가됩니다

- ⚠️ 기존 필드는 그대로 남아있습니다 (자동으로 삭제되지 않음)

- 📝 프론트엔드에서 두 필드를 모두 고려해야 합니다

**해결 방법:**

1. **새 필드 추가**

```
"todos 테이블에 dueDate 필드 추가해줘."
```

2. **프론트엔드에서 마이그레이션 처리**

```
// 기존 데이터는 deadline, 새 데이터는 dueDate 사용
const todos = await todosApi.list(userId);
const normalized = todos.map(t => ({
  ...t,
  dueDate: t.dueDate || t.deadline  // fallback 처리
}));
```

3. **기존 필드 삭제 (옵션)**

```
"todos 테이블에서 deadline 필드 삭제해줘."
```

---

---

## 프론트엔드 연동

백엔드가 완성되었나요? 이제 프론트엔드에서 API를 호출해보세요.

---

[https://slashpage.com/bkend/ywk9j7297883d2gpqvnd](https://slashpage.com/bkend/ywk9j7297883d2gpqvnd)

**포함된 내용:**

- 인증 API (회원가입, 로그인, 사용자 정보 조회)

- 데이터 CRUD API (생성, 조회, 수정, 삭제)

- 필터링, 검색, 정렬

- React/Vue 예시 코드

- 에러 처리

## 자주 묻는 질문

---

### **MCP 서버가 연결되지 않아요**

**< 진단 체크 리스트 >**

**✔️ Organization ID가 정확한가요?**

- 콘솔 우측 상단 프로필 > 조직 설정에서 확인

- 복사할 때 앞뒤 공백이 들어가지 않았는지 확인

**✔️ API Key가 유효한가요?**

- 콘솔 > 설정 > API 키에서 상태 확인

- 만료되었다면 새 키 생성

**✔️ 설정 파일 경로가 맞나요?**

- Claude Code: `~/.claude/settings.local.json` 또는 `.mcp.json`

- Cursor: `~/.cursor/mcp.json`

- Google Antigravity: `~/.gemini/antigravity/mcp_config.json`

**✔️ AI 에디터를 재시작했나요?**

- 설정 파일 변경 후 반드시 재시작 필요

### 해결 방법

**1단계: 로그 확인**

```
# Claude Code
cat ~/.claude/logs/mcp.log

# Cursor
cat ~/.cursor/logs/mcp.log
```

**2단계: 설정 파일 재확인**

```
# Claude Code
cat ~/.claude/settings.local.json

# 올바른 형식인지 확인
{
  "mcpServers": {
    "mcp-bkend": {
      "type": "http",
      "url": "https://api.bkend.ai/mcp",
      "headers": {
        "X-Organization-Id": "your_org_id",
        "X-Api-Key": "your_api_key"
      }
    }
  }
}
```

**3단계: 권한 확인**

```
# 설정 파일 권한 확인
ls -la ~/.claude/settings.local.json

# 읽기 권한이 없다면
chmod 644 ~/.claude/settings.local.json
```

### **프론트엔드에서 API 호출 실패해요**

**< 진단 체크리스트 >**

**✔️ 필수 헤더가 모두 포함되었나요?**

```
{
  "Authorization": "Bearer {access_token}",
  "X-Project-Id": "{project_id}",
  "X-Environment": "dev",
  "Content-Type": "application/json"
}
```

**✔️ CORS 에러인가요?**

- 개발 환경: Vite/Webpack 프록시 설정 필요

- 프로덕션: CORS는 자동으로 처리됨

**✔️ 인증 토큰이 유효한가요?**

- `localStorage.getItem('access_token')`으로 확인

- 만료되었다면 재로그인 필요

### 해결 방법

**Vite 프록시 설정:**

```
// vite.config.ts
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'https://api-enduser.bkend.ai',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
});
```

### **테이블 생성에 실패했어요**

**< 일반적인 원인 >**

1. **환경 배포가 완료되지 않음**

- 콘솔에서 환경 상태 확인: `dev` 환경이 `active`인지 확인

- 배포 실패 시 프로젝트를 다시 생성해야 함 (Beta 제약)

2. **스키마 유효성 검증 실패**

- 필드 타입이 올바른지 확인

- 필수 필드가 누락되지 않았는지 확인

3. **권한 문제**

- Organization ID가 올바른지 확인

- API Key가 만료되지 않았는지 확인

### 해결 방법

**1단계: 콘솔에서 환경 상태 확인**

- 프로젝트 선택 → 환경 탭 → `dev` 상태 확인

**2단계: 간단한 테이블로 테스트**

```
"test라는 이름으로 간단한 테이블 만들어줘. title 필드 하나만."
```

**3단계: 에러 메시지 공유**

- 🐛 버그 리포트  또는 지원팀에 에러 메시지 공유

---

**문서 버전:** 1.0.0  |  **최종 수정: **2025-12-04

For the site tree, see the [root Markdown](https://slashpage.com/bkend.md).
