티스토리 뷰
작성일: 2025-11-02
카테고리: Platform Architecture, API Gateway, Kubernetes
난이도: 고급
TL;DR
- 문제: REST API 개발과 배포가 복잡하고, AI 에이전트와의 통합이 어려웠습니다.
- 해결: CloudFunction 기반 API 개발 + Kubernetes 자동 배포 + MCP Tool 통합을 하나의 플랫폼으로 제공했습니다.
- 핵심:
- Hot Reload: MongoDB Change Stream으로 1초 이내 배포
- State Machine: Phase 기반 리소스 생명주기 관리
- Multi-tenant DB: Gateway별 독립 MongoDB 데이터베이스
- Environment 분리: dev/staging/prod 환경 자동 생성
- AI 통합: CloudFunction을 MCP Tool로 변환하여 AI 에이전트가 직접 호출
- 결과: 코드 작성부터 배포, AI 통합까지 웹 콘솔 하나로 완결되는 통합 API Platform 완성
들어가며
imprun.dev는 "API 개발부터 AI 통합까지, 모든 것을 하나로" 제공하는 Kubernetes 기반 API Platform입니다.
개발자는 웹 브라우저에서 CloudFunction 코드를 작성하고, 버튼 클릭 한 번으로 REST API를 배포하며, AI 에이전트가 이 API를 Tool로 사용할 수 있게 만들 수 있습니다.
graph LR
Dev["코드 작성<br/>(Web Console)"]
Deploy["1초 배포<br/>(Hot Reload)"]
API["REST API<br/>(Gateway)"]
AI["AI 통합<br/>(MCP Tool)"]
Dev --> Deploy
Deploy --> API
API --> AI
style Dev stroke:#2563eb,stroke-width:2px
style Deploy stroke:#ea580c,stroke-width:2px
style API stroke:#16a34a,stroke-width:2px
style AI stroke:#7c3aed,stroke-width:2px
이 글에서 다룰 내용:
- imprun Platform의 전체 아키텍처
- Hot Reload 배포 메커니즘
- State Machine 기반 리소스 관리
- Multi-tenant Database 설계
- AI Gateway와 MCP Tool 통합
1. Platform 개요
1.1 imprun이 해결하는 문제
기존 REST API 개발의 불편함:
- 로컬 개발 환경 구축 (Node.js, DB, Redis 설치)
- Git 커밋/푸시 → CI/CD 파이프라인 대기 (5~10분)
- Kubernetes 배포 설정 작성 (Deployment, Service, Ingress)
- 환경별(dev/staging/prod) 설정 관리
- AI 에이전트와 통합하려면 별도 서버 필요
imprun의 접근:
- ✅ 웹 브라우저가 IDE (Monaco Editor)
- ✅ 코드 저장 즉시 배포 (1초 이내, Hot Reload)
- ✅ Kubernetes 리소스 자동 생성
- ✅ 3개 환경(dev/staging/prod) 자동 구성
- ✅ CloudFunction → MCP Tool 자동 변환
1.2 핵심 구성 요소
graph TB
subgraph "Web Console"
Console["Next.js 15 Web Console"]
Monaco["Monaco Editor"]
end
subgraph "API Server"
ApiServer["NestJS API Server"]
MongoDB["System DB (MongoDB)"]
Redis["Session Store (Redis)"]
end
subgraph "Kubernetes Cluster"
subgraph "Gateway Runtime"
Runtime1["Runtime Pod (dev)"]
Runtime2["Runtime Pod (staging)"]
Runtime3["Runtime Pod (prod)"]
end
GatewayDB["Gateway DB (MongoDB)"]
APISIX["APISIX Ingress"]
end
subgraph "AI Gateway"
AIGateway["AI Gateway Service"]
MCPProxy["MCP Proxy"]
end
Console --> ApiServer
Monaco --> ApiServer
ApiServer --> MongoDB
ApiServer --> Redis
ApiServer -->|Kubernetes API| Runtime1
ApiServer -->|Kubernetes API| Runtime2
ApiServer -->|Kubernetes API| Runtime3
Runtime1 -.->|Change Stream| GatewayDB
Runtime2 -.->|Change Stream| GatewayDB
Runtime3 -.->|Change Stream| GatewayDB
APISIX -->|Route| Runtime1
APISIX -->|Route| Runtime2
APISIX -->|Route| Runtime3
AIGateway --> MCPProxy
MCPProxy -->|HTTP Call| APISIX
style Console stroke:#2563eb,stroke-width:2px
style ApiServer stroke:#16a34a,stroke-width:2px
style Runtime1 stroke:#ea580c,stroke-width:2px
style AIGateway stroke:#7c3aed,stroke-width:2px
계층별 역할:
- Web Console: CloudFunction 작성 및 배포 UI
- API Server: Platform 전체 관리 (Gateway, Function, User, Auth)
- Kubernetes Cluster: Runtime Pod 실행 및 트래픽 라우팅
- AI Gateway: LLM 통합 및 MCP Tool 오케스트레이션
2. 핵심 아키텍처 패턴
2.1 Hot Reload: MongoDB Change Stream
전통적인 배포 방식:
코드 변경 → Git Push → CI 빌드 → 이미지 빌드 → Kubernetes 배포 → Pod 재시작 (5~10분)imprun의 Hot Reload:
코드 저장 → MongoDB 저장 → Change Stream 감지 → VM 재로드 (1초 이내)구현 세부사항:
// 1. FunctionService: 사용자가 Function 배포
async publish(gatewayId: string, functionId: string, environment: string) {
const func = await this.findOne(gatewayId, functionId)
// Gateway 전용 Database의 __published_functions 컬렉션에 저장
const gatewayDb = this.getGatewayDb(gatewayId)
await gatewayDb.collection('__published_functions').updateOne(
{ name: func.name, environment },
{
$set: {
name: func.name,
code: func.code,
environment,
publishedAt: new Date()
}
},
{ upsert: true }
)
// ✨ Change Stream이 자동으로 Runtime Pod에 전파
}
// 2. Runtime Pod: Change Stream 감지 및 Hot Reload
const changeStream = db.collection('__published_functions').watch()
changeStream.on('change', async (change) => {
if (change.operationType === 'insert' || change.operationType === 'update') {
const func = change.fullDocument
// VM Context 재생성 (Cold Start 없음!)
this.vmContexts.set(func.name, this.createVMContext(func.code))
console.log(`✅ Hot Reload: ${func.name} (${Date.now() - func.publishedAt.getTime()}ms)`)
}
})
Hot Reload의 장점:
- 빠른 배포: 1초 이내 코드 반영
- 무중단: Pod 재시작 없이 VM Context만 재생성
- 비용 절감: 이미지 빌드/푸시 불필요
- 개발 경험: 즉각적인 피드백으로 생산성 향상
2.2 State Machine: Phase 기반 리소스 관리
API Gateway 생성 시 여러 Kubernetes 리소스(Namespace, Deployment, Service, Ingress, Database)를 순차적으로 프로비저닝해야 합니다. 이를 State Machine + Background Task 패턴으로 안정적으로 관리합니다.
Gateway Phase Lifecycle:
Creating → Created → Starting → Started
↓ ↓ ↓ ↓
Deleting ← ← ← ← ← ← ← ← ← ← DeletedPhase별 작업:
| Phase | 작업 내용 | 완료 조건 |
|---|---|---|
| Creating | Namespace, Database, Environment 3개 생성 | 모든 Environment가 Created 상태 |
| Created | Environment별 Runtime Pod 배포 | 모든 Pod가 Ready 상태 |
| Starting | APISIX Route 생성, Health Check | 모든 Pod 응답 확인 |
| Started | 정상 운영 중 | - |
Background Task 구현:
@Injectable()
export class ApiGatewayTaskService {
@Cron('*/1 * * * * *') // 매초 실행
async tick() {
await this.handleCreatingPhase()
await this.handleCreatedPhase()
await this.handleStartingPhase()
}
async handleCreatingPhase() {
// Optimistic Lock: 처리 중이지 않은 Gateway만 선택
const gateway = await this.db.findOneAndUpdate(
{
phase: 'Creating',
lockedAt: { $lt: new Date(Date.now() - 60000) } // 60초 이상 지난 것만
},
{ $set: { lockedAt: new Date() } },
{ sort: { lockedAt: 1, updatedAt: 1 }, returnDocument: 'after' }
)
if (!gateway) return
try {
// 1. Namespace 생성 (멱등성 보장)
await this.k8sService.createNamespace(gateway.gatewayId)
// 2. Environment 3개 생성 (dev/staging/prod)
for (const envName of ['dev', 'staging', 'prod']) {
await this.environmentService.create({
gatewayId: gateway.gatewayId,
name: envName,
domain: `${gateway.gatewayId}-${envName}.api.imprun.dev`,
phase: 'Creating'
})
}
// 3. Gateway Database 생성
await this.databaseService.createGatewayDatabase(gateway.gatewayId)
// 4. 모든 Environment가 Created 상태인지 확인
const allCreated = await this.environmentService.checkAllCreated(gateway.gatewayId)
if (allCreated) {
// Phase 전환: Creating → Created
await this.db.updateOne(
{ gatewayId: gateway.gatewayId },
{
$set: {
phase: 'Created',
lockedAt: null
}
}
)
}
} finally {
// Lock 해제
await this.db.updateOne(
{ gatewayId: gateway.gatewayId },
{ $set: { lockedAt: new Date(0) } }
)
}
}
}
State Machine의 장점:
- 명확한 상태: 각 Gateway의 현재 상태를 명확히 파악
- 멱등성: 실패 시 재시도해도 안전
- 복구 가능: Lock timeout으로 자동 복구
- 확장 가능: 새로운 Phase 추가 용이
2.3 Optimistic Locking: 동시성 제어
여러 Background Task 인스턴스가 동시에 실행될 때, 같은 Gateway를 중복 처리하지 않도록 lockedAt 필드를 사용한 낙관적 락을 구현했습니다.
Optimistic Lock 패턴:
// 1. Lock 획득 시도 (findOneAndUpdate로 원자적 수행)
const gateway = await db.findOneAndUpdate(
{
phase: 'Creating',
lockedAt: { $lt: new Date(Date.now() - 60000) } // 60초 이상 지난 것만
},
{ $set: { lockedAt: new Date() } }, // 현재 시각으로 Lock
{ sort: { lockedAt: 1, updatedAt: 1 }, returnDocument: 'after' }
)
// 2. Lock 획득 실패 시 (다른 Task가 처리 중)
if (!gateway) return
// 3. 작업 수행
try {
await processGateway(gateway)
} finally {
// 4. Lock 해제 (INIT_TIME = new Date(0)으로 설정)
await db.updateOne(
{ gatewayId: gateway.gatewayId },
{ $set: { lockedAt: new Date(0) } }
)
}
Lock Timeout 전략:
- 정상 처리: 작업 완료 후
lockedAt = INIT_TIME설정 - 실패/타임아웃: 60초 경과 시 다른 Task가 재시도
- 멱등성 보장: 각 단계는 중복 실행되어도 안전하게 설계
3. Multi-tenant Database 설계
3.1 Database 분리 전략
imprun은 System Database와 Gateway Database를 분리하여 데이터 격리와 성능을 보장합니다.
graph LR
subgraph "System Database (sys_db)"
User["User"]
ApiGateway["ApiGateway"]
Environment["Environment"]
Function["Function (코드)"]
Application["Application"]
ApplicationGrant["ApplicationGrant"]
end
subgraph "Gateway Database (gatewayid_hometax)"
Published["__published_functions"]
UserData["users (사용자 정의)"]
TaxData["taxes (사용자 정의)"]
end
subgraph "Gateway Database (gatewayid_shopping)"
Published2["__published_functions"]
Product["products (사용자 정의)"]
Order["orders (사용자 정의)"]
end
ApiServer["API Server"] --> User
ApiServer --> ApiGateway
ApiServer --> Environment
ApiServer --> Function
Runtime1["Runtime Pod (hometax-dev)"] -.->|Change Stream| Published
Runtime1 -.->|"imp.db()"| UserData
Runtime1 -.->|"imp.db()"| TaxData
Runtime2["Runtime Pod (shopping-prod)"] -.->|Change Stream| Published2
Runtime2 -.->|"imp.db()"| Product
Runtime2 -.->|"imp.db()"| Order
style ApiServer stroke:#16a34a,stroke-width:2px
style Runtime1 stroke:#ea580c,stroke-width:2px
style Runtime2 stroke:#ea580c,stroke-width:2px
System Database (sys_db):
- Platform 관리 데이터
- User, ApiGateway, Environment, Function, Application, ApplicationGrant
- API Server만 접근
Gateway Database (gatewayid_{gatewayId}):
- Gateway별 독립 데이터베이스
__published_functions: 배포된 CloudFunction (Change Stream 대상)- 사용자 정의 컬렉션: CloudFunction에서 자유롭게 생성
- Runtime Pod만 접근 (
imp.db()API)
3.2 Database ACL
CloudFunction에서는 imp.db() API를 통해 Gateway Database에 접근하며, ACL 규칙으로 권한을 제어합니다.
// CloudFunction 코드 예시
export default async function(req, res) {
// imp.db()는 현재 Gateway의 Database에만 접근
const users = await imp.db().collection('users').find({}).toArray()
// ❌ 다른 Gateway의 Database 접근 불가
// ❌ System Database 접근 불가
return res.json({ users })
}
ACL 규칙:
- ✅ 현재 Gateway의 Database만 접근 가능
- ✅ 모든 컬렉션 읽기/쓰기 허용
- ❌
__published_functions컬렉션 수정 금지 - ❌ 다른 Gateway Database 접근 금지
- ❌ System Database 접근 금지
4. Environment 아키텍처
4.1 dev/staging/prod 자동 구성
API Gateway 생성 시 3개 Environment가 자동으로 생성되며, 각각 독립적인 Runtime Pod와 도메인을 가집니다.
graph TB
Gateway["API Gateway: hometax"]
subgraph "Environment: dev"
EnvDev["Environment (dev)"]
PodDev["Runtime Pod"]
DomainDev["hometax.dev.api.imprun.dev"]
end
subgraph "Environment: staging"
EnvStaging["Environment (staging)"]
PodStaging["Runtime Pod"]
DomainStaging["hometax.staging.api.imprun.dev"]
end
subgraph "Environment: prod"
EnvProd["Environment (prod)"]
PodProd["Runtime Pod"]
DomainProd["hometax.prod.api.imprun.dev"]
end
Gateway --> EnvDev
Gateway --> EnvStaging
Gateway --> EnvProd
EnvDev --> PodDev
EnvStaging --> PodStaging
EnvProd --> PodProd
PodDev --> DomainDev
PodStaging --> DomainStaging
PodProd --> DomainProd
style Gateway stroke:#2563eb,stroke-width:2px
style EnvDev stroke:#16a34a,stroke-width:2px
style EnvStaging stroke:#ea580c,stroke-width:2px
style EnvProd stroke:#dc2626,stroke-width:2px
Environment Entity 구조:
{
_id: ObjectId,
gatewayId: "hometax",
name: "dev" | "staging" | "prod",
domain: "hometax.dev.api.imprun.dev",
customDomain?: "api.hometax.com",
authType: "key-auth" | "none",
plugins: [...], // APISIX Plugins
state: "active" | "inactive",
phase: "Creating" | "Created",
createdAt: Date,
updatedAt: Date
}
4.2 Promotion Pipeline
CloudFunction을 dev → staging → prod로 순차적으로 승격할 수 있습니다.
// Promotion API
POST /api-gateways/:gatewayId/functions/:functionId/promote
{
from: "dev",
to: "staging"
}
// 구현
async promote(gatewayId: string, functionId: string, from: string, to: string) {
// 1. 소스 Environment에서 Function 조회
const func = await this.findOnePublished(gatewayId, functionId, from)
if (!func) {
throw new NotFoundException(`Function not found in ${from}`)
}
// 2. 타겟 Environment에 배포
await this.publish(gatewayId, functionId, to)
// 3. 배포 히스토리 기록
await this.deploymentHistoryService.create({
gatewayId,
functionId,
environment: to,
version: func.version,
promotedFrom: from
})
}
5. AI Gateway 통합
5.1 MCP (Model Context Protocol) Tool
CloudFunction을 AI 에이전트가 호출할 수 있는 MCP Tool로 변환합니다.
graph LR
User["사용자"]
AIGateway["AI Gateway"]
LLM["LLM (Claude, GPT)"]
MCPProxy["MCP Proxy"]
Function["CloudFunction"]
User -->|"세금 계산해줘"| AIGateway
AIGateway -->|Chat Request| LLM
LLM -->|Tool Call| MCPProxy
MCPProxy -->|HTTP POST| Function
Function -->|Result| MCPProxy
MCPProxy -->|Result| LLM
LLM -->|Final Answer| AIGateway
AIGateway -->|Response| User
style User stroke:#2563eb,stroke-width:2px
style LLM stroke:#7c3aed,stroke-width:2px
style Function stroke:#16a34a,stroke-width:2px
MCP Tool 등록:
// 1. CloudFunction 작성
export default async function calculateTax(req, res) {
const { income, deductions } = req.body
const tax = (income - deductions) * 0.22
return res.json({ tax })
}
// 2. MCP Tool로 등록
POST /ai-gateway/mcp-tools
{
"name": "calculate_tax",
"description": "소득세를 계산합니다",
"functionUrl": "https://hometax.prod.api.imprun.dev/calculateTax",
"inputSchema": {
"type": "object",
"properties": {
"income": { "type": "number" },
"deductions": { "type": "number" }
},
"required": ["income", "deductions"]
}
}
// 3. AI Chat에서 사용
POST /ai-gateway/chat
{
"messages": [
{ "role": "user", "content": "연봉 5000만원, 공제 1000만원일 때 세금 계산해줘" }
]
}
// LLM이 자동으로 calculate_tax Tool 호출 → 결과 반환
5.2 Multi-LLM Support
AI Gateway는 여러 LLM Provider를 지원하며, Adaptor 패턴으로 통일된 인터페이스를 제공합니다.
// Adaptor Interface
interface IAdaptor {
chatCompletion(params: ChatCompletionParams): Promise<ChatCompletionResponse>
}
// OpenAI Adaptor
export class OpenAIAdaptor implements IAdaptor {
async chatCompletion(params) {
const response = await this.openai.chat.completions.create({
model: params.model,
messages: params.messages,
tools: params.tools
})
return this.normalize(response)
}
}
// Anthropic Adaptor
export class AnthropicAdaptor implements IAdaptor {
async chatCompletion(params) {
const response = await this.anthropic.messages.create({
model: params.model,
messages: params.messages,
tools: params.tools
})
return this.normalize(response)
}
}
// Factory Pattern
export class AdaptorFactory {
create(provider: string): IAdaptor {
switch (provider) {
case 'openai': return new OpenAIAdaptor()
case 'anthropic': return new AnthropicAdaptor()
case 'local': return new LocalAdaptor()
default: throw new Error(`Unknown provider: ${provider}`)
}
}
}
6. Application-Grant 인증 아키텍처
6.1 3-Layer 구조
외부 Consumer가 API Gateway를 사용하려면 Application을 생성하고, Grant를 통해 특정 Environment에 접근 권한을 부여받습니다.
graph TB
subgraph "Layer 1: Application (Business)"
App["Application<br/>(모바일 앱)"]
end
subgraph "Layer 2: ApplicationGrant (Permission)"
Grant1["Grant<br/>(hometax-dev)"]
Grant2["Grant<br/>(shopping-prod)"]
end
subgraph "Layer 3: Environment (Infrastructure)"
Env1["Environment<br/>(hometax-dev)"]
Env2["Environment<br/>(shopping-prod)"]
end
App --> Grant1
App --> Grant2
Grant1 --> Env1
Grant2 --> Env2
style App stroke:#2563eb,stroke-width:2px
style Grant1 stroke:#16a34a,stroke-width:2px
style Grant2 stroke:#16a34a,stroke-width:2px
style Env1 stroke:#ea580c,stroke-width:2px
style Env2 stroke:#ea580c,stroke-width:2px
핵심 원칙:
- Application은 Gateway 독립적: 여러 Gateway/Environment 접근 가능
- Grant는 N:M 관계: 하나의 Application이 여러 Gateway/Environment에 Grant 보유
- API Key는 Grant당 1개: AES-256 암호화, 평문은 생성 시 1회만 노출
6.2 API Key 보안
// 1. API Key 생성 (ApplicationGrantService)
async create(dto: CreateApplicationGrantDto) {
const apiKey = this.generateApiKey() // 32바이트 랜덤
const encryptedKey = this.encrypt(apiKey) // AES-256-CBC
const grant = await this.db.insert({
applicationId: dto.applicationId,
gatewayId: dto.gatewayId,
environment: dto.environment,
credentialId: `${dto.gatewayId}-${dto.environment}-${dto.appId}`,
credentialSecret: encryptedKey,
active: true
})
// ✅ 평문 API Key는 생성 시 1회만 반환
return {
...grant,
apiKey // 이후 다시 조회 불가
}
}
// 2. APISIX Consumer 생성 (자동)
await this.apisixService.createOrUpdateConsumer(
region,
namespace,
environment,
{
username: grant.credentialId,
plugins: {
'key-auth': {
key: apiKey // APISIX에는 평문 전달
}
}
}
)
7. 전체 Request Flow
사용자 요청이 CloudFunction까지 도달하는 전체 흐름을 살펴보겠습니다.
sequenceDiagram
participant Client as Client
participant APISIX as APISIX Ingress
participant Runtime as Runtime Pod
participant GatewayDB as Gateway DB
participant VM as VM Context
Client->>APISIX: GET /calculateTax?income=5000
Note over APISIX: API Key 인증 (key-auth plugin)
APISIX->>Runtime: Forward Request
Note over Runtime: 1. Change Stream으로 최신 코드 로드됨
Runtime->>GatewayDB: Check __published_functions
GatewayDB-->>Runtime: Function Code
Runtime->>VM: Execute Function in VM Context
Note over VM: imp.db() 사용 가능
VM->>GatewayDB: imp.db().collection('users').find()
GatewayDB-->>VM: Query Result
VM-->>Runtime: Function Result
Runtime-->>APISIX: Response
APISIX-->>Client: Response
각 단계 설명:
- Client → APISIX: API Key 포함하여 요청
- APISIX 인증:
key-authPlugin으로 Consumer 확인 - APISIX → Runtime: 인증 성공 시 Runtime Pod로 라우팅
- Runtime: Change Stream으로 최신 코드 로드 완료 (Hot Reload)
- VM Context: Function 실행 (Sandbox 환경)
- imp.db(): Gateway Database 접근 (ACL 적용)
- Response: 결과 반환
8. 성능 및 확장성
8.1 Hot Reload 성능
측정 결과:
- MongoDB 저장 → Change Stream 감지: 평균 50ms
- VM Context 재생성: 평균 20ms
- 총 배포 시간: 70ms ~ 1초
전통적인 배포 대비:
- 이미지 빌드/푸시: 3~5분 → 제거
- Pod 재시작: 30초~1분 → 제거
- Health Check: 10초 → 제거
8.2 Multi-tenant 확장
Gateway별 격리:
- 독립 Database → 데이터 격리 및 성능 보장
- 독립 Runtime Pod → 리소스 격리 (CPU, Memory)
- 독립 도메인 → 트래픽 분산
Kubernetes HPA:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: hometax-dev-runtime
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hometax-dev-runtime
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
8.3 Background Task 독립 실행
각 TaskService는 독립적으로 Cron 실행되므로, 여러 Gateway를 동시에 프로비저닝할 수 있습니다.
// ApiGatewayTaskService
@Cron('*/1 * * * * *')
async tick() {
// Fire-and-Forget 패턴 (에러만 로깅)
this.handleCreatingPhase().catch((err) => this.logger.error(err))
this.handleDeletingPhase().catch((err) => this.logger.error(err))
}
// EnvironmentTaskService
@Cron('*/1 * * * * *')
async tick() {
this.handleCreatingPhase().catch((err) => this.logger.error(err))
this.handleDeletingPhase().catch((err) => this.logger.error(err))
}
// InstanceTaskService
@Cron('*/5 * * * * *')
async tick() {
this.handleStartingPhase().catch((err) => this.logger.error(err))
this.handleStoppingPhase().catch((err) => this.logger.error(err))
}
장점:
- 각 TaskService가 독립적으로 동작
- 하나의 Phase 실패가 다른 Phase에 영향 없음
- Optimistic Lock으로 동시성 제어
9. 실전 예시: 홈택스 API 구축
전체 아키텍처를 활용하여 홈택스 API를 구축하는 과정을 살펴보겠습니다.
9.1 API Gateway 생성
# Web Console에서 생성
POST /api-gateways
{
"name": "홈택스 API",
"regionId": "64a7b2c3...",
"bundle": {
"runtime": "nodejs20",
"memory": "512Mi",
"cpu": "500m"
}
}
# Response
{
"gatewayId": "hometax",
"phase": "Creating",
"environments": []
}
# Background Task가 자동으로:
# 1. Namespace 생성
# 2. dev/staging/prod Environment 3개 생성
# 3. Gateway Database 생성
# 4. Runtime Pod 3개 배포
# → 약 30초 후 phase: "Started"
9.2 CloudFunction 작성 및 배포
// Web Console Monaco Editor에서 작성
export default async function calculateTax(req, res) {
const { income, deductions } = req.body
// Validation
if (!income || !deductions) {
return res.status(400).json({ error: 'Missing required fields' })
}
// 세금 계산
const taxableIncome = income - deductions
const tax = taxableIncome * 0.22
// Database 저장
await imp.db().collection('tax_calculations').insertOne({
income,
deductions,
tax,
calculatedAt: new Date()
})
return res.json({ tax })
}
# dev 환경에 배포
POST /api-gateways/hometax/functions/calculateTax/publish
{ "environment": "dev" }
# ✅ 1초 이내에 배포 완료 (Hot Reload)
# 호출: GET https://hometax.dev.api.imprun.dev/calculateTax?income=5000&deductions=1000
9.3 Application 생성 및 API Key 발급
# 1. Application 생성
POST /applications
{
"name": "모바일 앱",
"description": "홈택스 모바일 앱",
"organization": "국세청"
}
# 2. Grant 생성 (dev 환경 접근 권한)
POST /application-grants
{
"applicationId": "64a7b2c3...",
"gatewayId": "hometax",
"environment": "dev"
}
# Response
{
"grantId": "64a7b2c4...",
"credentialId": "hometax-dev-app123",
"apiKey": "sk_live_abc123def456...", # ✅ 평문은 이번 1회만 노출
"credentialReference": {
"apisixConsumerId": "hometax-dev-app123"
}
}
9.4 API 호출 (Client)
# API Key로 인증하여 호출
curl -X POST https://hometax.dev.api.imprun.dev/calculateTax \
-H "apikey: sk_live_abc123def456..." \
-H "Content-Type: application/json" \
-d '{
"income": 50000000,
"deductions": 10000000
}'
# Response
{
"tax": 8800000
}
9.5 Promotion (dev → prod)
# dev 테스트 완료 후 staging으로 배포
POST /api-gateways/hometax/functions/dev%2FcalculateTax/deploy-to-stage
{
"targetStage": "staging"
}
# Response: staging/calculateTax 생성 (source.semver 상속)
# History: "Deployed from dev to staging (version: 1.0.0)"
# staging 테스트 완료 후 prod로 배포
POST /api-gateways/hometax/functions/staging%2FcalculateTax/deploy-to-stage
{
"targetStage": "prod"
}
# Response: prod/calculateTax 생성
# History: "Deployed from staging to prod (version: 1.0.0)"
# ✅ 각 배포 1초 이내 완료 (Hot Reload)
# ✅ Promotion Pipeline 활성화 시 stage 순서 자동 검증
9.6 AI 통합 (MCP Tool)
# 1. MCP Tool 등록
POST /ai-gateway/mcp-tools
{
"name": "calculate_tax",
"description": "소득세를 계산합니다",
"functionUrl": "https://hometax.prod.api.imprun.dev/calculateTax",
"inputSchema": {
"type": "object",
"properties": {
"income": { "type": "number", "description": "연간 소득" },
"deductions": { "type": "number", "description": "공제액" }
},
"required": ["income", "deductions"]
}
}
# 2. AI Chat에서 사용
POST /ai-gateway/chat
{
"messages": [
{ "role": "user", "content": "연봉 5천만원, 공제 1천만원일 때 세금 계산해줘" }
]
}
# AI가 자동으로 calculate_tax Tool 호출 → 결과 생성
# Response:
{
"choices": [
{
"message": {
"role": "assistant",
"content": "연봉 5천만원에서 공제 1천만원을 제외하면 과세 대상 소득은 4천만원입니다. 세율 22%를 적용하면 세금은 8,800,000원입니다."
}
}
]
}
10. 개발 경험 및 배운 점
10.1 기술적 도전
Hot Reload 구현의 어려움:
- 초기에는 WebSocket으로 코드를 전송했으나, 연결 안정성 문제 발생
- MongoDB Change Stream으로 전환하여 안정성과 순서 보장 확보
- VM Context 재생성 시 메모리 누수 방지 (명시적 가비지 컬렉션)
State Machine 복잡도:
- Phase 전환 조건이 복잡하여 초기 구현 시 무한 루프 발생
- Optimistic Lock으로 동시성 문제 해결
- 멱등성을 보장하는 코드 작성이 핵심
Multi-tenant Database 성능:
- Gateway가 1000개 이상으로 증가 시 MongoDB 연결 풀 부족
- Connection Pooling 전략 개선 (Gateway당 최소 연결 수 제한)
- Database Sharding 도입 고려 중
10.2 아키텍처 선택의 trade-off
Hot Reload vs. 이미지 빌드:
- 장점: 빠른 배포, 개발 경험 향상
- 단점: 코드 버전 관리 어려움 (Git과 분리)
- 해결: Deployment History 기록, Git 통합 계획
State Machine vs. Event-Driven:
- 장점: 명확한 상태, 복구 용이
- 단점: Polling 방식으로 리소스 사용량 증가
- 해결: Cron 주기 최적화, Phase별 독립 처리
Multi-tenant DB vs. Shared DB:
- 장점: 완전한 데이터 격리, 성능 보장
- 단점: Database 수 증가로 관리 복잡도 증가
- 해결: 자동화된 Database 생성/삭제, Monitoring 강화
11. 향후 계획
11.1 단기 계획 (1~3개월)
Enhanced Observability:
- CloudFunction 실행 로그 수집 (Loki)
- Metrics 수집 (Prometheus)
- Distributed Tracing (Jaeger)
Testing Infrastructure:
- E2E 테스트 자동화
- Performance 테스트 (K6)
- Chaos Engineering (실패 시나리오 테스트)
11.2 장기 계획 (6개월~1년)
Secret Management:
- Kubernetes Secret 통합
- Vault 도입 검토
- API Key Rotation
Performance Optimization:
- Function Cold Start 최소화
- Database Query 최적화
- CDN 통합
Developer Experience:
- CLI 도구 개발
- Local Development 환경 개선
- VSCode Extension
마치며
imprun Platform은 Hot Reload, State Machine, Multi-tenant Database, AI 통합이라는 4가지 핵심 아키텍처 패턴으로 구축되었습니다.
핵심 가치:
- 빠른 배포: 1초 이내 코드 반영으로 개발 생산성 극대화
- 안정적 운영: State Machine과 Optimistic Lock으로 복구 가능한 시스템
- 완전한 격리: Gateway별 독립 Database와 Runtime으로 Multi-tenant 환경 보장
- AI 통합: CloudFunction을 MCP Tool로 변환하여 AI 에이전트 활용
선택한 이유:
- Event-Driven 대신 State Machine을 선택한 이유는 명확한 상태 관리와 복구 가능성
- 이미지 빌드 대신 Hot Reload를 선택한 이유는 개발 경험 향상
- Shared Database 대신 Multi-tenant Database를 선택한 이유는 완전한 데이터 격리
피한 이유:
- Saga Pattern은 현재 아키텍처에서 과도한 복잡도 추가
- Event-Driven은 디버깅이 어렵고 순서 보장이 어려움
- Blue-Green Deployment는 Hot Reload로 무중단 배포 가능
달성한 결과:
- 배포 시간: 5~10분 → 1초 이내 (99% 감소)
- Gateway 생성 시간: 수동 30분 → 자동 30초 (98% 감소)
- 개발자 만족도: Hot Reload로 즉각적인 피드백
- AI 통합: CloudFunction → MCP Tool 자동 변환
관련 블로그:
'실제 경험과 인사이트를 AI와 함께 정리한 글' 카테고리의 다른 글
| Saga Pattern 소개: 언제 사용하고, 언제 피해야 하나? (0) | 2025.11.02 |
|---|---|
| API Gateway 생성 실패 대응: Timeout과 Graceful Degradation 패턴 (0) | 2025.11.02 |
| API Platform의 Consumer 인증 설계: Application-Grant 아키텍처 (0) | 2025.11.02 |
| API Gateway의 Consumer: 인증의 시작점 (0) | 2025.11.02 |
| 분산 환경에서 Optimistic Lock으로 동시성 제어하기 (0) | 2025.11.01 |
- Total
- Today
- Yesterday
- Next.js
- Rag
- authentication
- LLM
- LangChain
- Ontology
- Go
- SHACL
- ai 개발 도구
- Developer Tools
- 개발 도구
- api gateway
- AI
- Kubernetes
- frontend
- troubleshooting
- Claude
- authorization
- AI agent
- workflow
- Tailwind CSS
- PYTHON
- security
- Tax Analysis
- claude code
- AI Development
- backend
- architecture
- react
- knowledge graph
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |