5강 50분

API Gateway와 서비스 간 통신

API Gateway 패턴과 서비스 간 동기/비동기 통신 방법을 학습합니다. 실전에서 사용하는 통신 패턴과 Best Practice를 다룹니다.

API Gateway와 서비스 간 통신

학습 목표

  • API Gateway의 역할과 필요성
  • 동기 vs 비동기 통신 선택 기준
  • Service Mesh와 사이드카 패턴
  • 실전 통신 패턴 구현

API Gateway란?

클라이언트와 백엔드 마이크로서비스 사이의 **단일 진입점(Single Entry Point)**입니다.

문제 상황 (Gateway 없이)

┌─────────┐
│ Mobile  │──┐
│  App    │  │
└─────────┘  │    ┌──────────┐
             ├───→│ Auth     │
┌─────────┐  │    │ Service  │
│   Web   │──┤    └──────────┘
│  App    │  │
└─────────┘  │    ┌──────────┐
             ├───→│ Product  │
             │    │ Service  │
             │    └──────────┘

             │    ┌──────────┐
             └───→│ Order    │
                  │ Service  │
                  └──────────┘

문제점:

  • 클라이언트가 여러 서비스 호출 필요
  • 각 서비스마다 인증 처리
  • 서비스 주소 변경 시 클라이언트도 변경
  • CORS 정책 관리 어려움

해결책: API Gateway

graph TD
    Mobile["Mobile App"]
    Web["Web App"]
    Gateway["API Gateway<br/>라우팅, 인증, Rate Limiting"]
    Auth["Auth Service"]
    Product["Product Service"]
    Order["Order Service"]

    Mobile --> Gateway
    Web --> Gateway
    Gateway --> Auth
    Gateway --> Product
    Gateway --> Order

    style Mobile fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
    style Web fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    style Gateway fill:#fff3e0,stroke:#ef6c00,stroke-width:3px
    style Auth fill:#fce4ec,stroke:#c2185b,stroke-width:2px
    style Product fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style Order fill:#e0f2f1,stroke:#00695c,stroke-width:2px

API Gateway의 주요 기능

1. 라우팅

// Kong Gateway 설정 예제
{
  "routes": [
    {
      "path": "/api/products",
      "service": "product-service"
    },
    {
      "path": "/api/orders",
      "service": "order-service"
    }
  ]
}

2. 인증/인가

// Gateway에서 JWT 검증
async function authenticate(request) {
  const token = request.headers.authorization?.split(' ')[1];

  if (!token) {
    throw new UnauthorizedError();
  }

  const decoded = jwt.verify(token, JWT_SECRET);

  // 사용자 정보를 헤더에 추가
  request.headers['X-User-Id'] = decoded.userId;
  request.headers['X-User-Role'] = decoded.role;

  return next();
}

3. Rate Limiting

// 분당 100 요청 제한
{
  "plugins": [{
    "name": "rate-limiting",
    "config": {
      "minute": 100,
      "policy": "local"
    }
  }]
}

4. 응답 집계 (Response Aggregation)

// 여러 서비스 호출 결과 합치기
async function getProductDetails(productId) {
  const [product, reviews, inventory] = await Promise.all([
    productService.getProduct(productId),
    reviewService.getReviews(productId),
    inventoryService.getStock(productId),
  ]);

  return {
    ...product,
    reviews,
    stock: inventory.quantity,
  };
}

동기 vs 비동기 통신

동기 통신 (REST/gRPC)

사용 시기:

  • 즉시 응답 필요
  • 간단한 CRUD 작업
  • 클라이언트가 결과를 대기해야 함

예제: REST API

// Order Service가 Product Service 호출
async function createOrder(customerId, productId, quantity) {
  // 1. 상품 정보 조회 (동기)
  const product = await fetch(
    `http://product-service/api/products/${productId}`
  ).then(res => res.json());

  if (product.stock < quantity) {
    throw new InsufficientStockError();
  }

  // 2. 주문 생성
  const order = await Order.create({
    customerId,
    productId,
    quantity,
    totalAmount: product.price * quantity,
  });

  return order;
}

장점:

  • 구현 간단
  • 즉시 응답
  • 디버깅 쉬움

단점:

  • 서비스 간 강한 결합
  • 장애 전파
  • 성능 병목

비동기 통신 (메시지 큐)

사용 시기:

  • 즉시 응답 불필요
  • 장시간 작업
  • 서비스 간 느슨한 결합 필요
  • 이벤트 기반 아키텍처

예제: 이벤트 기반

// 주문 생성 시 이벤트 발행
async function createOrder(orderData) {
  const order = await Order.create(orderData);

  // 이벤트 발행 (비동기)
  await eventBus.publish('order.created', {
    orderId: order.id,
    customerId: order.customerId,
    items: order.items,
    totalAmount: order.totalAmount,
  });

  return order;
}

// Payment Service가 이벤트 구독
eventBus.subscribe('order.created', async (event) => {
  await processPayment(event.orderId, event.totalAmount);

  // 결제 완료 이벤트 발행
  await eventBus.publish('payment.completed', {
    orderId: event.orderId,
  });
});

// Inventory Service가 이벤트 구독
eventBus.subscribe('order.created', async (event) => {
  await decreaseStock(event.items);
});

// Notification Service가 이벤트 구독
eventBus.subscribe('order.created', async (event) => {
  await sendEmailToCustomer(event.customerId, event.orderId);
});

장점:

  • 서비스 간 느슨한 결합
  • 확장성 우수
  • 장애 격리

단점:

  • 복잡도 증가
  • 디버깅 어려움
  • 최종 일관성

통신 패턴 Best Practice

1. Saga 패턴 (분산 트랜잭션)

주문 → 결제 → 재고 차감이 하나의 트랜잭션처럼 동작

// Choreography 방식 (이벤트 기반)
class OrderSaga {
  async execute(orderData) {
    // 1. 주문 생성
    const order = await createOrder(orderData);
    emit('order.created', order);

    // 2. 결제 (Payment Service가 처리)
    // 3. 재고 차감 (Inventory Service가 처리)
    // 4. 배송 준비 (Delivery Service가 처리)
  }

  // 보상 트랜잭션 (실패 시)
  async compensate(orderId) {
    emit('order.cancelled', { orderId });
    // 각 서비스가 보상 트랜잭션 실행
  }
}

2. Circuit Breaker 패턴

서비스 장애 시 빠른 실패

class CircuitBreaker {
  private failureCount = 0;
  private state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN

  async call(serviceCall) {
    if (this.state === 'OPEN') {
      throw new ServiceUnavailableError();
    }

    try {
      const result = await serviceCall();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onFailure() {
    this.failureCount++;
    if (this.failureCount >= THRESHOLD) {
      this.state = 'OPEN';
      setTimeout(() => this.state = 'HALF_OPEN', TIMEOUT);
    }
  }

  private onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
  }
}

3. Retry with Exponential Backoff

async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;

      const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      await sleep(delay);
    }
  }
}

// 사용
const product = await retryWithBackoff(() =>
  productService.getProduct(productId)
);

Service Mesh

서비스 간 통신을 관리하는 인프라 계층

┌─────────┐         ┌─────────┐
│Service A│◄───────►│Service B│
│ ┌─────┐ │         │ ┌─────┐ │
│ │Proxy│ │         │ │Proxy│ │
│ └──┬──┘ │         │ └──┬──┘ │
└────┼────┘         └────┼────┘
     │                   │
     └──────┬────────────┘

      ┌─────▼─────┐
      │  Control  │
      │   Plane   │
      │  (Istio)  │
      └───────────┘

기능:

  • 자동 로드 밸런싱
  • 서비스 디스커버리
  • 트래픽 관리
  • 보안 (mTLS)
  • 모니터링

다음 강의

다음 강의에서는 배포 전략과 CI/CD 파이프라인을 배웁니다.

핵심 정리

  • API Gateway는 마이크로서비스의 단일 진입점
  • 동기 통신은 간단하지만 결합도 높음
  • 비동기 통신은 복잡하지만 확장성 좋음
  • Saga, Circuit Breaker 등 패턴으로 안정성 확보
  • Service Mesh로 통신 관리 자동화