[TIL] API 게이트웨이 (Spring Cloud Gateway)

김건우·2024년 8월 2일

[TIL]

목록 보기
5/25

API 게이트웨이

  • API 게이트웨이는 클라이언트의 요청을 받아 백엔드 서비스로 라우팅하고, 다양한 부가 기능을 제공하는 중간 서버입니다.
  • 클라이언트와 서비스 간의 단일 진입점 역할을 하며, 보안, 로깅, 모니터링, 요청 필터링 등을 처리합니다.

API 게이트웨이의 주요 기능

  • 라우팅: 클라이언트 요청을 적절한 서비스로 전달
  • 인증 및 권한 부여: 요청의 인증 및 권한을 검증
  • 로드 밸런싱: 여러 서비스 인스턴스 간의 부하 분산
  • 모니터링 및 로깅: 요청 및 응답을 로깅하고 모니터링
  • 요청 및 응답 변환: 요청과 응답을 변환하거나 필터링

Spring Cloud Gateway

  • Spring Cloud Gateway는 Spring 프로젝트의 일환으로 개발된 API 게이트웨이로, 클라이언트 요청을 적절한 서비스로 라우팅하고 다양한 필터링 기능을 제공합니다.
  • Spring Cloud Netflix 패키지의 일부로, 마이크로서비스 아키텍처에서 널리 사용됩니다.

Spring Cloud Gateway의 주요 특징

  • 동적 라우팅: 요청의 URL 패턴에 따라 동적으로 라우팅
  • 필터링: 요청 전후에 다양한 작업을 수행할 수 있는 필터 체인 제공
  • 모니터링: 요청 로그 및 메트릭을 통해 서비스 상태 모니터링
  • 보안: 요청의 인증 및 권한 검증

Spring Cloud Gateway 필터링

필터 종류

  • Global Filter: 모든 요청에 대해 작동하는 필터
  • Gateway Filter: 특정 라우트에만 적용되는 필터

필터 시점별 종류

  • Pre 필터
    • Pre 필터는 요청이 처리되기 전에 실행됩니다. 따라서 Pre 필터에서는 요청을 가로채고 필요한 작업을 수행한 다음, 체인의 다음 필터로 요청을 전달합니다. 이때, 추가적인 비동기 작업을 수행할 필요가 없기 때문에 then 메서드를 사용할 필요가 없습니다.
  • Post 필터
    • Post 필터는 요청이 처리된 후, 응답이 반환되기 전에 실행됩니다. Post 필터에서는 체인의 다음 필터가 완료된 후에 실행되어야 하는 추가적인 작업을 수행해야 합니다. 따라서 chain.filter(exchange)를 호출하여 다음 필터를 실행한 후, then 메서드를 사용하여 응답이 완료된 후에 실행할 작업을 정의합니다.

Spring Cloud와의 통합

  • Spring Cloud Gateway는 Spring Cloud Netflix 패키지의 일부로, Eureka와 쉽게 통합할 수 있습니다.
  • Eureka를 통해 동적으로 서비스 인스턴스를 조회하여 로드 밸런싱과 라우팅을 수행할 수 있습니다.

실습

cloud gateway + eureka + order instance (1) + product instance (2)

yml

server:
  port: 19091  # 게이트웨이 서비스가 실행될 포트 번호

spring:
  main:
    web-application-type: reactive  # Spring 애플리케이션이 리액티브 웹 애플리케이션으로 설정됨
  application:
    name: gateway-service  # 애플리케이션 이름을 'gateway-service'로 설정
  cloud:
    gateway:
      routes:  # Spring Cloud Gateway의 라우팅 설정
        - id: order-service  # 라우트 식별자
          uri: lb://order-service  # 'order-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
          predicates:
            - Path=/order/**  # /order/** 경로로 들어오는 요청을 이 라우트로 처리
        - id: product-service  # 라우트 식별자
          uri: lb://product-service  # 'product-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
          predicates:
            - Path=/product/**  # /product/** 경로로 들어오는 요청을 이 라우트로 처리
      discovery:
        locator:
          enabled: true  # 서비스 디스커버리를 통해 동적으로 라우트를 생성하도록 설정

eureka:
  client:
    service-url:
      defaultZone: http://localhost:19090/eureka/  # Eureka 서버의 URL을 지정

PreFiler

@Component
public class CustomPreFilter implements GlobalFilter, Ordered {

	private static final Logger logger = Logger.getLogger(CustomPreFilter.class.getName());

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpRequest request = exchange.getRequest();
		logger.info("PreFilter : Request URI : " + request.getURI());
		return chain.filter(exchange);
	}

	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE; // 최상위 순서
	}
}

요청을 처리 하기 전에 실행되는 필터로 최상위 순서를 가지고 있다.
GlobalFilter 를 사용함으로써 모든 요청에 대한 로깅을 하는 필터라 생각할 수 있다.

PostFiler

@Component
public class CustomPostFilter implements GlobalFilter, Ordered {

	private static final Logger logger = Logger.getLogger(CustomPostFilter.class.getName());

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		return chain.filter(exchange).then(Mono.fromRunnable(() -> {
			ServerHttpResponse response = exchange.getResponse();
			logger.info("PostFilter : Response Status Code : " + response.getStatusCode());
		}));
	}

	@Override
	public int getOrder() {
		return Ordered.LOWEST_PRECEDENCE;
	}
}

모든 요청에 대해서 응답이 나갈 때 로깅을 하는 역할을 한다.
특별하게 볼 점은 chain.filter 뒤에 then(Mono.fromRunnable(...)) 메서드를 통해 이전의 필터 체인이 완료된 후 실행할 작업을 정의한다. 여기는 Response에 대한 상태코드를 호출중이다.
최 하위 순서로 설정해 결과가 반환되기 가장 마지막에 실행됨을 유추할 수 있다.


마무리

게이트웨이를 활용할 수 있는 방법은 매우 다양하다고 생각한다.

토스는 Gateway 이렇게 씁니다

토스 개발블로그에서 사용처는 크게

  • Request에 대한 전처리, 후처리
  • 유저 정보를 이용한 로직 수행
  • 보안 그리고 서비스 안정화를 위한 설정

으로 사용한다고 한다.

그리고 또 중요한 점 중 하나는 모니터링 이다.
모니터링에 중요한 요소로는 로깅, 메트릭, 트레이싱이 있는데 토스에서는 로깅은 Elasticsearch로 적재하고, 메트릭은 Prometheus & Grafana 으로 수집 & 모니터링을 수행한다고 한다.

또한 게이트웨이는 단일 진입점으로 SPOF 문제가 발생할 수 있다.

토스에서는 Gateway는 Stateless 하게 개발하여 K8S 위에 여러개의 pod 을 두고 트래픽을 분산하고, 또한 업데이트 배포가 있을때는 카나리 배포를 통해 소수점 단위로 최소한 트래픽을 태워 긴시간 검증하고 배포하고 있으며, 카나리 배포를 할 수 없는 경우에도 Active - Active 구조로 된 데이터 센터의 트래픽을 조정하여 데이터 센터 간 카나리 배포를 하고 있습니다.

라고한다.

역시 스케일 아웃을 생각해서 서비스를 개발하는 단계에서도 Stateless 하게 개발해야 한다.
서버의 배포 또한 긴 시간 세세하게 검증하는 것 같고, Active - Active 구조의 데이터 센터까지 있다고 하니 상상할 수 없게 거대한 것 같다.

profile
공부 정리용

0개의 댓글