API Gateway

미미·2025년 11월 5일

MSA

목록 보기
5/5
post-thumbnail

API Gateway란?

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

주요기능

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

Spring Cloud Gateway란?

Spring Cloud Gateway는 Spring 프로젝트의 일환으로 개발된 API 게이트웨이로, 클라이언트 요청을 적절한 서비스로 라우팅하고 다양한 필터링 기능을 제공합니다.
Spring Cloud Netflix 패키지의 일부로, Eureka와 쉽게 통합할 수 있습니다.
Eureka를 통해 동적으로 서비스 인스턴스를 조회하여 로드 밸런싱과 라우팅을 수행할 수 있습니다.

특징

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

필터링

  • Global Filter : 모든 요청에 대해 작동하는 필터 (GlobalFilter 인터페이스 구현, filter 메서드 오버라이드)
  • Gateway Filter : 특정 라우터에만 적용되는 필터 (GatewayFilter 인터페이스 구현, filter 메서드 오버라이드)

필터 주요 객체

  • Mono :리액티브 프로그래밍에서 0 또는 1개의 데이터를 비동기적으로 처리합니다.
    - Mono<Void> 는 아무 데이터도 반환하지 않음을 의미합니다.
  • ServerWebExchange : HTTP 요청과 응답을 캡슐화한 객체입니다.
    - exchange.getRequest()로 HTTP 요청을 가져옵니다.
    - exchange.getResponse()로 HTTP 응답을 가져옵니다.
  • GatewayFilterChain : 여러 필터를 체인처럼 연결합니다.
    - chain.filter(exchange)는 다음 필터로 요청을 전달합니다.

필터 시점별 종류

  • Pre 필터 : 요청이 처리되기 전에 실행됩니다.
    Pre 필터에서는 요청을 가로채고 필요한 작업을 수행한 다음, 체인의 다음 필터로 요청을 전달합니다. 이때, 추가적인 비동기 작업을 수행할 필요가 없기 때문에 then 메서드를 사용할 필요가 없습니다.

    
    @Component
    public class PreFilter implements GlobalFilter, Ordered {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            // 요청 로깅
            System.out.println("Request: " + exchange.getRequest().getPath());
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {  // 필터의 순서를 지정합니다.
            return -1;  // 필터 순서를 가장 높은 우선 순위로 설정합니다.
        }
    }
  • Post 필터 : 요청이 처리된 후, 응답이 반환되기 전에 실행됩니다.
    Post 필터에서는 체인의 다음 필터가 완료된 후에 실행되어야 하는 추가적인 작업을 수행해야 합니다. 따라서 chain.filter(exchange)를 호출하여 다음 필터를 실행한 후, then 메서드를 사용하여 응답이 완료된 후에 실행할 작업을 정의합니다.

    @Component
    public class PostFilter implements GlobalFilter, Ordered {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // 응답 로깅
                System.out.println("Response Status: " + exchange.getResponse().getStatusCode());
            }));
        }
    
        @Override
        public int getOrder() {
            return -1;
        }
    }
    

    실습


    server, order, product는 이전 실습에서 사용했던 프로젝트를 사용하겠습니다.

게이트웨이 서버 생성


게이트웨이 서버를 import하고 간단한 CustomPreFilter와 CustomPostFilter를 작성해보겠습니다.

CustomPreFilter

@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 response = exchange.getRequest();
        logger.info("Pre Filter: Request URI is " + response.getURI());
        // Add any custom logic here

        return chain.filter(exchange);
    }

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

CustomPostFilter

@Component
public class CustomPostFilter implements GlobalFilter, Ordered {

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

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            ServerHttpResponse response = exchange.getResponse();
            logger.info("Post Filter: Response status code is " + response.getStatusCode());
            // Add any custom logic here
        }));
    }

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


라우팅 설정을 위한 yml파일 생성

  • application.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'라는 이름으로 로드 밸런싱(lb)된 서비스로 라우팅
          predicates:
            - Path=/order/**  # /order/** 경로로 들어오는 요청을 이 라우트로 처리
        - id: product-service  # 라우트 식별자
          uri: lb://product-service  # 'product-service'라는 이름으로 로드 밸런싱(lb)된 서비스로 라우팅
          predicates:
            - Path=/product/**  # /product/** 경로로 들어오는 요청을 이 라우트로 처리
      discovery:
        locator:
          enabled: true  # 서비스 디스커버리를 통해 동적으로 라우트를 생성하도록 설정

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

cloud:gateway:routes:

  • id: order-service
    • 라우트 규칙 식별자
    • /order/** 로 들어오는 요청을 처리
    • lb://order-service 로 라우팅
    -> lb:// prefix는 Eureka 클러스터에 등록된 서비스 목록 중 "order-service" 로 로드 밸런싱한다는 의미.

  • id: product-service
    • /product/** 로 들어온 요청을 "product-service" 로 라우팅
    • 동일하게 Eureka 기반 로드 밸런싱 적용

실행

이제 유레카 서버 ⇒ 게이트웨이 ⇒ 주문 ⇒ 상품 순으로 어플리케이션을 실행해보겠습니다.
1. http://localhost:19091/order 로 접속하여 게이트웨이에서 order 서비스를 호출하는 것을 확인 할 수 있습니다.
2. http://localhost:19091/product 를 여러번 호출 하면서 포트가 달라지는 것을 확인합니다. 이를통해 로드밸런싱이 동작함을 확인합니다.
3. 게이트 웨이의 로그를 보면 호출 할때마다 필터가 동작하는 것을 확인 할 수 있습니다.

0개의 댓글