Skip to content

필사 모드: API Gateway 완전 가이드 2025: Kong, Envoy, AWS API Gateway, 인증/레이트리밋/모니터링

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

목차

1. 왜 API Gateway가 필요한가

1.1 마이크로서비스의 Cross-Cutting Concerns

마이크로서비스 아키텍처에서는 수십~수백 개의 서비스가 독립적으로 운영됩니다. 각 서비스마다 인증, 로깅, 레이트 리밋 등을 개별 구현하면 중복과 불일치가 발생합니다.

API Gateway 없는 세계:

[클라이언트]

│ │ │

│ │ └──── [서비스 A] (자체 인증, 자체 로깅, 자체 레이트 리밋)

│ └─────── [서비스 B] (자체 인증, 자체 로깅, 자체 레이트 리밋)

└────────── [서비스 C] (자체 인증, 자체 로깅, 자체 레이트 리밋)

중복! 불일치! 관리 불가!

API Gateway가 있는 세계:

[클라이언트]

┌────┴─────────────────────────┐

│ API Gateway │

│ ┌─────────────────────────┐ │

│ │ 인증 / 인가 │ │

│ │ 레이트 리밋 │ │

│ │ 요청 변환 │ │

│ │ 캐싱 │ │

│ │ 로깅 / 모니터링 │ │

│ │ 서킷 브레이커 │ │

│ │ 로드 밸런싱 │ │

│ └─────────────────────────┘ │

└──────┬───────┬───────┬───────┘

│ │ │

[서비스A] [서비스B] [서비스C]

(비즈니스 (비즈니스 (비즈니스

로직만) 로직만) 로직만)

1.2 API Gateway 패턴

3가지 핵심 Gateway 패턴:

1. Routing Pattern (라우팅)

클라이언트 요청을 올바른 백엔드 서비스로 전달

/api/users → User Service

/api/orders → Order Service

/api/products → Product Service

2. Aggregation Pattern (집약)

여러 서비스의 응답을 하나로 합쳐서 반환

GET /api/dashboard →

User Service (프로필) +

Order Service (최근 주문) +

Notification Service (알림)

→ 단일 JSON 응답

3. Offloading Pattern (오프로딩)

공통 기능을 서비스에서 Gateway로 이전

인증, SSL 종료, 압축, 캐싱, CORS 등

→ 서비스는 비즈니스 로직에만 집중

1.3 API Gateway가 처리하는 기능

| 기능 | 설명 |

|------|------|

| 라우팅 | URL 패턴/헤더 기반으로 요청을 올바른 서비스로 전달 |

| 인증/인가 | OAuth2, JWT, API Key 검증 |

| 레이트 리밋 | API 남용 방지. 사용자/IP/엔드포인트별 제한 |

| 요청/응답 변환 | 헤더 추가/제거, 바디 변환, 프로토콜 변환 |

| 캐싱 | 응답 캐싱으로 백엔드 부하 감소 |

| 로드 밸런싱 | 트래픽을 여러 인스턴스에 분산 |

| 서킷 브레이커 | 장애 서비스 격리. 연쇄 장애 방지 |

| 로깅/모니터링 | 접근 로그, 메트릭 수집, 분산 트레이싱 |

| SSL/TLS 종료 | HTTPS 처리를 Gateway에서 수행 |

| CORS | Cross-Origin 요청 정책 관리 |

| 카나리/A-B 라우팅 | 트래픽 비율 기반 배포 |

| 웹소켓/gRPC | 다양한 프로토콜 지원 |

2. Kong Deep Dive

2.1 Kong 아키텍처

Kong Architecture:

[클라이언트]

┌────┴────────────────────────────────┐

│ Kong Gateway │

│ │

│ ┌──────────────────────────────┐ │

│ │ Kong Core (OpenResty) │ │

│ │ Nginx + LuaJIT │ │

│ └──────────┬───────────────────┘ │

│ │ │

│ ┌──────────┴───────────────────┐ │

│ │ Plugin Layer │ │

│ │ │ │

│ │ Auth │ Rate │ Logging │ ... │ │

│ │ │ Limit │ │ │ │

│ └──────────────────────────────┘ │

│ │ │

│ ┌──────────┴───────────────────┐ │

│ │ Data Store │ │

│ │ PostgreSQL │ Cassandra │ │

│ │ (또는 DB-less Mode) │ │

│ └──────────────────────────────┘ │

└──────┬──────────┬──────────┬────────┘

│ │ │

[Service A] [Service B] [Service C]

2.2 Kong DB-less Mode (Declarative Config)

kong.yml - DB-less Declarative Configuration

_format_version: "3.0"

_transform: true

services:

- name: user-service

url: http://user-service:8080

routes:

- name: user-routes

paths:

- /api/v1/users

methods:

- GET

- POST

- PUT

- DELETE

strip_path: false

plugins:

- name: rate-limiting

config:

minute: 100

hour: 5000

policy: local

- name: jwt

config:

uri_param_names:

- token

claims_to_verify:

- exp

- name: order-service

url: http://order-service:8080

routes:

- name: order-routes

paths:

- /api/v1/orders

strip_path: false

plugins:

- name: rate-limiting

config:

minute: 50

hour: 2000

- name: request-transformer

config:

add:

headers:

- "X-Request-Source:api-gateway"

- "X-Forwarded-Service:order-service"

- name: product-service

url: http://product-service:8080

routes:

- name: product-routes

paths:

- /api/v1/products

strip_path: false

plugins:

- name: proxy-cache

config:

response_code:

- 200

request_method:

- GET

content_type:

- "application/json"

cache_ttl: 300

strategy: memory

Global Plugins

plugins:

- name: correlation-id

config:

header_name: X-Request-ID

generator: uuid

echo_downstream: true

- name: prometheus

config:

per_consumer: true

- name: file-log

config:

path: /dev/stdout

reopen: true

2.3 Kong 주요 플러그인

Kong Plugin 카테고리:

Authentication:

├── jwt - JWT 토큰 검증

├── oauth2 - OAuth2 서버 내장

├── key-auth - API Key 인증

├── basic-auth - Basic 인증

├── hmac-auth - HMAC 서명 인증

├── ldap-auth - LDAP/AD 인증

└── openid-connect - OIDC (Enterprise)

Security:

├── cors - Cross-Origin 정책

├── ip-restriction - IP 화이트/블랙리스트

├── bot-detection - 봇 탐지

├── acl - Access Control Lists

└── mtls-auth - mTLS 인증

Traffic Control:

├── rate-limiting - 레이트 리밋

├── request-size-limiting - 요청 크기 제한

├── request-termination - 요청 차단/유지보수 모드

└── proxy-cache - 응답 캐싱

Transformations:

├── request-transformer - 요청 헤더/바디 변환

├── response-transformer - 응답 헤더/바디 변환

├── correlation-id - 요청 추적 ID

└── grpc-gateway - REST to gRPC 변환

Observability:

├── prometheus - Prometheus 메트릭

├── datadog - Datadog APM 통합

├── zipkin - 분산 트레이싱

├── file-log - 파일 로깅

├── http-log - HTTP 기반 로그 전송

└── opentelemetry - OTel 통합

2.4 Kong Custom Plugin (Lua)

-- custom-auth-plugin/handler.lua

local BasePlugin = require "kong.plugins.base_plugin"

local CustomAuthHandler = BasePlugin:extend()

CustomAuthHandler.VERSION = "1.0.0"

CustomAuthHandler.PRIORITY = 1000 -- 실행 순서

function CustomAuthHandler:new()

CustomAuthHandler.super.new(self, "custom-auth")

end

function CustomAuthHandler:access(conf)

CustomAuthHandler.super.access(self)

-- 1. API Key 추출

local api_key = kong.request.get_header("X-API-Key")

if not api_key then

return kong.response.exit(401, {

message = "Missing API Key"

})

end

-- 2. 외부 인증 서비스 호출

local httpc = require("resty.http").new()

local res, err = httpc:request_uri(conf.auth_service_url, {

method = "POST",

headers = {

["Content-Type"] = "application/json",

},

body = '{"api_key":"' .. api_key .. '"}',

timeout = conf.timeout or 5000,

})

if not res then

kong.log.err("Auth service error: ", err)

return kong.response.exit(503, {

message = "Auth service unavailable"

})

end

if res.status ~= 200 then

return kong.response.exit(403, {

message = "Invalid API Key"

})

end

-- 3. 인증 정보를 업스트림 헤더에 추가

local cjson = require "cjson.safe"

local body = cjson.decode(res.body)

if body then

kong.service.request.set_header("X-Consumer-ID", body.consumer_id or "")

kong.service.request.set_header("X-Consumer-Plan", body.plan or "free")

end

end

return CustomAuthHandler

-- custom-auth-plugin/schema.lua

local typedefs = require "kong.db.schema.typedefs"

return {

name = "custom-auth",

fields = {

{ consumer = typedefs.no_consumer },

{ protocols = typedefs.protocols_http },

{ config = {

type = "record",

fields = {

{ auth_service_url = {

type = "string",

required = true,

default = "http://auth-service:8080/validate"

}},

{ timeout = {

type = "number",

default = 5000

}},

{ cache_ttl = {

type = "number",

default = 60

}},

},

}},

},

}

3. Envoy Proxy Deep Dive

3.1 Envoy 아키텍처

Envoy Core Architecture:

┌──────────────────────────────────────────────┐

│ Envoy Proxy │

│ │

│ Listener (포트 바인딩) │

│ │ │

│ ▼ │

│ Filter Chain (필터 체인) │

│ ├── Network Filters (L3/L4) │

│ │ ├── TCP Proxy │

│ │ ├── TLS Inspector │

│ │ └── HTTP Connection Manager │

│ │ │

│ └── HTTP Filters (L7) │

│ ├── Router (라우팅) │

│ ├── CORS │

│ ├── JWT Auth │

│ ├── Rate Limit │

│ ├── WASM Filter (커스텀) │

│ └── ... │

│ │

│ Route Configuration │

│ │ │

│ ▼ │

│ Cluster (업스트림 서비스 그룹) │

│ ├── Endpoint Discovery │

│ ├── Load Balancing │

│ ├── Health Checking │

│ ├── Circuit Breaking │

│ └── Outlier Detection │

└──────────────────────────────────────────────┘

3.2 Envoy 정적 설정

envoy.yaml - Static Configuration

static_resources:

listeners:

- name: main_listener

address:

socket_address:

address: 0.0.0.0

port_value: 8080

filter_chains:

- filters:

- name: envoy.filters.network.http_connection_manager

typed_config:

"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager

stat_prefix: ingress_http

codec_type: AUTO

access_log:

- name: envoy.access_loggers.stdout

typed_config:

"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog

log_format:

json_format:

timestamp: "%START_TIME%"

method: "%REQ(:METHOD)%"

path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"

status: "%RESPONSE_CODE%"

duration: "%DURATION%"

upstream: "%UPSTREAM_HOST%"

request_id: "%REQ(X-REQUEST-ID)%"

route_config:

name: local_route

virtual_hosts:

- name: api_service

domains: ["*"]

routes:

User Service Routes

- match:

prefix: "/api/v1/users"

route:

cluster: user_service

timeout: 10s

retry_policy:

retry_on: "5xx,connect-failure"

num_retries: 3

per_try_timeout: 3s

Order Service Routes

- match:

prefix: "/api/v1/orders"

route:

cluster: order_service

timeout: 15s

Product Service (with canary)

- match:

prefix: "/api/v1/products"

route:

weighted_clusters:

clusters:

- name: product_service_v1

weight: 90

- name: product_service_v2

weight: 10

http_filters:

- name: envoy.filters.http.cors

typed_config:

"@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors

- name: envoy.filters.http.jwt_authn

typed_config:

"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication

providers:

auth0:

issuer: "https://company.auth0.com/"

audiences:

- "https://api.company.com"

remote_jwks:

http_uri:

uri: "https://company.auth0.com/.well-known/jwks.json"

cluster: auth0_jwks

timeout: 5s

cache_duration: 600s

rules:

- match:

prefix: "/api/"

requires:

provider_name: "auth0"

- name: envoy.filters.http.router

typed_config:

"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

clusters:

- name: user_service

connect_timeout: 5s

type: STRICT_DNS

lb_policy: ROUND_ROBIN

health_checks:

- timeout: 3s

interval: 10s

unhealthy_threshold: 3

healthy_threshold: 2

http_health_check:

path: "/health"

circuit_breakers:

thresholds:

- priority: DEFAULT

max_connections: 1024

max_pending_requests: 1024

max_requests: 1024

max_retries: 3

load_assignment:

cluster_name: user_service

endpoints:

- lb_endpoints:

- endpoint:

address:

socket_address:

address: user-service

port_value: 8080

- name: order_service

connect_timeout: 5s

type: STRICT_DNS

lb_policy: LEAST_REQUEST

outlier_detection:

consecutive_5xx: 5

interval: 10s

base_ejection_time: 30s

max_ejection_percent: 50

load_assignment:

cluster_name: order_service

endpoints:

- lb_endpoints:

- endpoint:

address:

socket_address:

address: order-service

port_value: 8080

3.3 xDS API (동적 설정)

xDS API 종류:

┌──────────────────────────────────────┐

│ Control Plane │

│ (Istio Pilot / custom xDS server) │

└──────────┬───────────────────────────┘

┌────────┴──────────────────┐

│ │

▼ ▼

[Envoy A] [Envoy B]

xDS 유형:

├── LDS (Listener Discovery Service)

│ → 리스너 설정 동적 변경

├── RDS (Route Discovery Service)

│ → 라우팅 규칙 동적 변경

├── CDS (Cluster Discovery Service)

│ → 클러스터(업스트림) 동적 변경

├── EDS (Endpoint Discovery Service)

│ → 엔드포인트(IP:Port) 동적 변경

├── SDS (Secret Discovery Service)

│ → TLS 인증서 동적 변경

└── ECDS (Extension Config Discovery)

→ 필터 설정 동적 변경

3.4 Envoy WASM Filter

Envoy WASM Filter 설정 예시

http_filters:

- name: envoy.filters.http.wasm

typed_config:

"@type": type.googleapis.com/udpa.type.v1.TypedStruct

type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm

value:

config:

name: "custom_rate_limiter"

root_id: "rate_limiter"

vm_config:

runtime: "envoy.wasm.runtime.v8"

code:

local:

filename: "/etc/envoy/wasm/rate_limiter.wasm"

allow_precompiled: true

configuration:

"@type": type.googleapis.com/google.protobuf.StringValue

value: |

{

"max_requests_per_second": 100,

"burst_size": 20,

"response_code": 429

}

4. AWS API Gateway

4.1 AWS API Gateway 유형 비교

AWS API Gateway 3가지 유형:

┌──────────────┬───────────────┬───────────────┬───────────────┐

│ │ REST API │ HTTP API │ WebSocket │

├──────────────┼───────────────┼───────────────┼───────────────┤

│ 프로토콜 │ REST │ REST │ WebSocket │

│ 레이턴시 │ 보통 │ 낮음 (~35%) │ 보통 │

│ 가격 │ 높음 │ 낮음 (~70%) │ 메시지당 │

│ 캐싱 │ O │ X │ X │

│ Usage Plans │ O │ X │ X │

│ API Keys │ O │ X │ X │

│ WAF 통합 │ O │ X │ X │

│ Request 검증 │ O │ Parameter만 │ X │

│ Custom Domain│ O │ O │ O │

│ Lambda 통합 │ O │ O │ O │

│ VPC Link │ O │ O │ X │

│ 권장 사용 │ 풀기능 필요 │ 단순 프록시 │ 실시간 통신 │

└──────────────┴───────────────┴───────────────┴───────────────┘

4.2 AWS REST API Gateway + Lambda

SAM Template - REST API Gateway

AWSTemplateFormatVersion: '2010-09-09'

Transform: AWS::Serverless-2016-10-31

Globals:

Api:

Cors:

AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"

AllowHeaders: "'Content-Type,Authorization,X-Api-Key'"

AllowOrigin: "'https://app.company.com'"

Resources:

ApiGateway:

Type: AWS::Serverless::Api

Properties:

StageName: prod

Auth:

DefaultAuthorizer: CognitoAuth

Authorizers:

CognitoAuth:

UserPoolArn: !GetAtt UserPool.Arn

Identity:

Header: Authorization

ApiKeyRequired: true

UsagePlan:

CreateUsagePlan: PER_API

UsagePlanName: "StandardPlan"

Throttle:

BurstLimit: 100

RateLimit: 50

Quota:

Limit: 10000

Period: DAY

MethodSettings:

- HttpMethod: "*"

ResourcePath: "/*"

ThrottlingBurstLimit: 200

ThrottlingRateLimit: 100

CachingEnabled: true

CacheTtlInSeconds: 300

LoggingLevel: INFO

MetricsEnabled: true

User Function

UserFunction:

Type: AWS::Serverless::Function

Properties:

Handler: handlers/user.handler

Runtime: nodejs20.x

MemorySize: 256

Timeout: 10

Events:

GetUsers:

Type: Api

Properties:

RestApiId: !Ref ApiGateway

Path: /api/v1/users

Method: GET

CreateUser:

Type: Api

Properties:

RestApiId: !Ref ApiGateway

Path: /api/v1/users

Method: POST

Order Function

OrderFunction:

Type: AWS::Serverless::Function

Properties:

Handler: handlers/order.handler

Runtime: nodejs20.x

MemorySize: 512

Timeout: 15

Events:

GetOrders:

Type: Api

Properties:

RestApiId: !Ref ApiGateway

Path: /api/v1/orders

Method: GET

CreateOrder:

Type: Api

Properties:

RestApiId: !Ref ApiGateway

Path: /api/v1/orders

Method: POST

Cognito User Pool

UserPool:

Type: AWS::Cognito::UserPool

Properties:

UserPoolName: api-user-pool

AutoVerifiedAttributes:

- email

MfaConfiguration: "OPTIONAL"

Policies:

PasswordPolicy:

MinimumLength: 12

RequireUppercase: true

RequireLowercase: true

RequireNumbers: true

RequireSymbols: true

4.3 Lambda Authorizer (Custom Authorizer)

// authorizer.js - Lambda Custom Authorizer

const jwt = require('jsonwebtoken');

const JWKS_CACHE = {};

exports.handler = async (event) => {

try {

const token = extractToken(event.authorizationToken);

if (!token) {

throw new Error('Unauthorized');

}

// JWT 검증

const decoded = await verifyToken(token);

// IAM Policy 생성

const policy = generatePolicy(

decoded.sub,

'Allow',

event.methodArn,

{

userId: decoded.sub,

email: decoded.email,

role: decoded.role,

plan: decoded.plan || 'free'

}

);

return policy;

} catch (error) {

console.error('Authorization failed:', error.message);

throw new Error('Unauthorized');

}

};

function extractToken(authHeader) {

if (!authHeader) return null;

const parts = authHeader.split(' ');

if (parts.length !== 2 || parts[0] !== 'Bearer') return null;

return parts[1];

}

async function verifyToken(token) {

const decoded = jwt.decode(token, { complete: true });

if (!decoded) throw new Error('Invalid token');

// JWKS에서 공개키 가져오기

const publicKey = await getPublicKey(decoded.header.kid);

return jwt.verify(token, publicKey, {

algorithms: ['RS256'],

issuer: process.env.TOKEN_ISSUER,

audience: process.env.TOKEN_AUDIENCE,

});

}

function generatePolicy(principalId, effect, resource, context) {

const [arn, partition, service, region, accountId, apiId, stage] =

resource.split(/[:/]/);

return {

principalId,

policyDocument: {

Version: '2012-10-17',

Statement: [{

Action: 'execute-api:Invoke',

Effect: effect,

Resource: `arn:${partition}:${service}:${region}:${accountId}:${apiId}/${stage}/*`

}]

},

context: context || {}

};

}

5. Traefik

5.1 Traefik 아키텍처

Traefik Architecture:

[클라이언트]

┌────┴────────────────────────────────┐

│ Traefik Proxy │

│ │

│ EntryPoints (포트) │

│ ├── :80 (web) │

│ └── :443 (websecure) │

│ │ │

│ Routers (라우팅 규칙) │

│ ├── Host / Path / Header 매칭 │

│ ├── TLS 설정 │

│ └── Middleware 체인 │

│ │ │

│ Middlewares (중간 처리) │

│ ├── RateLimit │

│ ├── BasicAuth / ForwardAuth │

│ ├── Headers │

│ ├── Retry │

│ ├── CircuitBreaker │

│ └── StripPrefix │

│ │ │

│ Services (백엔드) │

│ ├── LoadBalancer │

│ ├── Weighted │

│ └── Mirroring │

│ │

│ Providers (설정 소스 - 자동 발견) │

│ ├── Docker │

│ ├── Kubernetes │

│ ├── File │

│ └── Consul / etcd │

└──────────────────────────────────────┘

5.2 Docker + Traefik 자동 발견

docker-compose.yml with Traefik

version: "3.8"

services:

traefik:

image: traefik:v3.0

command:

- "--api.dashboard=true"

- "--providers.docker=true"

- "--providers.docker.exposedByDefault=false"

- "--entrypoints.web.address=:80"

- "--entrypoints.websecure.address=:443"

- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"

- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"

- "--certificatesresolvers.letsencrypt.acme.email=admin@company.com"

- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"

- "--metrics.prometheus=true"

- "--accesslog=true"

- "--accesslog.format=json"

ports:

- "80:80"

- "443:443"

- "8080:8080"

volumes:

- /var/run/docker.sock:/var/run/docker.sock:ro

- letsencrypt:/letsencrypt

user-service:

image: user-service:latest

labels:

- "traefik.enable=true"

- "traefik.http.routers.users.rule=Host(`api.company.com`) && PathPrefix(`/api/v1/users`)"

- "traefik.http.routers.users.entrypoints=websecure"

- "traefik.http.routers.users.tls.certresolver=letsencrypt"

- "traefik.http.routers.users.middlewares=rate-limit,auth-forward"

- "traefik.http.services.users.loadbalancer.server.port=8080"

- "traefik.http.services.users.loadbalancer.healthcheck.path=/health"

- "traefik.http.services.users.loadbalancer.healthcheck.interval=10s"

order-service:

image: order-service:latest

labels:

- "traefik.enable=true"

- "traefik.http.routers.orders.rule=Host(`api.company.com`) && PathPrefix(`/api/v1/orders`)"

- "traefik.http.routers.orders.entrypoints=websecure"

- "traefik.http.routers.orders.tls.certresolver=letsencrypt"

- "traefik.http.routers.orders.middlewares=rate-limit,auth-forward"

- "traefik.http.services.orders.loadbalancer.server.port=8080"

Middleware 정의

Rate Limiting

rate-limit:

labels:

- "traefik.http.middlewares.rate-limit.ratelimit.average=100"

- "traefik.http.middlewares.rate-limit.ratelimit.burst=50"

- "traefik.http.middlewares.rate-limit.ratelimit.period=1m"

volumes:

letsencrypt:

5.3 Kubernetes IngressRoute (CRD)

Traefik IngressRoute CRD

apiVersion: traefik.io/v1alpha1

kind: IngressRoute

metadata:

name: api-routes

namespace: production

spec:

entryPoints:

- websecure

routes:

- match: Host(`api.company.com`) && PathPrefix(`/api/v1/users`)

kind: Rule

services:

- name: user-service

port: 8080

weight: 100

middlewares:

- name: rate-limit

- name: jwt-auth

- name: cors-headers

- match: Host(`api.company.com`) && PathPrefix(`/api/v1/products`)

kind: Rule

services:

- name: product-service-v1

port: 8080

weight: 90

- name: product-service-v2

port: 8080

weight: 10

tls:

certResolver: letsencrypt

apiVersion: traefik.io/v1alpha1

kind: Middleware

metadata:

name: rate-limit

namespace: production

spec:

rateLimit:

average: 100

burst: 50

period: 1m

sourceCriterion:

ipStrategy:

depth: 1

apiVersion: traefik.io/v1alpha1

kind: Middleware

metadata:

name: jwt-auth

namespace: production

spec:

forwardAuth:

address: http://auth-service:8080/verify

authResponseHeaders:

- X-User-ID

- X-User-Role

6. API Gateway 비교표 (15+ 항목)

| 항목 | Kong | Envoy | AWS API GW | Traefik |

|------|------|-------|------------|---------|

| **기반** | Nginx/OpenResty | C++ (자체) | AWS 관리형 | Go (자체) |

| **라이선스** | Apache 2.0 / Enterprise | Apache 2.0 | 종량제 | MIT |

| **배포 방식** | Self-hosted / Cloud | Self-hosted (사이드카) | Serverless | Self-hosted |

| **성능** | 높음 (Nginx) | 매우 높음 | 높음 (관리형) | 높음 |

| **설정 방식** | Admin API / Declarative | YAML / xDS API | Console / CloudFormation | Labels / YAML / CRD |

| **DB-less 모드** | O | N/A (항상 stateless) | N/A | N/A |

| **플러그인** | Lua / Go | C++ / WASM | Lambda Authorizer | 내장 Middleware |

| **서비스 디스커버리** | DNS / Consul | EDS (xDS) | CloudMap / VPC Link | Docker / K8s / Consul |

| **L7 기능** | 풍부 | 매우 풍부 | 기본 | 풍부 |

| **gRPC** | O | O | O (HTTP/2) | O |

| **WebSocket** | O | O | O (별도 API) | O |

| **mTLS** | O (Enterprise) | O | O (VPC Link) | O |

| **레이트 리밋** | 내장 플러그인 | 외부 서비스 / WASM | 내장 (Usage Plans) | 내장 Middleware |

| **분산 트레이싱** | Zipkin/OTel 플러그인 | 내장 (Zipkin/OTel) | X-Ray | Jaeger/Zipkin |

| **대시보드** | Kong Manager | 없음 (Kiali 등 사용) | AWS Console | 내장 대시보드 |

| **학습 곡선** | 중간 | 높음 | 낮음 | 낮음 |

| **적합 대상** | 범용 API GW | Service Mesh / 사이드카 | AWS 서버리스 | Docker/K8s 환경 |

7. 인증 (Authentication)

7.1 JWT 검증 구현

JWT 검증 미들웨어 (Python/FastAPI 예시)

from fastapi import Request, HTTPException

from jose import jwt, JWTError, ExpiredSignatureError

from functools import lru_cache

class JWTAuthMiddleware:

def __init__(self, jwks_url: str, issuer: str, audience: str):

self.jwks_url = jwks_url

self.issuer = issuer

self.audience = audience

self._jwks_cache = None

async def get_jwks(self):

"""JWKS (JSON Web Key Set) 가져오기 및 캐시"""

if self._jwks_cache is None:

async with httpx.AsyncClient() as client:

response = await client.get(self.jwks_url)

self._jwks_cache = response.json()

return self._jwks_cache

async def verify_token(self, request: Request) -> dict:

"""요청에서 JWT를 추출하고 검증"""

1. Authorization 헤더에서 토큰 추출

auth_header = request.headers.get("Authorization")

if not auth_header or not auth_header.startswith("Bearer "):

raise HTTPException(

status_code=401,

detail="Missing or invalid Authorization header"

)

token = auth_header.split(" ")[1]

try:

2. 토큰 헤더에서 kid 추출

unverified_header = jwt.get_unverified_header(token)

kid = unverified_header.get("kid")

3. JWKS에서 해당 키 찾기

jwks = await self.get_jwks()

key = None

for jwk in jwks.get("keys", []):

if jwk["kid"] == kid:

key = jwk

break

if key is None:

캐시 갱신 후 재시도

self._jwks_cache = None

jwks = await self.get_jwks()

for jwk in jwks.get("keys", []):

if jwk["kid"] == kid:

key = jwk

break

if key is None:

raise HTTPException(status_code=401, detail="Unknown signing key")

4. JWT 검증

payload = jwt.decode(

token,

key,

algorithms=["RS256"],

audience=self.audience,

issuer=self.issuer,

)

return payload

except ExpiredSignatureError:

raise HTTPException(status_code=401, detail="Token expired")

except JWTError as e:

raise HTTPException(status_code=401, detail=f"Invalid token: {str(e)}")

7.2 OAuth2 흐름

OAuth2 Authorization Code Flow + PKCE:

[브라우저/앱] [API Gateway] [Auth Server] [Backend]

│ │ │ │

│ 1. 로그인 요청 │ │ │

│─────────────────────────────▶│ │ │

│ │ │ │

│ 2. Auth Server로 리다이렉트 │ │ │

│◀─────────────────────────────│ │ │

│ │ │ │

│ 3. 인증 (ID/PW 또는 SSO) │ │ │

│──────────────────────────────────────────────────▶│ │

│ │ │ │

│ 4. Authorization Code 반환 │ │ │

│◀──────────────────────────────────────────────────│ │

│ │ │ │

│ 5. Code + PKCE verifier │ │ │

│─────────────────────────────▶│ │ │

│ │ 6. Code -> Token │ │

│ │───────────────────▶│ │

│ │ 7. Access Token │ │

│ │◀───────────────────│ │

│ │ │ │

│ 8. Access Token │ │ │

│◀─────────────────────────────│ │ │

│ │ │ │

│ 9. API 요청 + Bearer Token │ │ │

│─────────────────────────────▶│ │ │

│ │ 10. Token 검증 │ │

│ │───────────────────▶│ │

│ │ 11. 검증 결과 │ │

│ │◀───────────────────│ │

│ │ 12. 요청 전달 │ │

│ │───────────────────────────────────▶│

│ │ 13. 응답 │ │

│ │◀───────────────────────────────────│

│ 14. API 응답 │ │ │

│◀─────────────────────────────│ │ │

7.3 API Key 관리

API Key 관리 시스템

from datetime import datetime, timedelta

class APIKeyManager:

def __init__(self, db):

self.db = db

def generate_key(

self,

consumer_id: str,

plan: str = "free",

expires_in_days: int = 365

) -> dict:

"""새 API Key 생성"""

prefix + secret 형식 (sk_live_xxxx)

prefix = f"sk_{'live' if plan != 'free' else 'test'}"

raw_key = f"{prefix}_{secrets.token_urlsafe(32)}"

DB에는 해시만 저장

key_hash = hashlib.sha256(raw_key.encode()).hexdigest()

record = {

"key_hash": key_hash,

"key_prefix": raw_key[:12], # 식별용 prefix만 저장

"consumer_id": consumer_id,

"plan": plan,

"rate_limit": self._get_rate_limit(plan),

"created_at": datetime.utcnow().isoformat(),

"expires_at": (

datetime.utcnow() + timedelta(days=expires_in_days)

).isoformat(),

"is_active": True,

}

self.db.insert("api_keys", record)

return {

"api_key": raw_key, # 한 번만 반환, 이후 조회 불가

"prefix": raw_key[:12],

"plan": plan,

"expires_at": record["expires_at"],

}

def validate_key(self, raw_key: str) -> dict:

"""API Key 검증"""

key_hash = hashlib.sha256(raw_key.encode()).hexdigest()

record = self.db.find_one("api_keys", {"key_hash": key_hash})

if not record:

return {"valid": False, "error": "Invalid API key"}

if not record["is_active"]:

return {"valid": False, "error": "API key is deactivated"}

if datetime.fromisoformat(record["expires_at"]) < datetime.utcnow():

return {"valid": False, "error": "API key expired"}

return {

"valid": True,

"consumer_id": record["consumer_id"],

"plan": record["plan"],

"rate_limit": record["rate_limit"],

}

def _get_rate_limit(self, plan: str) -> dict:

limits = {

"free": {"rpm": 60, "rpd": 1000},

"starter": {"rpm": 300, "rpd": 10000},

"pro": {"rpm": 1000, "rpd": 100000},

"enterprise": {"rpm": 10000, "rpd": 1000000},

}

return limits.get(plan, limits["free"])

8. 레이트 리밋 알고리즘

8.1 Token Bucket

class TokenBucket:

"""Token Bucket 레이트 리미터

특징:

- 일정 속도로 토큰이 버킷에 추가됨

- 요청 시 토큰 1개 소비

- 토큰 없으면 요청 거부

- 버스트 허용 (버킷이 가득 찰 때까지)

"""

def __init__(self, rate: float, capacity: int):

self.rate = rate # 초당 토큰 생성 속도

self.capacity = capacity # 버킷 최대 크기 (버스트 허용량)

self.tokens = capacity # 현재 토큰 수

self.last_refill = time.monotonic()

self.lock = threading.Lock()

def allow_request(self) -> bool:

with self.lock:

now = time.monotonic()

elapsed = now - self.last_refill

토큰 보충

self.tokens = min(

self.capacity,

self.tokens + elapsed * self.rate

)

self.last_refill = now

if self.tokens >= 1:

self.tokens -= 1

return True

return False

def get_retry_after(self) -> float:

"""다음 토큰까지 남은 시간 (초)"""

if self.tokens >= 1:

return 0

return (1 - self.tokens) / self.rate

사용 예시

limiter = TokenBucket(rate=10, capacity=20)

rate=10: 초당 10개 토큰 생성

capacity=20: 최대 20개 토큰 저장 (버스트 20)

for i in range(25):

if limiter.allow_request():

print(f"Request {i}: ALLOWED")

else:

retry = limiter.get_retry_after()

print(f"Request {i}: DENIED (retry after {retry:.2f}s)")

8.2 Sliding Window Log

from collections import deque

class SlidingWindowLog:

"""Sliding Window Log 레이트 리미터

특징:

- 정확한 윈도우 기반 제한

- 메모리 사용량이 요청 수에 비례

- 경계 문제 없음 (Fixed Window 대비 장점)

"""

def __init__(self, max_requests: int, window_seconds: int):

self.max_requests = max_requests

self.window_seconds = window_seconds

self.requests = deque() # 타임스탬프 로그

self.lock = threading.Lock()

def allow_request(self) -> bool:

with self.lock:

now = time.monotonic()

window_start = now - self.window_seconds

윈도우 밖의 오래된 요청 제거

while self.requests and self.requests[0] < window_start:

self.requests.popleft()

if len(self.requests) < self.max_requests:

self.requests.append(now)

return True

return False

def get_remaining(self) -> int:

with self.lock:

now = time.monotonic()

window_start = now - self.window_seconds

while self.requests and self.requests[0] < window_start:

self.requests.popleft()

return max(0, self.max_requests - len(self.requests))

사용 예시

limiter = SlidingWindowLog(max_requests=100, window_seconds=60)

60초 동안 최대 100개 요청

8.3 Fixed Window Counter

from collections import defaultdict

class FixedWindowCounter:

"""Fixed Window Counter 레이트 리미터

특징:

- 메모리 효율적 (카운터만 저장)

- 윈도우 경계에서 2배 버스트 가능 (단점)

- 구현이 단순함

"""

def __init__(self, max_requests: int, window_seconds: int):

self.max_requests = max_requests

self.window_seconds = window_seconds

self.counters = defaultdict(int)

self.lock = threading.Lock()

def _get_window_key(self) -> int:

return int(time.time() // self.window_seconds)

def allow_request(self, client_id: str = "global") -> bool:

with self.lock:

window = self._get_window_key()

key = f"{client_id}:{window}"

이전 윈도우 정리

old_keys = [

k for k in self.counters

if not k.endswith(f":{window}")

]

for k in old_keys:

del self.counters[k]

if self.counters[key] < self.max_requests:

self.counters[key] += 1

return True

return False

8.4 분산 환경 레이트 리밋 (Redis)

class DistributedRateLimiter:

"""Redis 기반 분산 레이트 리미터 (Sliding Window Counter)"""

def __init__(self, redis_client: redis.Redis, prefix: str = "rl"):

self.redis = redis_client

self.prefix = prefix

def is_allowed(

self,

key: str,

max_requests: int,

window_seconds: int

) -> dict:

"""

Sliding Window Counter (Redis Sorted Set 사용)

"""

now = time.time()

window_start = now - window_seconds

redis_key = f"{self.prefix}:{key}"

pipe = self.redis.pipeline()

1. 윈도우 밖의 오래된 항목 제거

pipe.zremrangebyscore(redis_key, 0, window_start)

2. 현재 윈도우의 요청 수 조회

pipe.zcard(redis_key)

3. 현재 요청 추가

pipe.zadd(redis_key, {f"{now}:{id(object())}": now})

4. TTL 설정 (윈도우 크기 + 여유)

pipe.expire(redis_key, window_seconds + 1)

results = pipe.execute()

current_count = results[1]

if current_count < max_requests:

remaining = max_requests - current_count - 1

return {

"allowed": True,

"remaining": max(0, remaining),

"reset_at": int(now + window_seconds),

"limit": max_requests,

}

else:

방금 추가한 요청 제거

self.redis.zrem(redis_key, f"{now}:{id(object())}")

return {

"allowed": False,

"remaining": 0,

"reset_at": int(now + window_seconds),

"limit": max_requests,

"retry_after": window_seconds,

}

9. 요청/응답 변환 및 캐싱

9.1 Request/Response Transformation

Kong Request Transformer 예시

plugins:

- name: request-transformer

config:

add:

headers:

- "X-Gateway-Version:2.0"

- "X-Request-Start:$(now)"

querystring:

- "format:json"

rename:

headers:

- "X-Old-Header:X-New-Header"

remove:

headers:

- "X-Internal-Debug"

replace:

headers:

- "Host:internal-api.company.com"

- name: response-transformer

config:

add:

headers:

- "X-Response-Time:$(latency)"

- "X-RateLimit-Remaining:$(rate_limit_remaining)"

remove:

headers:

- "Server"

- "X-Powered-By"

- "X-Backend-Server"

replace:

headers:

- "Content-Security-Policy:default-src 'self'"

9.2 캐싱 전략

API Gateway 캐싱 전략:

1. Response Cache (응답 캐싱)

┌──────────┐ ┌──────────┐ ┌──────────┐

│ 클라이언트│────▶│ Gateway │────▶│ Backend │

│ │ │ Cache │ │ │

│ │◀────│ (Hit!) │ │ │

└──────────┘ └──────────┘ └──────────┘

Cache Key = Method + Path + Query + Selected Headers

TTL: GET /products → 5분, GET /products/123 → 1분

2. 캐시 무효화 전략:

- TTL 기반: 시간이 지나면 자동 만료

- Event 기반: 데이터 변경 시 캐시 퍼지

- Stale-While-Revalidate: 만료 후에도 캐시 반환, 백그라운드 갱신

3. Cache-Control 헤더:

Cache-Control: public, max-age=300, s-maxage=600

ETag: "v1-product-123-hash"

Vary: Accept, Authorization

Kong Proxy Cache 설정

plugins:

- name: proxy-cache

config:

strategy: memory

memory:

dictionary_name: cache_dict

response_code:

- 200

- 301

request_method:

- GET

- HEAD

content_type:

- "application/json"

- "text/html"

cache_ttl: 300

vary_headers:

- Accept

- Accept-Encoding

vary_query_params:

- page

- limit

- sort

cache_control: true

storage_ttl: 600

10. 서킷 브레이커 및 카나리 배포

10.1 서킷 브레이커 패턴

Circuit Breaker 상태:

┌──────────┐ 실패율 초과 ┌──────────┐

│ CLOSED │──────────────────▶│ OPEN │

│ (정상) │ │ (차단) │

└──────────┘ └────┬─────┘

▲ │

│ 타임아웃 후 │

│ 성공 ┌─────────┴──┐

└────────────────────│ HALF-OPEN │

│ (테스트) │

└────────────┘

실패 시 → 다시 OPEN

Envoy Circuit Breaker 설정

clusters:

- name: order_service

circuit_breakers:

thresholds:

- priority: DEFAULT

max_connections: 1024 # 최대 동시 연결

max_pending_requests: 512 # 대기 중 최대 요청

max_requests: 2048 # 최대 동시 요청

max_retries: 3 # 최대 재시도

track_remaining: true

outlier_detection:

consecutive_5xx: 5 # 연속 5xx 5회 시 제거

interval: 10s # 분석 주기

base_ejection_time: 30s # 기본 제거 시간

max_ejection_percent: 50 # 최대 제거 비율

success_rate_minimum_hosts: 3 # 통계에 필요한 최소 호스트

success_rate_request_volume: 100 # 통계에 필요한 최소 요청

success_rate_stdev_factor: 1900 # 표준편차 기반 제거

10.2 카나리/A-B 라우팅

Envoy Weighted Routing (카나리 배포)

route_config:

virtual_hosts:

- name: api

domains: ["api.company.com"]

routes:

- match:

prefix: "/api/v1/products"

route:

weighted_clusters:

clusters:

- name: product_v1

weight: 90

- name: product_v2

weight: 10

retry_policy:

retry_on: "5xx"

num_retries: 2

Header 기반 A/B 라우팅

- match:

prefix: "/api/v1/checkout"

headers:

- name: "X-Feature-Flag"

exact_match: "new-checkout"

route:

cluster: checkout_v2

- match:

prefix: "/api/v1/checkout"

route:

cluster: checkout_v1

Kong Canary Release 플러그인

plugins:

- name: canary

config:

start: 0 # 시작 비율 (%)

target: 100 # 목표 비율 (%)

steps: 10 # 단계 수

duration: 3600 # 전환 시간 (초)

upstream_host: "product-v2:8080"

upstream_port: 8080

hash: "consumer" # consumer/ip/header 기반 일관된 라우팅

11. GraphQL Gateway

11.1 Apollo Router / Federation

GraphQL Federation Architecture:

[클라이언트]

┌────┴────────────────────────┐

│ Apollo Router │

│ (Supergraph Gateway) │

│ │

│ ┌───────────────────────┐ │

│ │ Query Planner │ │

│ │ → 어떤 서브그래프에 │ │

│ │ 어떤 쿼리를 보낼지 │ │

│ │ 최적 계획 수립 │ │

│ └───────────────────────┘ │

└──────┬──────┬──────┬────────┘

│ │ │

┌────┴──┐ ┌┴────┐ ┌┴────────┐

│ User │ │Order│ │ Product │

│Subgraph│ │Sub- │ │Subgraph │

│ │ │graph│ │ │

└───────┘ └─────┘ └──────────┘

User Subgraph Schema

type User @key(fields: "id") {

id: ID!

name: String!

email: String!

orders: [Order]

}

type Query {

user(id: ID!): User

users(limit: Int, offset: Int): [User]

}

Apollo Router 설정

router.yaml

supergraph:

listen: 0.0.0.0:4000

headers:

all:

request:

- propagate:

named: "Authorization"

- propagate:

named: "X-Request-ID"

traffic_shaping:

all:

timeout: 30s

subgraphs:

users:

timeout: 10s

orders:

timeout: 15s

limits:

max_depth: 15

max_height: 200

max_aliases: 30

max_root_fields: 20

telemetry:

exporters:

metrics:

prometheus:

enabled: true

listen: 0.0.0.0:9090

path: /metrics

tracing:

otlp:

enabled: true

endpoint: http://otel-collector:4317

cors:

origins:

- https://app.company.com

methods:

- GET

- POST

allow_headers:

- Authorization

- Content-Type

ratelimit:

global:

capacity: 1000

interval: 1m

12. API 버저닝 전략

12.1 버저닝 방식 비교

3가지 API 버저닝 전략:

1. URL Path Versioning

GET /api/v1/users

GET /api/v2/users

→ 가장 직관적, 널리 사용

→ 캐싱 친화적

→ URL이 바뀜 (Breaking)

2. Header Versioning

GET /api/users

Accept: application/vnd.company.v2+json

→ URL 깔끔

→ 브라우저에서 테스트 어려움

3. Query Parameter Versioning

GET /api/users?version=2

→ 간단

→ 캐싱 복잡

→ 쿼리 파라미터 오염

12.2 Gateway 레벨 버저닝 구현

Kong Route 기반 버저닝

services:

- name: user-service-v1

url: http://user-service-v1:8080

routes:

- name: users-v1

paths:

- /api/v1/users

strip_path: false

- name: user-service-v2

url: http://user-service-v2:8080

routes:

- name: users-v2

paths:

- /api/v2/users

strip_path: false

Header 기반 버저닝

- name: user-service-v2-header

url: http://user-service-v2:8080

routes:

- name: users-v2-header

paths:

- /api/users

headers:

X-API-Version:

- "2"

strip_path: false

디폴트 (최신 안정 버전)

- name: user-service-stable

url: http://user-service-v1:8080

routes:

- name: users-default

paths:

- /api/users

strip_path: false

13. 모니터링 및 옵저버빌리티

13.1 Prometheus 메트릭

API Gateway 핵심 메트릭

1. 4 Golden Signals

golden_signals:

latency:

- histogram: api_request_duration_seconds

labels: [method, route, status_code]

buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]

traffic:

- counter: api_requests_total

labels: [method, route, status_code, consumer]

errors:

- counter: api_errors_total

labels: [method, route, error_type]

saturation:

- gauge: api_active_connections

- gauge: api_rate_limit_remaining

- gauge: api_circuit_breaker_state

2. 레이트 리밋 메트릭

rate_limiting:

- counter: api_rate_limit_hits_total

labels: [consumer, plan, route]

- gauge: api_rate_limit_remaining

labels: [consumer, route]

3. 캐시 메트릭

caching:

- counter: api_cache_hits_total

- counter: api_cache_misses_total

- gauge: api_cache_size_bytes

4. 업스트림 메트릭

upstream:

- histogram: api_upstream_latency_seconds

labels: [service, status]

- counter: api_upstream_errors_total

labels: [service, error_type]

- gauge: api_upstream_healthy_count

labels: [service]

Grafana 대시보드 쿼리 예시

panels:

- title: "Request Rate (RPS)"

query: |

sum(rate(api_requests_total[5m])) by (route)

- title: "P99 Latency"

query: |

histogram_quantile(0.99,

sum(rate(api_request_duration_seconds_bucket[5m])) by (le, route)

)

- title: "Error Rate"

query: |

sum(rate(api_requests_total{status_code=~"5.."}[5m]))

/

sum(rate(api_requests_total[5m]))

* 100

- title: "Rate Limit Hits"

query: |

sum(rate(api_rate_limit_hits_total[5m])) by (consumer)

- title: "Circuit Breaker Status"

query: |

api_circuit_breaker_state

13.2 분산 트레이싱

API Gateway 분산 트레이싱 흐름:

Request ID: abc-123-def

[Client] ─────────── [API Gateway] ──────── [User Service] ──── [DB]

│ │ │ │

│ Span: client-req │ Span: gateway │ Span: user-svc│ Span: db-query

│ trace_id: abc123 │ trace_id: abc123 │ trace_id: abc123

│ span_id: span-1 │ span_id: span-2 │ span_id: span-3

│ parent: none │ parent: span-1 │ parent: span-2

│ duration: 250ms │ duration: 200ms │ duration: 150ms

│ │ │

│ Tags: │ Tags: │ Tags:

│ http.method: GET │ gateway.auth: jwt │ db.type: postgres

│ http.url: /users │ gateway.cache: miss │ db.statement: SELECT

│ http.status: 200 │ consumer: user-123 │ db.duration: 50ms

14. 퀴즈

**답:**

1. **Routing Pattern (라우팅):** 클라이언트 요청을 URL 경로, 헤더 등을 기반으로 올바른 백엔드 서비스로 전달합니다.

2. **Aggregation Pattern (집약):** 여러 백엔드 서비스의 응답을 하나로 합쳐서 클라이언트에게 단일 응답으로 반환합니다. BFF(Backend for Frontend) 패턴과 관련됩니다.

3. **Offloading Pattern (오프로딩):** 인증, SSL 종료, 캐싱, 레이트 리밋 등 공통 관심사를 개별 서비스에서 Gateway로 이전하여 서비스가 비즈니스 로직에 집중할 수 있게 합니다.

**답:**

- **Kong:** 범용 API Gateway가 필요할 때. 풍부한 플러그인 생태계, DB-less 모드 지원. 인증/레이트 리밋/변환 등이 플러그인으로 즉시 사용 가능합니다.

- **Envoy:** Service Mesh의 데이터 플레인이나 사이드카 프록시로 사용할 때. xDS API를 통한 동적 설정, WASM 필터로 커스텀 확장. Istio와 통합 시 최적입니다.

- **AWS API Gateway:** AWS Lambda 기반 서버리스 아키텍처에서. 인프라 관리 불필요, Usage Plans과 API Key 관리가 내장되어 있습니다.

- **Traefik:** Docker/Kubernetes 환경에서 자동 서비스 발견이 필요할 때. 라벨/어노테이션만으로 라우팅 설정이 가능하고, Let's Encrypt 자동 인증서 관리를 지원합니다.

**답:**

- **Token Bucket:** 일정 속도로 토큰이 충전되고, 요청마다 토큰을 소비합니다. 버킷이 가득 차면 일시적인 버스트가 허용됩니다. 메모리 효율적이고 구현이 간단합니다.

- **Sliding Window Log:** 각 요청의 타임스탬프를 기록하고, 현재 시점에서 윈도우 크기만큼 이전까지의 요청을 카운트합니다. Fixed Window의 경계 문제(윈도우 시작/끝에서 2배 버스트)가 없어 정확하지만, 메모리 사용량이 요청 수에 비례합니다.

실무에서는 Redis Sorted Set을 사용한 Sliding Window Counter가 정확성과 메모리 효율의 균형이 좋아 널리 사용됩니다.

**답:**

GraphQL Federation은 여러 서비스가 각자의 GraphQL 스키마(서브그래프)를 정의하고, 이를 하나의 통합 스키마(슈퍼그래프)로 조합하는 아키텍처입니다.

**장점:**

- 서비스 자율성: 각 팀이 자신의 도메인 스키마를 독립적으로 관리

- 단일 엔드포인트: 클라이언트는 하나의 GraphQL 엔드포인트만 사용

- 타입 확장: 서비스 간 타입을 확장하여 연결 (User 타입에 Orders 필드 추가 등)

**Apollo Router:**

- 클라이언트 쿼리를 분석하여 어떤 서브그래프에 어떤 쿼리를 보낼지 Query Plan을 수립합니다.

- 각 서브그래프의 응답을 자동으로 합쳐 클라이언트에게 반환합니다.

- 레이트 리밋, 인증, 트레이싱, 캐싱 등 게이트웨이 기능을 제공합니다.

**답:** Google SRE에서 제안한 4가지 Golden Signal입니다:

1. **Latency (지연 시간):** 요청 처리에 걸리는 시간. P50, P95, P99 백분위 추적이 중요합니다.

2. **Traffic (트래픽):** 초당 요청 수(RPS). 엔드포인트별, 컨슈머별 트래픽 패턴을 파악합니다.

3. **Errors (에러율):** 전체 요청 대비 에러 비율. 5xx 서버 에러와 4xx 클라이언트 에러를 구분합니다.

4. **Saturation (포화도):** 시스템 자원 사용률. 동시 연결 수, 레이트 리밋 잔여량, 서킷 브레이커 상태 등입니다.

15. 참고 자료

1. [Kong Documentation](https://docs.konghq.com/)

2. [Envoy Proxy Documentation](https://www.envoyproxy.io/docs/envoy/latest/)

3. [AWS API Gateway Developer Guide](https://docs.aws.amazon.com/apigateway/latest/developerguide/)

4. [Traefik Documentation](https://doc.traefik.io/traefik/)

5. [Apollo Router Documentation](https://www.apollographql.com/docs/router/)

6. [GraphQL Federation Specification](https://www.apollographql.com/docs/federation/)

7. [NGINX Rate Limiting](https://www.nginx.com/blog/rate-limiting-nginx/)

8. [Token Bucket Algorithm (Wikipedia)](https://en.wikipedia.org/wiki/Token_bucket)

9. [Envoy xDS Protocol](https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol)

10. [Google SRE Book - Monitoring Distributed Systems](https://sre.google/sre-book/monitoring-distributed-systems/)

11. [OpenTelemetry Documentation](https://opentelemetry.io/docs/)

12. [Istio Service Mesh](https://istio.io/latest/docs/)

13. [RFC 6585 - Additional HTTP Status Codes (429)](https://datatracker.ietf.org/doc/html/rfc6585)

14. [Microsoft API Design Best Practices](https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design)

현재 단락 (1/1467)

마이크로서비스 아키텍처에서는 수십~수백 개의 서비스가 독립적으로 운영됩니다. 각 서비스마다 인증, 로깅, 레이트 리밋 등을 개별 구현하면 중복과 불일치가 발생합니다.

작성 글자: 0원문 글자: 34,079작성 단락: 0/1467