0. Pre필터와 Post 필터
pre 필터는 요청 전에 필요한 설정이 있을 경우 설정이 필요하며, 이 설정은 요청 처리의 일부로 간주됩니다. 반면에 post 필터는 요청 처리가 완료된 후에 실행되며, 설정이 필요한 경우에도 요청 처리에 직접적인 영향을 미치지 않기 때문에 설정이 필수적이지 않을 수 있습니다.
1. yml파일 - 디폴트 필터 적용
api-gateway-server 파일에 filters를 추가한다.
server:
port: 8000
spring:
application:
name: apigateway-server
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=fsreqhyml,fsreqhvyml #filters에 추가할 내역은 필터명=헤더이름,헤더값
- AddResponseHeader= fsreqhyml,fsreqhvyml
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
response 포스트맨으로 확인: first-service 마이크로서비스 내 header-check 키 값을 기반으로 value값이 출력됨
@GetMapping("header-check")
public String headerCheck(@RequestHeader("fsreqhyml") String header){
return header;
}
2. 커스텀 필터 적용
JPA에서 필터는 OncePerRequestFilter를 상속받아 쿠키나 인증 처리 시 Request요청이 들어왔을 때 한 번동작합니다.
스프링 게이트웨이에서 커스텀 필터는 AbstractGatewayFilterFactory를 상속한 필터 클래스를 @Component 어노테이션을 이용해 등록해주면 동작합니다.
2.1 CustomFilter의 이너클래스인 Config를 생성한다
2.2 생성자에서 super()를 이용해 필터 기본 세팅에 필요한 정보를 전달한다
2.3 추상화 클래스인 AbstractGatewayFilterFactory에서 apply라는 메서드를 오버로딩해서 구현한다
2.4 netty 서버는 tomcat 서버와 다르게 ServerHttpRequest와 ServerHttpResopnce를 사용
package com.example.apigatewayserver.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
// reactive 비동기 처리와 관련된 패키지로 import하기
import org.springframework.http.server.reactive.ServerHttpRequest;
import reactor.core.publisher.Mono;
@Component // Bean 등록
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {// <>자리는 설정 클래스가 들어가는 자리-> CustomFilter 내부 클래스인 Config를 가리킴
public CustomFilter(){
super(Config.class); // 생성자를 이용해 내부클래스 내역을 super()로 보냄
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> { // exchange과 chain을 람디식으로 받는다.
// 대부분의 필터의 경우 요청이 들어왔을 때 사전에 필터힝 - 내장 객체 필요
// 응답이 진행될 때 사후에 작업을 진행 - 내장 객체 필요
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// pre filter : 그냥 작성해도 작동함
System.out.println("Custom pre filter:" +request.getId());
// post filter : return 구문 안에 코드로 작성해야 작동함
return chain.filter(exchange).then(Mono.fromRunnable(()->{
System.out.println("Custom post filter:"+response.getStatusCode());
}));
};
}
public static class Config{ // CustomFilter의 내부클래스인 Config를 생성
}
}
2.5 yml파일에 filters에 @Component가 붙어있는 필터 클래스 이름을 작성함
server:
port: 8000
spring:
application:
name: apigateway-server
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- CustomFilter # @Component이 붙어있는 필터 클래스 이름을 작성함
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
3. 전역 필터 적용
인증,인가 필터와 같이 대부분의 서비스 전체에 적용할 때 전역필터로 사용합니다. 글로벌 필터는 내부에 있는 모든 마이크로서비스에 적용 가능합니다.
전역 필터는 AbstractGatewayFilterFactory 를 사용하되 등록을 게이트웨이 서버측 자체에 해 준다는 차이가 있습니다.
즉, yml파일에서의 등록을 gateway측에 직접 걸어줍니다.
3.1 yml 파일
server:
port: 8000
spring:
application:
name: apigateway-server
cloud:
gateway:
default-filters:
- name: GlobalFilter # Bean에 등록된 @Component가 붙은 필터 클래스명
# args 내부에는 Config 이너클래스 멤버 변수에게 전달할 값을 작성한다.
args:
message: Global Filter Default Message Test # 메시지명
pre: true # true or false
post: true # true or false
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- CustomFilter # @Component이 붙어있는 필터 클래스 이름을 작성함
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- CustomFilter2
3.2 GlobalFilter.java
package com.example.apigatewayserver.filter;
import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
public GlobalFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) ->{
ServerHttpResponse response = exchange.getResponse();
ServerHttpRequest request = exchange.getRequest();
// Config 멤버 변수 정보를 이용해서 전역 필터 내부 코드를 작성
// 1. pre filter
System.out.println("global filter default message:"+config.getMessage());
// pre에서 사용할지 말리 yml파일에서 설정하는 Boolean값으로 판단함
if(config.isPre()){
System.out.println("global pre filter" +request.getId());
}
// 2. post filter
return chain.filter(exchange).then(Mono.fromRunnable(()->{
if(config.isPost()){
System.out.println("global post filter" + response.getStatusCode());
}
}));
};
}
@Getter @Setter
public static class Config{
// 설정 파일에서 인자값을 넘겨받을 수 있도록 Config 이너클래스에 멤버 변수들을 선언합니다
private String message;
private boolean pre;
private boolean post;
public void setPost(boolean post) {
this.post = post;
}
}
}
4. 로그 필터 적용
package com.example.apigatewayserver.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class LogFilter extends AbstractGatewayFilterFactory<LogFilter.Config> {
public LogFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) ->{
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("log filter: {}", request.getId());
return chain.filter(exchange).then(Mono.fromRunnable(()->{ //Mono 는 스프링 5에서 추가되었는데 비동기방식 서버에서 단일값 전달할 때 사용하는 양식입니다.
log.info("log post filter {}",response.getStatusCode());
}));
};
}
public static class Config{
}
}
인자 제공을 각각 한다면 아래와 같이 나열하면 됩니다.
filters:
- name: GlobalFilter
- name: LogFilter
args:
# args 내부에는 Config 이너클래스 멤버 변수에게 전달할 값을 작성한다.
5. 필터 호출 순서
호출 우선순위는 글로벌 pre → 커스텀 pre → 로그 pre → 로그 post → 커스텀 post → 글로벌 post 순으로 마치 스택(후입선출 방식으로)처럼 호출 및 종료가 이뤄집니다.
'MSA' 카테고리의 다른 글
기존 마이크로서비스를 Eureka 클라이언트로 등록 (0) | 2023.10.24 |
---|---|
스프링 디스커버리를 위한 Eureka 클라이언트 생성 (1) | 2023.10.24 |
스프링 디스커버리를 위한 Eureka 서버 세팅 (1) | 2023.10.24 |
Config파일을 이용한 라우팅 정보 설정 + 기본 필터 활용 (0) | 2023.10.19 |
Spring Cloud Gateway와 MSA 학습을 위한 게이트웨이 서버 설정 (1) | 2023.10.18 |