본문 바로가기
AI

MCP 고급 과정 - 커스텀 서버 개발과 프로덕션

by 에버리치60 2025. 12. 15.

MCP 고급 과정

⚡ MCP 고급 과정 - 커스텀 서버 개발과 프로덕션

고급 과정에 오신 것을 환영합니다!

이제 직접 MCP 서버를 만들고, 프로덕션 환경에 배포하는 방법을 배웁니다.

  • TypeScript로 커스텀 MCP 서버 개발
  • 인증 및 보안 구현
  • 성능 최적화 및 모니터링
  • Docker를 활용한 배포
  • 실전: 엔터프라이즈급 MCP 서버
📋 사전 준비
  • ✅ MCP 초급 + 중급 완료
  • ✅ JavaScript/TypeScript 기본
  • ✅ Node.js 개발 경험
  • ✅ Git 및 npm 사용법
0%
실습 1⭐⭐⭐⭐

첫 커스텀 MCP 서버 만들기

1단계: 프로젝트 초기화
mkdir my-mcp-server cd my-mcp-server npm init -y npm install @modelcontextprotocol/sdk npm install -D typescript @types/node tsx
2단계: tsconfig.json
{ "compilerOptions": { "target": "ES2022", "module": "Node16", "outDir": "./dist", "rootDir": "./src", "strict": true } }
3단계: 기본 서버 (src/index.ts)
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new Server({ name: "my-calculator", version: "1.0.0" }, { capabilities: { tools: {} } }); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [{ name: "calculate", description: "수학 계산", inputSchema: { type: "object", properties: { op: { type: "string", enum: ["add","sub","mul","div"] }, a: { type: "number" }, b: { type: "number" } }, required: ["op","a","b"] } }] })); server.setRequestHandler(CallToolRequestSchema, async (req) => { if (req.params.name === "calculate") { const {op,a,b} = req.params.arguments; let result; if(op==="add") result=a+b; else if(op==="sub") result=a-b; else if(op==="mul") result=a*b; else if(op==="div") result=a/b; return { content: [{ type: "text", text: `결과: ${result}` }] }; } throw new Error("알 수 없는 도구"); }); const transport = new StdioServerTransport(); await server.connect(transport);
4단계: 빌드 및 연결
npm run build # Claude Desktop 설정 { "my-calculator": { "command": "node", "args": ["/절대경로/my-mcp-server/dist/index.js"] } }
실습 2⭐⭐⭐⭐

외부 API 연동 - 날씨 서비스

날씨 도구 추가
npm install node-fetch // tools/weather.ts export async function getWeather(city: string) { const res = await fetch( `https://wttr.in/${city}?format=j1` ); const data = await res.json(); const curr = data.current_condition[0]; return `${city} 날씨: 온도: ${curr.temp_C}°C 습도: ${curr.humidity}% 날씨: ${curr.weatherDesc[0].value}`; }
캐싱 구현
const cache = new Map(); const TTL = 5 * 60 * 1000; // 5분 function getCached(key: string) { const item = cache.get(key); if (item && Date.now() - item.time < TTL) { return item.data; } return null; } function setCache(key: string, data: any) { cache.set(key, { data, time: Date.now() }); }
실습 3⭐⭐⭐⭐⭐

리소스와 프롬프트 구현

리소스 기능
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [{ uri: "config://settings", name: "서버 설정", mimeType: "application/json" }] })); server.setRequestHandler(ReadResourceRequestSchema, async (req) => { if (req.params.uri === "config://settings") { return { contents: [{ uri: req.params.uri, mimeType: "application/json", text: JSON.stringify({ version: "1.0.0", features: ["calculator", "weather"] }) }] }; } });
프롬프트 템플릿
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [{ name: "analyze_weather", description: "날씨 분석", arguments: [{ name: "city", required: true }] }] })); server.setRequestHandler(GetPromptRequestSchema, async (req) => { const { name, arguments: args } = req.params; if (name === "analyze_weather") { return { messages: [{ role: "user", content: { type: "text", text: `${args.city}의 날씨를 분석하고: 1. 현재 상태 2. 외출 추천 3. 건강 조언을 제공해주세요` } }] }; } });
실습 4⭐⭐⭐⭐⭐

보안 및 인증

환경 변수 관리
npm install dotenv // .env API_KEY=your_secret_key MAX_REQUESTS=60 // 코드 import dotenv from 'dotenv'; dotenv.config(); const API_KEY = process.env.API_KEY; if (!API_KEY) { throw new Error('API_KEY 필요'); }
Rate Limiting
class RateLimiter { private requests = new Map(); check(id: string, max: number, windowMs: number) { const now = Date.now(); const reqs = (this.requests.get(id) || []) .filter(t => now - t < windowMs); if (reqs.length >= max) return false; reqs.push(now); this.requests.set(id, reqs); return true; } } const limiter = new RateLimiter(); if (!limiter.check('user', 60, 60000)) { throw new Error('요청 한도 초과'); }
🔒 보안 체크리스트
  • API 키 하드코딩 금지
  • 모든 입력 검증
  • Rate Limiting 구현
  • HTTPS 사용
  • 정기 업데이트
실습 5⭐⭐⭐⭐⭐

Docker 배포

Dockerfile 작성
FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY dist ./dist CMD ["node", "dist/index.js"]
빌드 및 실행
# 이미지 빌드 docker build -t my-mcp-server . # 컨테이너 실행 docker run -d \ --name mcp-server \ -e API_KEY=your_key \ my-mcp-server # 로그 확인 docker logs mcp-server
docker-compose.yml
version: '3.8' services: mcp-server: build: . environment: - API_KEY=${API_KEY} - NODE_ENV=production restart: unless-stopped logging: driver: json-file options: max-size: "10m" max-file: "3"
실습 6⭐⭐⭐⭐⭐

실전: 엔터프라이즈 서버

프로젝트 구조
enterprise-mcp/ ├── src/ │ ├── index.ts │ ├── tools/ │ ├── resources/ │ ├── middleware/ │ ├── utils/ │ └── types/ ├── tests/ ├── docker/ ├── docs/ └── .env.example
모니터링 추가
// 메트릭 수집 class Metrics { private calls = 0; private errors = 0; private latencies: number[] = []; recordCall(duration: number) { this.calls++; this.latencies.push(duration); } recordError() { this.errors++; } getStats() { return { totalCalls: this.calls, errors: this.errors, avgLatency: this.latencies.reduce((a,b)=>a+b,0) / this.calls }; } }
헬스 체크
// 리소스로 제공 { uri: "health://status", name: "서버 상태", handler: () => ({ status: "healthy", uptime: process.uptime(), memory: process.memoryUsage(), version: "1.0.0" }) }
🎉 고급 과정 완료!

프로덕션급 MCP 서버를 개발할 수 있는 능력을 갖추었습니다!

🎓 고급 과정 완료 후 능력

  • ✅ TypeScript로 커스텀 MCP 서버 개발
  • ✅ 보안 및 인증 구현
  • ✅ 성능 최적화 및 모니터링
  • ✅ Docker 컨테이너화
  • ✅ 엔터프라이즈급 아키텍처
🚀 다음 단계
  • 실제 서비스에 배포하기
  • CI/CD 파이프라인 구축
  • 커뮤니티에 기여하기
  • 오픈소스 MCP 서버 개발

📚 추가 학습 자료

  • 공식 문서: modelcontextprotocol.io
  • GitHub: github.com/modelcontextprotocol
  • 예제: awesome-mcp-servers
  • 커뮤니티: Discord, Reddit