API Gateway 실전 구현
학습 목표
- 주요 API Gateway 솔루션 비교
- Kong Gateway 실전 설정
- 인증/인가 구현
- Rate Limiting과 Circuit Breaker 적용
API Gateway 솔루션 비교
1. Kong Gateway
특징:
- Nginx 기반, 고성능
- 풍부한 플러그인 생태계
- OpenSource + Enterprise 버전
설치 (Docker):
docker run -d --name kong-database \
-e POSTGRES_USER=kong \
-e POSTGRES_DB=kong \
-e POSTGRES_PASSWORD=kong \
postgres:13
docker run -d --name kong \
--link kong-database:kong-database \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-p 8000:8000 \
-p 8443:8443 \
-p 8001:8001 \
kong:latest
서비스 등록:
# Product Service 등록
curl -i -X POST http://localhost:8001/services \
--data name=product-service \
--data url=http://product-api:3000
# Route 등록
curl -i -X POST http://localhost:8001/services/product-service/routes \
--data "paths[]=/api/products" \
--data "methods[]=GET" \
--data "methods[]=POST"
2. AWS API Gateway
특징:
- 완전 관리형 (Serverless)
- Lambda 통합 우수
- 자동 스케일링
Terraform 설정:
resource "aws_api_gateway_rest_api" "main" {
name = "ecommerce-api"
}
resource "aws_api_gateway_resource" "products" {
rest_api_id = aws_api_gateway_rest_api.main.id
parent_id = aws_api_gateway_rest_api.main.root_resource_id
path_part = "products"
}
resource "aws_api_gateway_method" "get_products" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.products.id
http_method = "GET"
authorization = "AWS_IAM"
}
resource "aws_api_gateway_integration" "lambda" {
rest_api_id = aws_api_gateway_rest_api.main.id
resource_id = aws_api_gateway_resource.products.id
http_method = aws_api_gateway_method.get_products.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.products.invoke_arn
}
3. Spring Cloud Gateway
특징:
- Spring 생태계 친화적
- Reactive (WebFlux 기반)
- 동적 라우팅
설정 (application.yml):
spring:
cloud:
gateway:
routes:
- id: product-service
uri: lb://PRODUCT-SERVICE
predicates:
- Path=/api/products/**
filters:
- StripPrefix=2
- name: CircuitBreaker
args:
name: productCircuitBreaker
fallbackUri: forward:/fallback/products
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=2
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
4. Traefik
특징:
- 클라우드 네이티브
- 자동 서비스 디스커버리
- Kubernetes 친화적
docker-compose.yml:
version: '3'
services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
product-service:
image: product-api:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.product.rule=PathPrefix(`/api/products`)"
- "traefik.http.services.product.loadbalancer.server.port=3000"
Kong Gateway 심화 구현
1. JWT 인증 플러그인
# JWT 플러그인 활성화
curl -X POST http://localhost:8001/services/product-service/plugins \
--data "name=jwt"
# Consumer 생성
curl -X POST http://localhost:8001/consumers \
--data "username=mobile-app"
# JWT Credential 생성
curl -X POST http://localhost:8001/consumers/mobile-app/jwt \
--data "key=app-key" \
--data "secret=super-secret"
JWT 토큰 생성 (Node.js):
import jwt from 'jsonwebtoken';
const token = jwt.sign(
{
iss: 'app-key', // Kong Consumer Key
userId: '12345',
role: 'customer',
},
'super-secret', // Kong Consumer Secret
{
algorithm: 'HS256',
expiresIn: '1h',
}
);
// API 호출 시 사용
const response = await fetch('http://localhost:8000/api/products', {
headers: {
Authorization: `Bearer ${token}`,
},
});
2. Rate Limiting
# Rate Limiting 플러그인 (분당 100 요청)
curl -X POST http://localhost:8001/services/product-service/plugins \
--data "name=rate-limiting" \
--data "config.minute=100" \
--data "config.policy=local"
# Consumer별로 다른 제한
curl -X POST http://localhost:8001/consumers/premium-user/plugins \
--data "name=rate-limiting" \
--data "config.minute=1000" # Premium은 1000 요청
3. Response Caching
# 응답 캐싱 (30초)
curl -X POST http://localhost:8001/services/product-service/plugins \
--data "name=proxy-cache" \
--data "config.response_code=200" \
--data "config.request_method=GET" \
--data "config.content_type=application/json" \
--data "config.cache_ttl=30" \
--data "config.strategy=memory"
4. Request Transformation
요청/응답 변환:
# 헤더 추가
curl -X POST http://localhost:8001/services/product-service/plugins \
--data "name=request-transformer" \
--data "config.add.headers=X-Internal-Request:true"
# 응답에서 민감한 정보 제거
curl -X POST http://localhost:8001/services/product-service/plugins \
--data "name=response-transformer" \
--data "config.remove.json=password" \
--data "config.remove.json=ssn"
고급 패턴 구현
1. Backend for Frontend (BFF)
모바일과 웹에 다른 API 제공:
// Gateway에서 BFF 패턴 구현
app.get('/api/mobile/products', async (req, res) => {
const [products, recommendations] = await Promise.all([
productService.getProducts({ limit: 10 }),
recommendationService.getForMobile(req.user.id),
]);
// 모바일 최적화 응답
res.json({
products: products.map(p => ({
id: p.id,
name: p.name,
price: p.price,
thumbnail: p.images[0], // 첫 이미지만
})),
recommendations,
});
});
app.get('/api/web/products', async (req, res) => {
const [products, reviews, recommendations] = await Promise.all([
productService.getProducts({ limit: 50 }),
reviewService.getTopReviews(),
recommendationService.getForWeb(req.user.id),
]);
// 웹 최적화 응답 (더 많은 정보)
res.json({
products: products.map(p => ({
id: p.id,
name: p.name,
description: p.description, // 상세 설명
price: p.price,
images: p.images, // 모든 이미지
specs: p.specifications, // 상세 스펙
})),
topReviews: reviews,
recommendations,
});
});
2. GraphQL Gateway
여러 서비스를 GraphQL로 통합:
import { ApolloServer } from '@apollo/server';
import { buildSubgraphSchema } from '@apollo/subgraph';
import { gql } from 'graphql-tag';
const typeDefs = gql`
type Product {
id: ID!
name: String!
price: Float!
reviews: [Review]
}
type Review {
id: ID!
productId: ID!
rating: Int!
comment: String!
}
type Query {
product(id: ID!): Product
products(limit: Int): [Product]
}
`;
const resolvers = {
Query: {
product: async (_,{ id }, { dataSources }) => {
return dataSources.productAPI.getProduct(id);
},
products: async (_, { limit }, { dataSources }) => {
return dataSources.productAPI.getProducts(limit);
},
},
Product: {
reviews: async (product, _, { dataSources }) => {
return dataSources.reviewAPI.getReviews(product.id);
},
},
};
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
3. 동적 라우팅
런타임에 라우팅 규칙 변경:
class DynamicRouter {
private routes = new Map<string, RouteConfig>();
addRoute(path: string, config: RouteConfig) {
this.routes.set(path, config);
console.log(`Route added: ${path} -> ${config.target}`);
}
removeRoute(path: string) {
this.routes.delete(path);
}
async handle(req: Request, res: Response) {
const route = this.findMatchingRoute(req.path);
if (!route) {
return res.status(404).json({ error: 'Not found' });
}
// Circuit Breaker 체크
if (route.circuitBreaker?.isOpen()) {
return res.status(503).json({ error: 'Service unavailable' });
}
// 요청 전달
try {
const response = await this.forwardRequest(req, route);
return res.json(response.data);
} catch (error) {
route.circuitBreaker?.recordFailure();
throw error;
}
}
}
// Redis에서 라우팅 규칙 로드
const router = new DynamicRouter();
redis.subscribe('routing:update', (message) => {
const { action, path, config } = JSON.parse(message);
if (action === 'add') {
router.addRoute(path, config);
} else if (action === 'remove') {
router.removeRoute(path);
}
});
보안 Best Practices
1. CORS 설정
// Kong CORS 플러그인
curl -X POST http://localhost:8001/services/product-service/plugins \
--data "name=cors" \
--data "config.origins=https://www.example.com" \
--data "config.methods=GET,POST,PUT,DELETE" \
--data "config.headers=Authorization,Content-Type" \
--data "config.credentials=true" \
--data "config.max_age=3600"
2. IP Restriction
// IP 화이트리스트
curl -X POST http://localhost:8001/services/admin-api/plugins \
--data "name=ip-restriction" \
--data "config.allow=10.0.0.0/8,192.168.0.0/16"
3. Request Size Limiting
// 요청 크기 제한 (10MB)
curl -X POST http://localhost:8001/services/upload-service/plugins \
--data "name=request-size-limiting" \
--data "config.allowed_payload_size=10"
모니터링과 로깅
Prometheus 메트릭 수집
# Kong Prometheus 플러그인
curl -X POST http://localhost:8001/plugins \
--data "name=prometheus"
# Prometheus scrape config
scrape_configs:
- job_name: 'kong'
static_configs:
- targets: ['kong:8001']
Access 로그 분석
// ELK Stack으로 전송
{
"service": "product-service",
"method": "GET",
"path": "/api/products/123",
"status": 200,
"latency": 45,
"user_agent": "Mozilla/5.0...",
"ip": "192.168.1.100",
"timestamp": "2024-12-01T10:30:00Z"
}
핵심 정리
- Kong, AWS API Gateway, Spring Cloud Gateway 각각 장단점 존재
- JWT, Rate Limiting, Caching은 필수 기능
- BFF 패턴으로 클라이언트별 최적화
- 동적 라우팅으로 유연성 확보
- 보안 설정 (CORS, IP 제한, Size 제한) 필수
- Prometheus로 메트릭 수집, ELK로 로그 분석