반응형
해당 페이지에서는 Spring Boot Webflux를 이용하여 실제 구현하고 활용하는 방법과 WebClient를 이용한 다른 도메인 호출 방법에 대해 공유합니다.
💡 [참고] 이전에 작성한 글을 읽고 오시면 크게 도움이 됩니다.
1) 주요 구성요소 확인
💡 개발 환경을 구성하기 이전에 주요 구성 요소들에 대해 확인한 뒤에 환경을 설정합니다.
1. Controller vs Router
💡 Spring Webflux는 비동기적 서비스를 위한 Spring 프레임워크의 한 요소로 ‘Controller’와 ‘Router’라는 두 가지 주요 컴포넌트를 제공합니다.
💡 Controller 방식은 Spring MVC와 유사한 방식으로 HTTP 요청을 처리하는 데 사용됩니다. 이 컴포넌트는 주로 RESTful API 엔드포인트를 구현하는 데 사용됩니다.
💡 Router 방식은 HTTP 요청을 처리하는 데 사용되는 라우터 및 핸들러 함수를 정의하는 데 사용됩니다. 비동기적으로 실행되며 스레드가 블로킹되지 않아 더 높은 처리량을 달성할 수 있습니다.
결론적으로는 Controller 방식은 동기식 요청 처리에 적합하며, Router는 비동기식 요청처리에 적합합니다.
분류 | Router 방식 | Controller 방식 |
설정 방식 | Java DSL을 사용하여 라우팅 규칙을 정의한다. | Annotation을 사용하여 URL 경로를 정의한다. |
요청 매핑 | 요청을 처리하는 메서드를 선택하기 위해 RequestPredicate를 사용한다. | URL 경로에 @RequestMapping 어노테이션을 사용한다. |
요청 처리 | HandlerFunction 또는 WebFlux.fn.ServerResponse 타입의 객체를 반환한다. | @RestController 어노테이션을 사용하여 해당 클래스의 모든 메서드가 HTTP 응답을 반환한다는 것을 나타낸다. |
예외 처리 | RouterFunction에서 예외 처리를 할 수 있다. | @ControllerAdvice 어노테이션을 사용하여 예외 처리 핸들러를 등록할 수 있다. |
[ 더 알아보기 ]
💡 엔드포인트(Endpoint)
- RESTful API에서 URI(Uniform Resource Identifier)로 식별되는 특정한 인터넷 자원에 대한 접근 경로를 말합니다.
- 클라이언트는 URI를 통해 서버에 요청을 보내고, 서버는 해당 요청에 대한 응답을 반환합니다.
- 예를 들어, https://example.com/api/users는 사용자 정보를 가져오기 위한 엔드포인트입니다.
💡 Java DSL(Java Domain Specific Languague)
- 특정 도메인에서 사용되는 언어를 의미하며 자바 코드를 사용하여 Router를 정의할 수 있습니다. Router는 요청을 처리하기 위해 적절한 핸들러 함수를 선택하는 메커니즘을 제공합니다.
- Java DSL을 사용하면 코드 가독성을 높일 수 있고, Router에 대한 정적 타입 검사를 수행할 수 있습니다.
2. Router Function
💡 Router Function
- Handler Function을 호출하는 역할을 합니다. 이를 통해 HTTP 요청을 받고 적절한 Handler Function으로 라우팅 할 수 있습니다.
- Router Function은 RouterFunctions 클래스를 사용하여 생성할 수 있으며 다양한 메소드를 사용하여 HTTP 요청에 따라 적절한 Handler Function으로 라우팅 할 수 있습니다.
- Router Function을 사용하면 라우팅 규칙을 보다 직관적이고 유연하게 제어할 수 있습니다.
@Bean
public RouterFunction<ServerResponse> routerFunction(ServiceHandler handler) {
return RouterFunctions.route(RequestPredicates.GET("/service/{id}"), handler::getService)
.andRoute(RequestPredicates.GET("/services"), handler::getServices)
.andRoute(RequestPredicates.POST("/service"), handler::createService)
.andRoute(RequestPredicates.PUT("/service/{id}"), handler::updateService)
.andRoute(RequestPredicates.DELETE("/service/{id}"), handler::deleteService);
}
[ 더 알아보기 ]
💡RequestPredicates
- Spring WebFlux에서 HTTP 요청을 처리하기 위한 RouterFunction을 작성할 때 사용되는 요청 매핑 조건을 나타내는 클래스입니다.RequestPredicates 클래스는 정적 메서드로 여러 요청 매핑 조건을 만들 수 있습니다.
3. Handler Function
💡 Handler Function
- 단일 입력과 단일 출력을 가지며, Spring WebFlux Framework가 HTTP 요청을 처리하기 위해 호출하는 메서드입니다.
- Handler Function은 Router Function에 매핑됩니다. Router Function은 HTTP 요청을 Handler Function으로 라우팅하는 Spring WebFlux Framework의 메서드입니다.
@Service
public class ServiceHandler {
private final ServiceRepository serviceRepository;
public ServiceHandler(ServiceRepository serviceRepository) {
this.serviceRepository = serviceRepository;
}
public Mono<ServerResponse> getService(ServerRequest request) {
return serviceRepository.findById(request.pathVariable("id"))
.flatMap(service -> ServerResponse.ok().body(BodyInserters.fromValue(service)))
.switchIfEmpty(ServerResponse.notFound().build());
}
public Mono<ServerResponse> getServices(ServerRequest request) {
return ServerResponse.ok().body(serviceRepository.findAll(), Service.class);
}
public Mono<ServerResponse> createService(ServerRequest request) {
return request.bodyToMono(Service.class)
.flatMap(service -> serviceRepository.save(service))
.flatMap(service -> ServerResponse.created(URI.create("/service/" + service.getId())).build());
}
public Mono<ServerResponse> updateService(ServerRequest request) {
return request.bodyToMono(Service.class)
.flatMap(service -> serviceRepository.findById(request.pathVariable("id"))
.flatMap(existingService -> {
existingService.setName(service.getName());
existingService.setDescription(service.getDescription());
return serviceRepository.save(existingService);
}))
.flatMap(service -> ServerResponse.noContent().build())
.switchIfEmpty(ServerResponse.notFound().build());
}
public Mono<ServerResponse> deleteService(ServerRequest request) {
return serviceRepository.deleteById(request.pathVariable("id"))
.flatMap(result -> ServerResponse.noContent().build())
.switchIfEmpty(ServerResponse.notFound().build());
}
}
4. Mono
💡 Mono
- Reactor 라이브러리에서 제공하는 Reactive Streams의 Publisher 중 하나로 오직 ‘0개 또는 하나의 데이터항목 생성’하고 이 결과가 생성되고 나면 스트림이 종료되면 결과 생성을 종료합니다.
- Mono를 사용하여 비동기적으로 결과를 반환하면 해당 결과를 구독하는 클라이언트는 결과가 생성될 때까지 블로킹하지 않고 다른 작업을 수행할 수 있습니다.
public Mono<String> getData() {
// perform some database or API call to get data
return Mono.just("example data");
}
5. Flux
💡 Flux
- Reactor 라이브러리에서 제공하는 Reactive Streams의 Publisher 중 하나로 Mono와 달리 ‘여러 개의 데이터 항목’를 생성하고 스트림이 종료되면 결과 생성을 종료합니다.
- 비동기 작업을 수행하면 작업이 완료될 때까지 블로킹하지 않고 다른 작업을 수행할 수 있습니다.
- Spring WebFlux에서 Flux를 사용하여 HTTP 요청을 처리하는 경우, 요청을 수신한 즉시 해당 요청을 처리하고 결과를 생성하는 대신 결과 생성이 완료될 때까지 다른 요청을 처리할 수 있습니다.
public Flux<String> getStreamedData() {
// perform a continuous stream of data
return Flux.just("streamed data 1", "streamed data 2", "streamed data 3");
}
2) 환경 구성
1. 개발환경
[ 더 알아보기 ]
💡 Webflux랑 SQL Mapper(MyBatis)는 함께 사용이 불가능 한가?
- 함께 사용하기가 어렵다. WebFlux의 경우는 비동기적이고 논 블로킹 방식으로 동작하며, MyBatis는 동기적 블로킹 방식으로 동작하기 때문에 같이 사용하면 MyBatis가 디폴트로 사용하는 스레드 풀을 블로킹하게 되어 전체적인 성능에 악영향을 미칠 수 있다고 합니다.
💡 그럼 Webflux랑 ORM(Object-Relational Mapping)는 함께 사용이 가능한가?
- WebFlux는 비동기 및 논 블로킹 방식으로 동작하며, ORM은 동기적인 블로킹 방식으로 동작하므로 함께 사용하면 전체적인 성능에 악영향을 미칠 수 있습니다.
💡 그럼 SQL Mapper, ORM 말고 무엇과 Webflux를 함께 사용해야하는가?
- RDBMS를 사용하는 것이 아닌 MongoDB를 함께 사용하는것이 일반적입니다. MongoDB는 비동기적이고 논 블로킹 방식으로 동작하기 때문입니다.
2. gradle.build 라이브러리 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux' // Spring Boot Webflux
}
Maven Repository: org.springframework.boot » spring-boot-starter-webflux
3. HandlerFunction을 구성
💡 Hello 메시지를 반환해 주는 단일 반환(Mono) 값의 서비스를 구성하였습니다.
package com.adjh.multiflexapi.modules.flux.handler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
/**
* 코드와 관련된 라우터입니다.
*
* @author : jonghoon
* @fileName : CodeRouter
* @since : 2023/08/13
*/
@Configuration
public class CodeRouter {
@Bean
public RouterFunction<ServerResponse> routerFunction(CodeHandler handler) {
return RouterFunctions.route(RequestPredicates.GET("/hello"), handler::hello)
.andRoute(RequestPredicates.POST("/api/v2/hello"), handler::hello);
}
}
4. Router Function을 구성
💡 구성한 Handler Function 함수의 서비스에 대해 Router를 통해 Endpoint를 연결합니다.
package com.adjh.multiflexapi.modules.flux.handler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
/**
* 코드와 관련된 라우터입니다.
*
* @author : jonghoon
* @fileName : CodeRouter
* @since : 2023/08/13
*/
@Configuration
public class CodeRouter {
@Bean
public RouterFunction<ServerResponse> route(CodeHandler codeHandler) {
return RouterFunctions.route(
RequestPredicates.GET("/api/v2/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), codeHandler::hello);
}
}
3) WebClient 이용방법 : 주요 메서드
분류 | 메서드 | 설명 |
인스턴스 생성 | create() | WebClient 인스턴스를 생성합니다. |
HTTP 메소드 | get() | GET 요청을 정의합니다. |
HTTP 메소드 | post() | POST 요청을 정의합니다. |
HTTP 메소드 | put() | PUT 요청을 정의합니다. |
HTTP 메소드 | delete() | DELETE 요청을 정의합니다. |
HTTP 메소드 | head() | HEAD 요청을 정의합니다. |
HTTP 메소드 | options() | OPTIONS 요청을 정의합니다. |
HTTP 메소드 | patch() | PATCH 요청을 정의합니다. |
HTTP 메소드 | method() | 지정된 HTTP 메소드로 요청을 정의합니다. |
URI | uri() | 요청할 엔드포인트 URL을 지정합니다. |
요청 바디 | body() | 요청 바디에 담을 데이터를 설정합니다. |
요청 바디 | bodyValue() | 요청 바디에 담을 데이터를 설정합니다. |
응답 결과 | bodyToMono() | 응답 결과를 Mono로 래핑합니다. |
응답 결과 | bodyToFlux() | 응답 결과를 Flux로 래핑합니다. |
응답 결과 | retrieve() | 요청 결과를 가져오는 ResponseSpec 인스턴스를 반환합니다. |
응답 결과 | exchange() | 요청 결과를 ClientResponse 인스턴스로 가져옵니다. |
응답 결과 | exchangeToMono() | 요청 결과를 Mono로 래핑합니다. |
응답 결과 | exchangeToFlux() | 요청 결과를 Flux로 래핑합니다. |
응답 결과 | exchangeToBodilessEntity() | 응답 결과를 ResponseEntity<Void>로 래핑합니다. |
응답 결과 | toEntity() | 응답 결과를 ResponseEntity로 래핑합니다. |
요청 헤더 | headers() | 요청 헤더를 설정합니다. |
요청 헤더 | header() | 요청 헤더를 설정합니다. |
요청 헤더 | cookies() | 쿠키를 설정합니다. |
요청 헤더 | cookie() | 쿠키를 설정합니다. |
요청 헤더 | accept() | 요청 헤더에서 Accept 헤더를 설정합니다. |
요청 헤더 | acceptCharset() | 요청 헤더에서 Accept-Charset 헤더를 설정합니다. |
요청 헤더 | acceptEncoding() | 요청 헤더에서 Accept-Encoding 헤더를 설정합니다. |
요청 헤더 | acceptLanguage() | 요청 헤더에서 Accept-Language 헤더를 설정합니다. |
요청 헤더 | contentType() | 요청 헤더에서 Content-Type 헤더를 설정합니다. |
요청 헤더 | contentLength() | 요청 헤더에서 Content-Length 헤더를 설정합니다. |
요청 헤더 | ifModifiedSince() | 요청 헤더에서 If-Modified-Since 헤더를 설정합니다. |
요청 헤더 | ifNoneMatch() | 요청 헤더에서 If-None-Match 헤더를 설정합니다. |
예외 처리 | onStatus() | 지정된 HTTP 상태 코드에 대한 처리를 정의합니다. |
예외 처리 | onErrorResume() | 지정된 예외가 발생했을 때 대체 동작을 정의합니다. |
예외 처리 | onErrorMap() | 지정된 예외가 발생했을 때 예외를 변환하고, 변환된 예외를 다시 발생시킵니다. |
예외 처리 | onErrorReturn() | 지정된 예외가 발생했을 때 반환할 값 또는 인스턴스를 정의합니다. |
재시도 및 타임아웃 | retries() | 요청이 실패했을 때 재시도 횟수 및 재시도 조건을 설정합니다. |
재시도 및 타임아웃 | retryWhen() | 재시도 횟수 및 재시도 조건을 정의합니다. |
재시도 및 타임아웃 | timeout() | 요청이 일정 시간 이상 걸린 경우 타임아웃 처리 방법을 설정합니다. |
Publisher 동작 | doOnSuccess() | Publisher가 값을 방출할 때마다 지정된 동작을 수행합니다. |
Publisher 동작 | doOnError() | Publisher가 에러를 방출할 때마다 지정된 동작을 수행합니다. |
Publisher 동작 | doOnTerminate() | Publisher가 종료될 때마다 지정된 동작을 수행합니다. |
Publisher 동작 | doOnSubscribe() | Publisher가 구독될 때마다 지정된 동작을 수행합니다. |
Publisher 동작 | doOnCancel() | Publisher가 취소될 때마다 지정된 동작을 수행합니다. |
동기 메소드 | block() | Publisher가 갖는 값을 동기적으로 반환하는 메소드입니다. 해당 Publisher가 값을 방출할 때까지 대기한 후 값을 반환합니다. |
4) WebClient 이용방법 -1 : WebClient.create() 이용방법
1. WebClient 인스턴스 생성
💡 Webflux의 WebClient를 사용하기 위해 다른 웹 서비스에 HTTP 요청을 보내기 위해 공통 URI를 입력하고 인스턴스를 생성합니다.
WebClient client = WebClient.create("<http://localhost:8000>");
// or
/**
* 공용으로 사용하는 WebClient
*
* @return Webclient Instance
*/
@Bean
public WebClient webClient() {
return WebClient.create("<http://localhost:8080>");
}
분류 | 메서드 | 설명 |
인스턴스 생성 | create() | 지정된 URL을 기반으로 WebClient 인스턴스를 만듭니다. |
2. WebClient HTTP Method, URI, 전송 데이터를 지정합니다.
webClient()
.post()
.uri("/api/v1/code/code")
.bodyValue(codeDto)
.retrieve();
분류 | 메서드 | 설명 |
HTTP 메소드 | get() | GET 요청을 정의합니다. |
HTTP 메소드 | post() | POST 요청을 정의합니다. |
HTTP 메소드 | put() | PUT 요청을 정의합니다. |
HTTP 메소드 | delete() | DELETE 요청을 정의합니다. |
HTTP 메소드 | head() | HEAD 요청을 정의합니다. |
HTTP 메소드 | options() | OPTIONS 요청을 정의합니다. |
HTTP 메소드 | patch() | PATCH 요청을 정의합니다. |
HTTP 메소드 | method() | 지정된 HTTP 메소드로 요청을 정의합니다. |
URI | uri() | 요청할 엔드포인트 URL을 지정합니다. |
요청 바디 | body() | 요청 바디에 담을 데이터를 설정합니다. |
요청 바디 | bodyValue() | 요청 바디에 담을 데이터를 설정합니다. |
응답 결과 | bodyToMono() | 응답 결과를 Mono로 래핑합니다. |
응답 결과 | bodyToFlux() | 응답 결과를 Flux로 래핑합니다. |
응답 결과 | retrieve() | 요청 결과를 가져오는 ResponseSpec 인스턴스를 반환합니다. |
응답 결과 | exchange() | 요청 결과를 ClientResponse 인스턴스로 가져옵니다. |
응답 결과 | exchangeToMono() | 요청 결과를 Mono로 래핑합니다. |
응답 결과 | exchangeToFlux() | 요청 결과를 Flux로 래핑합니다. |
응답 결과 | exchangeToBodilessEntity() | 응답 결과를 ResponseEntity<Void>로 래핑합니다. |
응답 결과 | toEntity() | 응답 결과를 ResponseEntity로 래핑합니다. |
3. WebClient의 응답 결과를 지정합니다.
webClient()
.post()
.uri("/api/v1/code/code")
.bodyValue(codeDto)
.retrieve()
.toEntity(CodeDto.class)
.block();
분류 | 메서드 | 설명 |
응답 결과 | bodyToMono() | 응답 결과를 Mono로 래핑합니다. |
응답 결과 | bodyToFlux() | 응답 결과를 Flux로 래핑합니다. |
응답 결과 | retrieve() | 요청 결과를 가져오는 ResponseSpec 인스턴스를 반환합니다. |
응답 결과 | exchange() | 요청 결과를 ClientResponse 인스턴스로 가져옵니다. |
응답 결과 | exchangeToMono() | 요청 결과를 Mono로 래핑합니다. |
응답 결과 | exchangeToFlux() | 요청 결과를 Flux로 래핑합니다. |
응답 결과 | exchangeToBodilessEntity() | 응답 결과를 ResponseEntity<Void>로 래핑합니다. |
응답 결과 | toEntity() | 응답 결과를 ResponseEntity로 래핑합니다. |
동기 메소드 | block() | Publisher가 갖는 값을 동기적으로 반환하는 메소드입니다. 해당 Publisher가 값을 방출할 때까지 대기한 후 값을 반환합니다. |
4. 종합 결과
1. WebClient 인스턴스를 최초 생성합니다.
2. WebClient 인스턴스를 기반으로 HTTP Method의 POST 방식을 이용합니다.
3. 최종 전달하려는 http://localhost:8000/api/v1/code/code로 POST의 Body 데이터를 담아 전송합니다.
4. 전송이 되면 반환값으로 CodeDto.class 객체의 값으로 반환받습니다.
WebClient client = WebClient.create("<http://localhost:8000>");
ResponseEntity<ApiResponseWrapper> result = client
.post()
.uri("/api/v1/code/code")
.bodyValue(codeDto)
.retrieve()
.toEntity(CodeDto.class)
.block();
Map<String, Object> resultMap = (Map<String, Object>) result.getBody().getResult();
log.info("resultJsonObj :: " + resultMap);
5) WebClient 이용방법 -2 : Builder()를 이용한 방법
💡 WebClient에서 Builder()를 사용하면 WebClient 인스턴스를 더욱 유연하게 초기화할 수 있습니다.
1. WebClient : Builder 메서드
분류 | 메서드 | 설명 |
일반적인 설정 | baseUrl(String baseUrl) | WebClient 인스턴스의 기본 URL을 설정합니다. |
일반적인 설정 | codecs(Consumer<CodecConfigurer> configurer) | 요청 및 응답 바디에 사용할 코덱을 구성합니다. |
일반적인 설정 | defaultCookie(String name, String value) | 요청에 대한 기본 쿠키 값을 설정합니다. |
일반적인 설정 | defaultHeader(String headerName, String... headerValues) | 요청에 대한 기본 헤더 값을 설정합니다. |
일반적인 설정 | defaultUriVariables(Map<String, ?> defaultUriVariables) | 확장된 URI 템플릿에서 사용할 기본 URI 변수를 설정합니다. |
일반적인 설정 | defaultUriVariables(Consumer<UriBuilderFactory> builderConsumer) | UriBuilderFactory를 사용하여 확장된 URI 템플릿에서 사용할 기본 URI 변수를 설정합니다. |
일반적인 설정 | exchangeStrategies(ExchangeStrategies strategies) | 요청 및 응답 바디에 사용할 교환 전략을 구성합니다. |
일반적인 설정 | filter(ExchangeFilterFunction filterFunction) | ExchangeFilterFunction을 WebClient 인스턴스에 추가합니다. |
일반적인 설정 | filters(Consumer<List<ExchangeFilterFunction>> filters) | 여러 ExchangeFilterFunction을 WebClient 인스턴스에 추가합니다. |
요청 설정 | accept(MediaType... acceptableMediaTypes) | 응답에 대한 허용 가능한 미디어 유형을 설정합니다. |
요청 설정 | attribute(String name, Object value) | 요청에 속성을 추가합니다. |
요청 설정 | body(BodyInserter<?, ? super ClientHttpRequest> inserter) | 요청 바디를 설정합니다. |
요청 설정 | body(BodyInserter<?, ? super ClientHttpRequest> inserter, Class<?> elementClass) | 요청 바디를 설정하고 요소의 클래스를 지정합니다. |
요청 설정 | cookies(Consumer<MultiValueMap<String, HttpCookie>> cookiesConsumer) | 요청에 쿠키를 추가합니다. |
요청 설정 | header(String headerName, String... headerValues) | 요청에 헤더를 추가합니다. |
요청 설정 | headers(Consumer<HttpHeaders> headersConsumer) | 요청에 여러 헤더를 추가합니다. |
요청 설정 | ifModifiedSince(ZonedDateTime ifModifiedSince) | 요청의 "If-Modified-Since" 헤더를 설정합니다. |
요청 설정 | ifNoneMatch(String ifNoneMatch) | 요청의 "If-None-Match" 헤더를 설정합니다. |
요청 설정 | uri(URI uri) | 요청 URI를 설정합니다. |
요청 설정 | uri(String uri, Object... uriVariables) | 포맷된 문자열과 URI 변수를 사용하여 요청 URI를 설정합니다. |
HTTP 메소드 | get() | GET 요청을 설정합니다. |
HTTP 메소드 | post() | POST 요청을 설정합니다. |
HTTP 메소드 | put() | PUT 요청을 설정합니다. |
HTTP 메소드 | delete() | DELETE 요청을 설정합니다. |
HTTP 메소드 | head() | HEAD 요청을 설정합니다. |
HTTP 메소드 | options() | OPTIONS 요청을 설정합니다. |
HTTP 메소드 | patch() | PATCH 요청을 설정합니다. |
HTTP 메소드 | method() | 지정된 HTTP 메소드로 요청을 설정합니다. |
2. 일반적인 방식 vs Builder를 이용한 방식
💡 아래의 두 개의 코드가 있습니다.
- 하나는 일반적으로 사용하는 WebClient.create() 방식과 다른 하나는 Builder()를 이용한 방식입니다.
- 빌더를 사용하면 defaultHeader 메서드를 통해 공통적으로 사용되는 헤더를 추가할 수 있습니다. 또한 header 메서드를 통해 요청에 특정 헤더를 추가할 수 있습니다.
2.1. 일반적인 방식 : WebClient.create()
WebClient client = WebClient.create("<http://localhost:8000>");
ResponseEntity<ApiResponseWrapper> result = client
.post()
.uri("/api/v1/code/code")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.AUTHORIZATION, "Bearer my-token")
.bodyValue(codeDto)
.retrieve()
.toEntity(CodeDto.class)
.block();
Map<String, Object> resultMap = (Map<String, Object>) result.getBody().getResult();
log.info("resultJsonObj :: " + resultMap);
2.2. Builder를 이용한 방식
WebClient client = WebClient.builder()
.baseUrl("<http://localhost:8000>")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer my-token")
.build();
ResponseEntity<ApiResponseWrapper> result = client
.post()
.uri("/api/v1/code/code")
.bodyValue(codeDto)
.retrieve()
.toEntity(CodeDto.class)
.block();
Map<String, Object> resultMap = (Map<String, Object>) result.getBody().getResult();
log.info("resultJsonObj :: " + resultMap);
오늘도 감사합니다. 😀
반응형
'Java > Spring Boot' 카테고리의 다른 글
[Java] Spring Boot OAuth2 Client + Spring Security + JWT + Kakao 구성하기 -1 : 초기 환경 (0) | 2023.08.19 |
---|---|
[Java] Spring Boot Web 활용 : RestTemplate 이해하기 (0) | 2023.08.14 |
[Java] Spring Boot Webflux 이해하기 -1 : 흐름 및 주요 특징 이해 (3) | 2023.08.09 |
[Java] Spring Boot OAuth 2 Client 이해하기 -2: Security 없이 카카오 로그인 구성 (8) | 2023.07.16 |
[Java] Spring Boot OAuth 2 Client 이해하기 -1 : 정의, 흐름, 인증방식 종류 (0) | 2023.07.09 |