사용자 정의 필터 생성 방법

날아올라돼지야·2024년 8월 28일
0

Spring Cloud Gateway에서 사용자 정의 필터를 생성하는 방법을 다뤄보겠습니다.

1. 사용자 정의 필터 생성 및 비즈니스 로직 설정:

  • RequestTraceFilter: Gateway 서버로 외부 요청이 들어올 때마다 고유한 상관 관계 ID(Trace ID)를 생성하여 요청 헤더에 추가합니다. 이 ID는 이후 모든 마이크로서비스 호출에 전달되며, 로그 추적에 사용됩니다.
  • ResponseTraceFilter: 요청에 대한 응답을 클라이언트로 전달하기 전에, ResponseTraceFilter는 상관 관계 ID를 응답 헤더에 추가하여 클라이언트가 이를 확인할 수 있도록 합니다.

2. FilterUtility 클래스

공통 로직을 FilterUtility 클래스에 정의하여 필터에서 재사용할 수 있도록 했습니다. 이 클래스는 상관 관계 ID를 요청 및 응답 헤더에서 가져오거나 설정하는 역할을 합니다.

3. GlobalFilter 인터페이스 사용:

  • RequestTraceFilter에서는 GlobalFilter 인터페이스를 구현하여 모든 요청에 대해 필터를 적용했습니다. 필터 체인을 사용해 다음 필터를 호출할 수 있도록 구현했습니다.
  • ResponseTraceFilter에서는 GlobalFilter를 구현하는 또 다른 방법으로, 메서드 내에서 GlobalFilter 객체를 생성하고 이를 빈으로 등록했습니다.

4. 디버그 로깅 설정

application.yml 파일에 디버그 로깅을 활성화하는 설정을 추가하여, 필터의 디버그 로그가 출력되도록 했습니다. 이를 통해 필터가 올바르게 작동하는지 확인할 수 있습니다.

구현된 코드 요약

RequestTraceFilter.java

@Component
@Order(1)
public class RequestTraceFilter implements GlobalFilter {

    private final Logger logger = LoggerFactory.getLogger(RequestTraceFilter.class);

    @Autowired
    private FilterUtility filterUtility;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        HttpHeaders headers = exchange.getRequest().getHeaders();
        if (isCorrelationIdPresent(headers)) {
            logger.debug("easybank-correlation-id found in RequestTraceFilter: {}",
                         filterUtility.getCorrelationId(headers));
        } else {
            String correlationId = generateCorrelationId();
            exchange = filterUtility.setCorrelationId(exchange, correlationId);
            logger.debug("easybank-correlation-id generated in RequestTraceFilter: {}", correlationId);
        }
        return chain.filter(exchange);
    }

    private boolean isCorrelationIdPresent(HttpHeaders headers) {
        return filterUtility.getCorrelationId(headers) != null;
    }

    private String generateCorrelationId() {
        return UUID.randomUUID().toString();
    }
}

ResponseTraceFilter.java

@Configuration
public class ResponseTraceFilter {

    private final Logger logger = LoggerFactory.getLogger(ResponseTraceFilter.class);

    @Autowired
    private FilterUtility filterUtility;

    @Bean
    public GlobalFilter postGlobalFilter() {
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                HttpHeaders headers = exchange.getRequest().getHeaders();
                String correlationId = filterUtility.getCorrelationId(headers);
                logger.debug("Updated the correlation id to the outbound headers. {}", correlationId);
                exchange.getResponse().getHeaders().add(FilterUtility.CORRELATION_ID, correlationId);
            }));
        };
    }
}

FilterUtility.java

@Component
public class FilterUtility {

    public static final String CORRELATION_ID = "easybank-correlation-id";

    public String getCorrelationId(HttpHeaders requestHeaders) {
        if (requestHeaders.get(CORRELATION_ID) != null) {
            List<String> requestHeaderList = requestHeaders.get(CORRELATION_ID);
            return requestHeaderList.get(0);
        } else {
            return null;
        }
    }

    public ServerWebExchange setCorrelationId(ServerWebExchange exchange, String correlationId) {
        return exchange.mutate().request(exchange.getRequest().mutate().header(CORRELATION_ID, correlationId).build()).build();
    }
}

application.yml

logging:
  level:
    com.eazybytes.gatewayserver: debug

중간 요약

사용자 정의 필터를 생성하여 상관 관계 ID를 생성하고 이를 모든 마이크로서비스 호출에 전달하는 방법을 배웠습니다. 다음 챕터에서는 마이크로서비스 내에서 이러한 상관 관계 ID를 활용하는 방법에 대해 살펴볼 예정입니다.

헤더 활용

Gateway 서버를 통해 요청이 전달될 때 각 마이크로서비스에서 요청 헤더를 수락하고 처리하는 방법에 대해 알아봅니다. 이제 모든 마이크로서비스에서 easybank-correlationId 헤더를 처리하여 로깅을 추가하고 이 상관 관계 ID를 활용하는 방법을 살펴보겠습니다.

1. 마이크로서비스에서 요청 헤더 수락:

  • 각 마이크로서비스(accounts, loans, cards)의 컨트롤러에서 @RequestHeader를 사용하여 easybank-correlationId를 수락하고 이를 로깅에 사용했습니다.
  • 예를 들어, AccountsController에서 다음과 같이 요청 헤더를 수락하고 로깅합니다:
    @GetMapping("/api/fetchCustomerDetails")
    public CustomerDetails fetchCustomerDetails(@RequestHeader(value = "easybank-correlationId", required = false) String correlationId, @RequestParam String mobileNumber) {
        logger.debug("easybank-correlationId found: {}", correlationId);
        // 로직 생략
    }

2. Feign 클라이언트를 통한 상관 관계 ID 전달:

  • accounts 마이크로서비스에서 loanscards 마이크로서비스로 요청을 전달할 때, 상관 관계 ID를 함께 전달하도록 FeignClient 인터페이스를 수정했습니다.
  • 예를 들어, LoansFeignClientCardsFeignClient에서 @RequestHeader를 추가하여 상관 관계 ID를 수락합니다:
    @GetMapping("/api/fetch")
    LoanDetails fetchLoanDetails(@RequestHeader(value = "easybank-correlationId", required = false) String correlationId, @RequestParam String mobileNumber);

3. 모든 마이크로서비스에서 로깅 활성화:

  • application.yml 파일에 logging.level을 추가하여 디버그 로깅을 활성화했습니다. 이를 통해 com.eazybytes 패키지의 모든 디버그 로그를 확인할 수 있습니다.
  • 각 마이크로서비스(accounts, loans, cards)의 application.yml에서 해당 설정을 추가했습니다:
    logging:
      level:
        com.eazybytes: debug

4. 모든 마이크로서비스 및 Gateway 서버 재시작:

  • 모든 마이크로서비스와 Gateway 서버를 재시작하여 설정이 반영되도록 합니다.

5. Postman을 사용한 테스트:

  • 각 마이크로서비스에 데이터를 생성한 후 fetchCustomerDetails API를 호출하여 상관 관계 ID가 모든 마이크로서비스로 전달되고 로그에 기록되는지 확인합니다.
  • 각 마이크로서비스에서 로그를 검색하여 상관 관계 ID가 올바르게 전달되었는지 확인합니다.

결론

이번 챕터를 통해 상관 관계 ID를 활용하여 Gateway 서버를 통해 전달된 요청이 마이크로서비스 간에 어떻게 추적되고 로깅될 수 있는지 이해할 수 있었습니다. 이로 인해 시스템의 디버깅 및 문제 해결이 훨씬 쉬워졌습니다.

더욱 복잡한 비즈니스 로직과 크로스커팅 관심사를 처리할 수 있는 Gateway 서버의 유연성을 활용하여 더 안전하고 확장 가능한 마이크로서비스 아키텍처를 구축할 수 있습니다.

profile
무슨 생각하며 사니

0개의 댓글