일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- gradle
- vaultTemplate
- ResponseBodyAdvice
- restTemple
- Save Action
- AOP target
- AOP 매개변수
- AOP this
- @AutoConfiguration
- JsonStringType
- fotmatter
- findFirst
- 개방/폐쇄 원칙
- ClientHttpRequestInterceptor
- 포맷터
- findAny
- AccessLevel
- spring
- auto configuration
- java
- LogInterceptor
- Starter
- JsonType
- Stream
- AOP
- 쓰레드 안전
- jpa
- RestTemplate
- Thread Safety
- Spring Boot
- Today
- Total
맨땅에 헤딩하는 개바른자
[Spring] WebClient 빈 별로 RestClient 빈 구성 본문
목차
- 개요
- 구성 파일
- 구성 파일 설명
- WooForwardFilterFunction (필터 기능)
- AddExchangeFilterFunctions (필터 기능 주입)
- WooRestClientAutoConfiguration (AutoConfiguration 설정)
- WooRestClientAutoConfigureRegistrar (WebClient별 RestClient 빈 생성)
- 간략한 정리
1. 개요
RestTemplate와 같이 WebClient가 여러 주소로 통신 하기위해서는 주소별 WebClient빈을 생성해야합니다. 생성 된 빈들은 사용 클래스에서 빈 이름으로 구별하여 주입받아 사용하게 됩니다.
이번 포스팅에서는 여러 WebClient빈을 특정 어노테이션을 이용하여 통신 기능을 담당하는 클래스(Repository)를 생성하고, 이 클래스(Repository)를 실제 구현 클래스(Service) 로직에서 주입받아 사용 할 수 있는 구성을 하는 과정을 설명하겠습니다.
2. 구성 파일
3. 구성 파일 설명
WooForwardFilterFunction
Request Header 부분에 X-Forwarded-For 항목을 추가하는 설정으로 WebClient 전역에 적용하기위해 ExchangeFilterFunction로 역활을 추가합니다.
public class WooForwardFilterFunction implements ExchangeFilterFunction {
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return forwardHeader(request)
.flatMap(next::exchange);
}
private Mono<ClientRequest> forwardHeader(ClientRequest request) {
return Mono.just(
ClientRequest.from(request)
.header(X_FORWARDED_FOR_VALUE, MDC.get(X_FORWARDED_FOR_VALUE))
.build()
);
}
}
Q. 오잉 왜 이렇게? 일반적인 Filter로는 못하나 차이가 뭔지 궁금해
A. ExchangeFilterFunction과 WebClient의 filter 메서드를 사용하여 추가할 수 있는 Filter는 모두 Spring WebFlux 프레임워크에서 사용되는 요청 및 응답의 가로채기, 수정 및 추가 기능을 제공하는 데 사용됩니다. 그러나 두 가지 접근 방식 간에는 몇 가지 차이가 있습니다.
- 인터페이스 vs 클래스:
- ExchangeFilterFunction: 이 인터페이스는 함수형 프로그래밍 스타일을 지원하며, 단일 메서드 filter(ClientRequest request, ExchangeFunction next)를 가지고 있습니다. 이 메서드에서 ExchangeFunction을 통해 실제 요청을 보낼 수 있습니다.
- Filter: 이 클래스는 추상 클래스 또는 인터페이스일 수 있으며, 일반적으로 여러 메서드를 구현해야 합니다. Spring WebFlux에서 WebClient에 대한 ExchangeFilterFunction을 추가하려면 filter 메서드를 구현해야 합니다.
- 유연성:
- ExchangeFilterFunction: 함수형 프로그래밍 스타일로 제공되기 때문에 유연성이 있습니다. 람다 표현식을 사용하여 간결하게 작성할 수 있습니다.
- Filter: 클래스의 형태를 가지므로 상태를 유지할 수 있습니다. 필터 내에서 상태를 저장하거나 여러 메서드를 통해 다양한 동작을 구현할 수 있습니다.
- 구성 및 적용 방법:
- ExchangeFilterFunction: WebClient.Builder를 통해 직접 WebClient 빌드 시 filter 메서드를 사용하여 추가할 수 있습니다.
- Filter: WebClientConfigurer 인터페이스를 구현하거나, WebClient.Builder를 통해 filter 메서드를 사용하여 추가할 수 있습니다. 이때 클래스를 생성하고 빈으로 등록해야 합니다.
AddExchangeFilterFunctions
public class AddExchangeFilterFunctions implements Consumer<List<ExchangeFilterFunction>> {
ExchangeFilterFunction[] filterFunctions;
public AddExchangeFilterFunctions(ExchangeFilterFunction...filterFunctions) {
this.filterFunctions = filterFunctions;
}
@Override
public void accept(List<ExchangeFilterFunction> exchangeFilterFunctions) {
exchangeFilterFunctions.addAll(getFilterFunctions());
}
private List<ExchangeFilterFunction> getFilterFunctions() {
return Arrays.stream(filterFunctions).toList();
}
}
이 코드는 Consumer<List<ExchangeFilterFunction>> 인터페이스를 구현하는 AddExchangeFilterFunctions 클래스입니다. 이 클래스는 WebClient의 filter를 추가할 때 사용할 수 있는 편의성을 제공합니다.
여러 개의 ExchangeFilterFunction을 한 번에 추가하기 위해 사용됩니다. Consumer 인터페이스를 구현하여 함수형 프로그래밍 스타일로 WebClient.Builder에 필터를 추가할 수 있도록 합니다.
public class GlobalExchangeFilterFunctions implements Consumer<List<ExchangeFilterFunction>> {
ExchangeFilterFunction[] filterFunctions;
public GlobalExchangeFilterFunctions(ExchangeFilterFunction...filterFunctions) {
this.filterFunctions = filterFunctions;
}
@Override
public void accept(List<ExchangeFilterFunction> exchangeFilterFunctions) {
exchangeFilterFunctions.addAll(getFilterFunctions());
}
private List<ExchangeFilterFunction> getFilterFunctions() {
return Arrays.stream(filterFunctions).toList();
}
}
코드를 보시면 webClient.Builder에 추가 된 filter을 호출하여 ExchangeFilterFunction에 accept 합니다. 이로볼 때 WebClient가 빈으로 등록 될 때 전역으로 관리되고 있는 ExchangeFilterFunction에 추가하는 것을 알 수 있습니다.
이로 짐작할 수 있는건 전역filter를 관리한다는건 여러개의 다른 WebClient빈들이 생성될 때 공통으로 적용되는 filter가 있다라고 볼 수 있겟습니다.
WooRestClientAutoConfiguration
@Slf4j
@AutoConfiguration
@ConditionalOnClass(WebClient.class)
@ConditionalOnProperty(prefix = "woo.client", name = "enabled", matchIfMissing = true)
@Import(WooRestClientAutoConfigureRegistrar.class)
@EnableConfigurationProperties(WooRestClientProperties.class)
public class WooRestClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public GlobalExchangeFilterFunctions wooClientFilterFunctions() {
log.debug("### WooAutoconfigure Bean: '{}'", "wooClientFilterFunctions");
return new GlobalExchangeFilterFunctions(
new ServletBearerExchangeFilterFunction(),
new WooForwardFilterFunction()
);
}
}
AutoConfiguration을 등록하는 구간입니다. GlocalExchaneFilterFunctions에 설정 된 filter를 Bean으로 등록 합니다. 여기서 WebClient를 Bean으로 등록하는 메소드가 빠져보이지만 자세히보면 @Import(WooRestClientAutoConfigureRegistrar.class) 어노테이션으로 별도 등록을 하고 있습니다.
WooRestClientAutoConfigureRegistrar
이 구간에서는 RestClient를 빈으로 등록하는 과정이 이루워 입니다. 자세한 코드는 GitHub 를 통해서 확인하실 수 있습니다. (GitHub)
우선 설명에 앞서 컨셉에 대해서 설명드리겠습니다.
@WooRestClient라는 어노테이션 인터페이스를 생성하였습니다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface WooRestClient {
String webClientBeanName();
}
해당 어노테이션은 빈으로 생성 된 여러 종유의 WebClient를 주입받아 사용하는 용도의 어노테이션입니다.
예를들어
@WooRestClient(webClientBeanName = "cust1webClient")
interface HelloRepository {
@GetExchange("/hello")
MyResponse hello();
}
record MyResponse(String message) {
}
@AutoConfigurationPackage
@TestConfiguration
static class Cust1WebClientConfiguration {
@Bean
public WebClient cust1WebClient() {
WebClient webClient = WebClient.builder()
.baseUrl("https://cust1.com")
.build();
return mock(webClient);
}
}
Cust1WebClientConfiguration클래스에서는 cust1webClient란 이름으로 빈을 만듭니다. 해당 빈은 https://cust1.com 으로 통신용도로 생성되는 빈입니다.
HelloRepository 인터페이스는 cust1.com으로 통신을 하기위해 cust1WebClient WebClient빈을 주입받게끔 @WooRestClient(webClientBeanName = "cust1WebClient")를 선언합니다.
본 설정으로 다시 이동하여 WooRestClientAutoConfigureRegistrar 클래스에서는 @WooRestClient로 선언 된 항목을 모두 조회하여 RestClient Bean을 등록합니다.
이과정에서 implements FactoryBean<Object>, ApplicationContextAware 기술이 사용되는데 BeanDefinitionBuilder를 genericBeanDefinition() 기능을 사용할 때 필요로 합니다.
4. 간략한 정리
- 사용 케이스
- RestClient 통신용 기능을 담당하는 Repository 클래스 구성
- 해당 클래스에 통신정보를 주입하는 WebClient를 지정
- @WooRestClient(webClientBeanName = "custWebClient")
- GET, POST 등의 기능 메소드 작성
- 해당 클래스에 통신정보를 주입하는 WebClient를 지정
- Service 구현로직에서는 Repository를 주입받아서 통신용 메소드를 사용
- RestClient 통신용 기능을 담당하는 Repository 클래스 구성
- 구성
- spring boot starter 구성 컨셉으로 AutoConfiguration으로 WebClient 필터 전역설정 등록
- ExchangeFilterFunction 기능을 이용하여 필요한 필터를 등록합니다.
- spring boot starter 구성 컨셉으로 AutoConfiguration으로 WebClient빈 별로 Repository 빈 생성
- AutoConfiguration 클래스 설정 파일에 @Import(WooRestClientAutoConfigureRegistrar.class)로 등록합니다.
- AutoConfiguration과 별개로 주소별 기능별 WebClient 빈을 각각 생성합니다.
- 각 Repository에는 @WooRestClient를 선언하여 용도에 맞는 WebClient빈을 지정합니다.
- BeanRegister에서 각 Repository에 선언 된 @WooRestClient 정보를 스캔하여 Repository를 빈으로 등록합니다.
- 위 과정들은 스프링 프로세스가 기동 될 때 처리됩니다.
- spring boot starter 구성 컨셉으로 AutoConfiguration으로 WebClient 필터 전역설정 등록
'SPRING' 카테고리의 다른 글
[Spring] Auto Configuration 설정 시 사용되는 항목 알아보기 (0) | 2024.02.23 |
---|---|
[Spring] Auto Configuration에서 META-INF.spring 역활 (0) | 2024.02.22 |