Gyunpang 프로젝트를 시작하며, 우선적으로 API Gateway를 간단하게 만들어보려 한다.
해당 API Gateway는
- 인증, 인가 기능을 수행
- uri에 따라 알맞은 서버로 요청 인계
- (추후) 트래픽 분산을 위해 Load balancing
대략 이렇게 3가지 기능을 담당하게 될 것이다 (물론, 더 늘어날 수 있다)
처음부터 모든 걸 다 구현하기엔 다소 무리가 있으니 간단하게 Spring Cloud Gateway를 사용하는 방법과 간단한 껍데기를 만들어 보려한다.
개념
공식 문서 에 나와 있는 간단한 그림을 통해 알아보면
- 클라이언트가 보낸 요청을 Gateway Handler Mapping 이 요청이 설정 경로와 일치하는 지 확인하고
- 일치하지 않을 경우 돌려보낸다
- 일치하는 경우 Gateway Web Handler 에 요청을 전달한다.
- Gateway Web Handler 는 받은 요청에 대해서 요청에 걸려있는 필터들로 쭉 보낸다.
- 위 사진을 보면 필터들이 반으로 나뉘어져 있는데 이건 pre, post 필터를 구분하는 것이다.
- 즉, proxy request가 보내지기 전에 걸리는 필터, 보내지고 난 후에 걸리는 필터 모두를 표시한 것
- 위 사진을 보면 필터들이 반으로 나뉘어져 있는데 이건 pre, post 필터를 구분하는 것이다.
쉽게 말하자면, 받은 요청에 대해서 처리할 경로가 있다면 해당 경로에 설정된 필터들을 거쳐 요청을 보내고, 응답을 다시 인계해주는 것이 기본적 개념이다.
크게 어렵지 않을 것이라 생각되니.. 바로 간단하게 구현해보자 !
의존성 추가
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
단순하게 gateway의 역할만 하기에 위와 같이 spring-cloud-starter-gateway 와 이제는 필수인? lombok 만 추가해준다.
Routing 설정
spring:
cloud:
gateway:
routes:
- id: main
uri: <http://localhost:8080>
predicates:
- Path=/main-api/**
filters:
- AddRequestHeader=req, reqValue
- AddResponseHeader=res, resValue
- CustomAuthFilter
- RewritePath=/main-api/(?<path>.*),/$\\{path}
server:
port: 8088
logging:
level:
com.gyunpang.gateway : DEBUG
Spring Cloud Gateway에서 라우팅을 설정하는 방식으론
- 위 처럼 설정 파일에 직접 값을 넣는 방식
- 직접 설정 내용을 Java Code로 작성하여 @Configuration 로 등록하는 방법
이렇게 두 가지 방법이 있다.
아무래도, public repo에 올라가는거니 추후 github secret으로 값을 대체할 생각까지 해본다면 설정파일에 넣는 방식이 합리적이라 생각되어 (또, 더 간단하다) 위 처럼 구현하기로 했다.
간단하게 필드들을 설명하면
id : 말 그대로 식별 id를 의미한다
uri : 받은 요청에 대해서 최종적으로 전달할 서버의 주소
predicates : 받은 요청을 구분짓는 지점 (* Gateway Handler Mapping 에서 경로가 매칭되는 지 확인하는 부분)
filters : 해당 경로에 적용할 필터 리스트 (정말 다양한 필터들이 있고 CustomAuthFilter 처럼 사용자가 직접 필터를 구현하여 등록할 수도 있다)
filters 에 들어간 필터들에 대해서 더 살펴보자.
AddRequestHeader → 말 그대로 요청 헤더에 내가 정한 헤더를 추가한다.
AddResponseHeader → 말 그대로 응답 헤더에 내가 정한 헤더를 추가한다.
CustomAuthFilter → 직접 정의한 헤더를 추가한다
package com.gyunpang.gateway.filters;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class CustomAuthFilter extends AbstractGatewayFilterFactory<CustomAuthFilter.Config> {
public CustomAuthFilter(){
super(Config.class);
log.debug("[Filter] CustomAuthFilter invoked");
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
log.debug("[Filter] got request");
ServerHttpRequest request = exchange.getRequest();
if(!request.getHeaders().containsKey("token")){
return unAuthrizeHandler(exchange); // 401 Error
}
log.debug("[Filter] request is valid");
return chain.filter(exchange);
});
}
private Mono<Void> unAuthrizeHandler(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
log.debug("[Filter] unAuthorized !!");
return response.setComplete();
}
public static class Config{
}
}
위 처럼, AbstractGatewayFilterFactory 추상 클래스를 extends해서 직접 커스텀 필터를 체인에 등록할 수 있다.
RewritePath → 경로를 다시 쓰는 것, 즉 /main-api/{path} 를 /path 로 변경하여 요청을 전달하는 것
LoadBalancing
- 아무래도 도커로 서버 여러개 띄우고 요청을 순차적으로 보내는 등의 추가 작업을 통해 구현 가능하다고 생각
우선은 이렇게 다소 간단하게 게이트웨이를 구성하고 마무리하려 한다.
추후, 인증 서버, 메인 백엔드서버, 백오피스 서버 등등 여러 서버들의 틀을 잡고 난 후 상세하게 구현해보자 !
'Gyunpang' 카테고리의 다른 글
6. 여러대의 인스턴스에 무중단 배포하기 (0) | 2024.04.06 |
---|---|
5. Nginx + React 무중단 배포하기 (0) | 2024.03.26 |
4. Docker + Github Action + nginx로 CI/CD 파이프라인 구축하기 (1) | 2024.03.24 |
3. 도메인 연결 및 nginx 설정하기 (0) | 2024.03.24 |
2. Docker를 통해 Sonarqube를 프로젝트와 연동시켜보자 (0) | 2024.03.24 |