본 포스팅은 Spring Cloud 를 기반으로 MSA 구조를 학습하고자 작성하는 포스팅입니다.
아래 포스팅을 먼저 보고 오시면 이해가 수월하시니 참고바랍니다.
https://byunsw4.tistory.com/31
1. API Gateway 란?
MSA 구조에서 꼭 필요한 구성 중 하나는, 클라이언트의 요청이 각각 어떤 마이크로 서비스에서 실행되어야 하는지 분기 처리를 담당하는 무언가가 존재해야 합니다. 보통 이런 역할을 수행하는 서비스를 MSA 에서 API Gateway(이하. Gateway) 라고 부릅니다.
Gateway 를 사용할 때, 가장 큰 장점 중 하나는 "클라이언트가 호출하고자 하는 각각의 마이크로 서비스의 주소를 알 필요가 없다." 는 점입니다.
예를 들어, /users api 를 처리하는 사용자 마이크로 서비스와 /orders api 를 처리하는 주문 마이크로 서비스가 있다고 할 때, Gateway가 없다면, 아래와 같이 클라이언트는 2개의 URL 정보를 알고 있어야 합니다.
이런 경우, 만약 사용자 마이크로 서비스의 주소가 변경되거나, 각 마이크로 서비스의 Port 정보가 변경되면, 해당 마이크로 서비스를 호출하고 있는 모든 클라이언트에서 변경을 해야함으로, 굉장히 많은 변경 포인트가 요구됩니다.
(사용자 마이크로 서비스를 100군데의 클라이언트에서 쓴다면, 100군데의 클라이언트 모두 변경되어야 함.)
이런 경우, API Gateway 를 도입하여, 클라이언트는 고정된 API Gateway 의 주소를 통해 요청을 보내고, 요청을 받은 API Gateway 에서 요청온 URI 에 맞게 각 마이크로 서비스로 요청을 전달하는 기능을 수행하게되면, 클라이언트와 마이크로 서비스간의 독립적인 운영이 가능해집니다.
이렇게 되면, 클라이언트는 API Gateway 의 주소만 알면, 원하는 결과를 얻을 수 있게 되고, 외부(클라이언트)의 요청으로 부터 주요 서비스(서버)의 직접적인 접근을 막아, 보안을 더욱 강화할 수 있게 됩니다.
이렇듯 MSA 구조에서 API Gateway 는 꼭 필요한 구성 요소이며, 모든 클라이언트의 요청이 통과하는 관문 역할을 수행하기 때문에 아래와 같은 여러 공통 기능을 수행하기에 적합합니다.
- 접속 로그 기록 : 클라이언트가 어떤 요청을 언제 보냈는지 등의 Access Log 기록
- 인증/인가 : 요청의 인증정보를 기반으로 한 인증 처리 수행 및 인증된 사용자에게 적절한 접근 권한 부여
2. Spring Cloud Gateway 적용
API Gateway 적용을 위해 사용할 수 있는 라이브러리 중 spring 에서 제공해주는 Spring Cloud Gateway 가 있습니다.
아래 의존성을 추가하면, Spring Cloud Gateway 를 사용할 수 있습니다.
```build.gradle
// ... 이상 생략
// 여기 추가
ext {
set('springCloudVersion', "2022.0.4")
}
dependencies {
// 여기 의존성(gateway, eureka-client) 추가
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
// ... 이하 생략
}
// 여기 추가
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
의존성 추가 후, 아래와 같이 Eureka Client 설정을 추가해줍니다.
# application.yml
# eureka client 설정 추가
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
// @EnableDiscoveryClient 추가 - eureka client 등록
@EnableDiscoveryClient
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
이후, 서비스를 실행해서 Eureka Server (http://127.0.0.1:8761) 에 접속하면 아래와 같이 Gateway 서비스가 추가된 것을 확인할 수 있습니다.
3. Routing 설정하기
API Gateway 의 가장 큰 역할은 "클라이언트의 요청을 마이크로 서비스에게 전달하는 것" 입니다.
먼저, Routing을 테스트 하기 위해 사전에 만들어놓은 2개의 마이크로 서비스를 실행해서 Eureka Server 에 추가해놨습니다.
USER-SERVICE 는 /users 와 관련된 요청을 처리하고, MAIN-SERVICE 는 그 외의 모든 요청을 처리한다고 예를 들어보겠습니다.
API Gateway 에서 요청에 따라 두 서비스로 요청을 routing 해주는 설정은 아래와 같게 됩니다.
@RequiredArgsConstructor
@Configuration
public class GatewayConfiguration {
private final JwtAuthorizationFilter jwtAuthorizationFilter;
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", predicateSpec -> predicateSpec
.path("/users/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec
.removeRequestHeader(HttpHeaders.COOKIE)
.filter(jwtAuthorizationFilter))
)
.uri("lb://USER-SERVICE")
)
.route("main-service", predicateSpec -> predicateSpec
.path("/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec
.removeRequestHeader(HttpHeaders.COOKIE)
.filter(jwtAuthorizationFilter))
)
.uri("lb://MAIN-SERVICE")
)
.build();
}
}
기본적인 routing 설정으로, "/users" 로 시작하는 모든 요청은 "lb://USER-SERVICE" 로 요청을 다시 보내고, 그 외의 요청은 "lb://MAIN-SERVICE" 로 요청을 다시 보내는 설정입니다.
route() 메서드 내에는 predicateSpec 을 통해 routing의 전체적인 설정을 추가할 수 있습니다.
.route("user-service", predicateSpec -> predicateSpec
.path("/users/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec
.removeRequestHeader(HttpHeaders.COOKIE)
.filter(jwtAuthorizationFilter))
.uri("lb://USER-SERVICE")
)
먼저, path() 을 통해 API Gateway 로 들어온 요청 중 어떤 요청이 해당 Routing 에 적용될 것 인가를 지정할 수 있습니다.
위 코드를 예로 들면, path("/users/**") 라는 설정은 "API Gateway 로 들어온 요청 중에 /users 를 시작하는 요청은 체인잉된 filters() 메서드를 통해 요청을 가공하고, 최종적으로 해당 요청을 'lb://USER-SERVICE' 로 다시 보낸다." 라고 할 수 있습니다.
filters() 메서드 내에 gatewayFilterSpec 을 통해 해당 요청에 어떠한 추가 작업을 처리할 것인지, 어떠한 filter 를 적용시킬 것인지 등을 설정할 수 있습니다. 위 코드를 예로 들면, API Gateway 로 들어온 요청을 다시 전달할 때, 아래 2가지 과정을 거친 후 요청을 보낸다는 의미입니다.
- 요청 헤더에서 cookie 를 모두 제거한다.
- 요청에 대해 jwtAuthorizationFilter 라는 필터를 수행한다.(해당 필터에 대한 추후에...)
마지막으로 uri() 메서드를 통해, 최종적으로 위 과정을 거친 요청을 어디로 보낼 것인가 를 결정합니다.
lb://USER-SERVICE 는, Eureka Server 에 등록된 마이크로 서비스 중 USER-SERVICE 라는 이름으로 등록된 애플리케이션으로 요청을 보내기 위한 설정임을 의미합니다.
기타 추가로 필요하신 기능은 아래 공식문서에서 참고하시면 좋으실 것 같아 링크 남깁니다.
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
참고 및 출처
https://github.com/dongha-byun/springboot-shoppingmall-api-gateway
https://spring.io/projects/spring-cloud-gateway
'DDD&MSA' 카테고리의 다른 글
[MSA] Feign Client - 마이크로 서비스 간 통신 구현하기 (0) | 2023.11.08 |
---|---|
[MSA] API Gateway 에 인증 구현하기 - GatewayFilter 추가하기 (0) | 2023.10.08 |
[MSA] Micro Service Architecture(MSA) 시작하기 - Eureka Server & Client (0) | 2023.09.10 |
[DDD] CQRS - Command 와 Query 의 분리 (0) | 2023.09.07 |
[DDD] 이벤트 처리하기 : 3. 이벤트 저장소 (0) | 2023.08.24 |
댓글