📌 API 게이트웨이
`API 게이트웨이`는 클라이언트의 요청을 받아 다양한 백엔드 서비스로 라우팅하고 다양한 부가 기능을 제공하는 중간 서버이다.
즉 클라이언트 요청을 받아 적절한 서비스로 전달하는 역할을 수행한다. 그 외에도 인증 및 권한 부여, 로드 밸런싱, 모니터링 및 로깅 등의 기능을 수행한다.
📌 Spring Cloud Gateway
`Spring Cloud Gateway`란 Spring 프로젝트의 일환으로 개발된 API 게이트웨이로 MSA 아키텍처에서 널리 사용된다.
동적 라우팅, 필터링, 모니터링, 보안 등의 기능을 제공한다.
`Spring Cloud Gateway`를 사용하려면 관련 의존성을 추가해야 한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
}
또한 `application.yml` 파일에서 라우팅 설정을 정의할 수 있다. 예시는 실습에서 볼 것이다!
Spring Cloud Gateway 에는 두 가지 필터가 있다모든 요청에 대해 작동하는 `Global Filter`와 특정 라우트에만 작동하는 `Gateway Filter` 이다.필터를 구현하려면 `GlobalFilter`/`GatewayFilter` 인터페이스를 구현하고, `filter` 메서드를 오버라이드 해야 한다.
필터에는 시점별로 두 가지 종류가 있는데 요청이 처리되기 전에 실행되는 `Pre` 필터와요청이 처리된 후 실행되는 `Post` 필터가 있다.
@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` 필터는 `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;
}
}
return 한 후에 필터 로직을 수행하기 때문에 `then`이 필요한 것이다!
📌 Spring Cloud Gateway 실습
Erueka + Order (1개) + Product (3개) + Cloud Gateway 를 사용하여 진행한다.
gateway로 요청하여 다양한 서비스로 라우팅하는 방식이다.
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을 지정
게이트웨이 어플리케이션의 `application.yml` 파일이다.
이런 식으로 `routes`에 서비스들을 등록하고 경로에 따라 어떤 라우트로 처리할건지 설정해주면 된다.
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.logging.Logger;
@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;
}
}
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.logging.Logger;
@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;
}
}
이는 `PreFilter`와 `PostFilter`를 작성한 것이다.
`Mono`는 필터의 주요 객체로 리액티브 프로그래밍에서 0 또는 1개의 데이터를 비동기적으로 처리한다.
`ServerWebExchange`는 Http 요청과 응답을 캡슐화한다.
`exchange.getRequest()`는 Http 요청을 가져오고 `exchange.getResponse()`는 Http 응답을 가져온다.
`GatewayFilterChain`은 여러 필터를 체인처럼 연결한다. `chain.filter(exchange)를 통해 다음 필터로 요청을 전달하고 있다.
나머지 서비스들은 이전에 진행한 실습 코드와 거의 동일하기에 생략하도록 하겠다.
유레카 서버 ⇒ 게이트웨이 ⇒ 주문 ⇒ 상품 순으로 어플리케이션을 실행하면
`/19091/order` 로 접속 시 order 서비스를 호출하고
`/19091/product` 로 접속 시 product 서비스를 호출하는 것을 확인할 수 있다.
또한 로그 확인 시, 호출 할때마다 필터가 동작하는 것을 확인할 수 있다!
실습을 진행한 결과 API Gateway는 다양한 백엔드 서비스로 라우팅하는 중간 계층 역할을 수행하고
해당 과정에서 인증, 필터링, 로드 밸런싱 등의 다양한 기능을 수행하는 것을 알게 되었다.
'공부 > Spring' 카테고리의 다른 글
Springboot JPA N+1 문제와 해결법 (JPQL, FetchType) (0) | 2024.09.02 |
---|---|
[Spring Cloud] Gateway PreFilter에 로그인 추가하기 (OAuth2 + JWT) (0) | 2024.08.05 |
[Spring Cloud] 서킷 브레이커 (Resilience4j) (0) | 2024.08.05 |
[Spring Cloud] 클라이언트 사이드 로드 밸런싱 (FeignClient와 Ribbon) (0) | 2024.08.05 |
[Spring Cloud] MSA Spring Cloud (Eureka) (0) | 2024.08.01 |