Cloud/MSA
[MSA] Spring Cloud Gateway
문승주
2024. 6. 29. 15:50
반응형
본 내용은 인프런의 이도원 님의 강의 "Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)" 내용을 바탕으로 정리한 내용입니다.
Spring Cloud Gateway란?
- Spring Reactive 생태계에 구현된 API Gateway로 이 서비스는 논블로킹 (non-blocking), 비동기 (Asynchronous) 방식의 Netty Server를 사용하여 규모와 관계없이 REST 및 WebSocket API를 생성, 게시, 유지, 모니터링 및 보호하는 역할을 한다.
Predicate
- 요청을 어떤 경로로 라우팅할지 결정하는데 사용되는데 Pre Filter와 Post Filter 의 역할을 수행한다.
- Pre Filter
- 메소드가 실행되기 전에 요청에 대한 사전 조건을 검증한다.
- Post Filter
- 메소드의 실행을 멈추지 않지만 실행이 완료된 후에 요청에 대한 사후 조건을 검증한다.
Spring Cloud Gateway의 역활
RESTful API 생성
- HTTP 기반의 상태 비저장 클라이언트-서버 통신을 활성화하며 표준 HTTP 메서드 (GET, POST, PUT, PATCH, DELETE)를 구현한다.
WebSocket API 생성
- 클라이언트와 서버 간에 상태를 저장하는 전이중 통신을 지원하며 수신 메시지를 메시지 콘텐츠에 따라 라우팅한다.
Spring Cloud Gateway 구현
GateWayService-Project 생성
- Spring Boot : 3.2.7-SNAPSHOT
- Java : 11
- Project Name : gateway-service
- Dependencies : DevTools, Eureka Discovery Client, Gateway
1. Java 코드로 구현
GateWayService-Project 코드
application.yml
server:
port: 8080
spring:
application:
name: gateway-service
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka
eureka.client.register-with-eureka: false
: 애플리케이션은 Eureka 서버에 자신을 등록하지 않는다.eureka.client.fetch-registry: false
: 애플리케이션은 Eureka 서버에서 다른 서비스의 정보를 가져오지 않는다.eureka.client.service-url.defaultZone: http://localhost:8761/eureka
: 애플리케이션은 Eureka 서버를http://localhost:8761/eureka
에서 찾는다.
FilterConfig
- 자바코드를 사용하여 특정 URI 요청 시 필터 작업을 한다.
package com.example.gateway_service.filter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
/**
* first-service나 second-service 호출시 필터작업
* RequestHeader와 ResponseHeader에 값을 추가
* @param builder
* @return
*/
@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder){
return builder.routes()
.route(r -> r.path("/first-service/**")
.filters(f -> f.addRequestHeader("first-request","first-request-header")
.addRequestHeader("first-response", "first-response-header"))
.uri("http://localhost:8081/"))
.route(r -> r.path("/second-service/**")
.filters(f -> f.addRequestHeader("second-request","second-request-header")
.addRequestHeader("second-response", "second-response-header"))
.uri("http://localhost:8082/"))
.build();
}
}
- `/first-service/`로 들어오는 요청**
- 요청 헤더에
first-request: first-request-header
를 추가한다. - 응답 헤더에
first-response: first-response-header
를 추가한다. - 요청을
http://localhost:8081/
로 전달한다.
- 요청 헤더에
- `/second-service/`로 들어오는 요청**
- 요청 헤더에
second-request: second-request-header
를 추가한다. - 응답 헤더에
second-response: second-response-header
를 추가한다. - 요청을
http://localhost:8082/
로 전달한다.
- 요청 헤더에
FirstService-Project / SecondService-Project 수정
- uri 뒤에 Path 가 붙여서 호출되므로 @RequestMapping에 **-service를 추가해야 된다.
- 필터 작업에 따른 message 헤더 추가
FirstServiceController
package com.example.firstservice.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/first-service")
public class FirstServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the First service.";
}
@GetMapping("/message")
public String message(@RequestHeader("first-request") String header) {
System.out.println(header);
return "Hello World in First Service";
}
}
SecondServiceController
package com.example.secondservice.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/second-service")
public class SecondServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the Second service.";
}
@GetMapping("/message")
public String message(@RequestHeader("second-request") String header) {
System.out.println(header);
return "Hello World in Second Service";
}
}
- 요청 헤더와 응답 헤더에 필터에서 추가한 값이 포함됩니다.
결과
그림 1) first-reponse-header 값이 ResponseHeaders에 추가되고 second-service도 second-reponse-header 값이 ResponseHeaders에 추가된다.
2. yml 파일로 구현
GateWayService-Project 코드
- FilterConfig 파일 삭제
application.yml
server:
port: 8080
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=first-request, first-request-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-request-header2
- AddResponseHeader=second-response, second-response-header2
- Java 코드 대신
application.yml
에 경로, 필터, 헤더 등을 정의. - 동일한 필터 동작을 yml 설정으로 처리.
결과
그림 2) Postman에서 요청 후 응답헤더에 설정한 필터 first-response-header2 값 확인
Java vs yml 비교
구현 방식 | 특징 |
Java 코드로 설정 | 동적인 라우팅 로직 구현 가능. 복잡한 조건과 커스터마이징에 적합. |
yml 파일로 설정 | 간단한 설정과 유지보수에 적합. 코드 분리를 통해 가독성과 간결성을 유지. |
반응형