6강 55분

API Gateway 실전 구현

Kong, AWS API Gateway, Spring Cloud Gateway 등 실제 프로덕션에서 사용되는 API Gateway를 비교하고 구현합니다.

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로 로그 분석