빙응의 공부 블로그
[Spring]WebClient 본문
[Spring API]Spring WebFlux와 WebClient (tistory.com)
[Spring API]Spring WebFlux와 WebClient
📝WebFlux웹플럭스란 적은 쓰레드로 동시 처리를 제어하고 적은 하드웨어 리소스로 확장하기 위해 만들어진반응형 웹 프레임워크이다.🚩 탄생계기그렇다면 스프링 웹플럭스는 왜 만들어졌을까
quddnd.tistory.com
https://godekdls.github.io/Reactive%20Spring/webclient/
WebClient
스프링5 웹 리액티브 스택 WebClient 한글 번역
godekdls.github.io
스프링 MVC에서 WebClient를 사용해보자
📝WebClient 생성
기본적인 생성
- create()메서드를 사용하여 간단하게 생성이 가능하다
WebClient.create()
WebClient.create(String baseUrl)
🚩 WebClient 인스턴스 생성
- WebClient를 제대로 사용하기 위해서는 WebClient.Builder를 통해 WebClient 인스턴스를 생성해야 한다.
- 물론 우리는 스프링 MVC에서 사용하기에 편의성을 위해 빈으로 등록해서 사용할 것이다.
- WebClient를 설정하기 위해서는 여러 옵션을 고려하며 주로 baseUrl, timeout, connection pool, interceptor 등 의 설정이 필요하다.
1. baseUrl 설정
@Configuration
public class WebClientConfig {
@Value("${url}")
private String baseUrl;
@Bean
public WebClient webClient(){
return WebClient.builder()
.baseUrl(baseUrl)
.build();
}
}
- baseUrl은 공통저긍로 사용될 기본 URL을 의미한다.
- baseUri을 설정하려면 WebClient.Builder의 baseUrl 메서드를 이용하면 된다.
2. 타임아웃 설정
@Configuration
public class WebClientConfig {
@Value("${url}")
private String baseUrl;
@Bean
public WebClient webClient(){
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) //연걸 타임아웃 5000
.responseTimeout(Duration.ofMillis(5000)) //응답 타임아웃 5000
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS)) //읽기 타임아웃 5000
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS))); //쓰기 타임아웃 5000
return WebClient.builder()
.baseUrl(baseUrl)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}
}
- clientConnector 메소드를 이용해서 각 처리에 대한 타임아웃을 설정할 수 있다.
3. Connetion Poll 설정
- WebClient는 내부적으로 Reactor Netty라는 것을 사용한다. 이것은 네트워크 애플리케이션을 효율적으로 개발할 수 있도록 도와주는 것인데, 우리는 Netty가 다중 연결을 관리하기 위한 내부 Connection Pool을 설정해주는 것이다.
- 꼭 따로 설정안해도 되지만 필요에 다라 커스터마이징이 가능하다.
@Bean
public WebClient webClient() {
TcpClient tcpClient = TcpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(5))
.addHandlerLast(new WriteTimeoutHandler(5)));
return WebClient.builder()
.baseUrl(baseUrl)
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient)))
.build();
}
4. Interceptor 추가
- 요청과 응답에 대해 처리해야 할 내용이 있다면 Interceptor를 추가하여 처리할 수 있다,
@Bean
public WebClient webClient() {
ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
.build();
return WebClient.builder()
.baseUrl(baseUrl)
.exchangeStrategies(strategies)
.filter((request, next) -> {
// 요청 전처리
System.out.println("Request: " + request.method() + " " + request.url());
return next.exchange(request);
})
.filter((response, next) -> {
// 응답 후처리
System.out.println("Response status code: " + response.statusCode());
return next.exchange(response);
})
.build();
}
📝 WebClient 사용
- WebClient는 다양한 요청을 보내며 커스텀할 수 있다.
🚩 첫단. HTTP 요청 메서드
GET 요청 보내기
webClient.get()
.uri("https://example.com/api/resource")
.retrieve()
.bodyToMono(String.class);
POST 요청 보내기
webClient.post()
.uri("https://example.com/api/resource")
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(requestBody))
.retrieve()
.bodyToMono(String.class);
PUT 요청 보내기
webClient.put()
.uri("https://example.com/api/resource/{id}", resourceId)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(requestBody))
.retrieve()
.bodyToMono(String.class);
DELETE 요청 보내기
webClient.delete()
.uri("https://example.com/api/resource/{id}", resourceId)
.retrieve()
.bodyToMono(Void.class); // 요청이 몸체를 반환하지 않는 경우
🚩 요청 설정
Method | 설명 |
uri() | 요청할 URI를 설정한다. |
header() | 헤더를 추가한다. |
cookie(0 | 쿠키를 추가한다. |
bodyValue() | 요청 본문에 값을 설정한다. |
body(), syncBody() | 요청 본문을 설정한다. |
🚩 응답 처리
Method | 설명 |
retrieve() | 요청을 실행하고 응답을 받습니다. |
exchange() | 요청을 실행하고 응답을 ClientResponse 객체로 받습니다. |
Method | 설명 |
bodyToMono() | 응답을 Mono로 변환합니다. |
bodyToFlux() | 응답을 Flux로 변환합니다. |
🚩 오류 처리
Method | 설명 |
onStatus() | 특정 상태 코드에 대한 처리를 설정합니다. |
onErrorMap() | 오류를 다른 오류로 매핑합니다. |
onErrorResume() | 오류가 발생했을 때 대체 값을 제공합니다. |
🚩 기타
Method | 설명 |
uriBuilderFactory() | URI 빌더 팩토리를 설정합니다. |
exchangeStrategies() | 교호나 전략을 설정합니다. |
filter() | 필터를 적용합니다. |
📝 WebClient 실습
[Spring]Spring - RestTemplate (tistory.com)
[Spring]Spring - RestTemplate
API를 이용하여 원하는 데이터 정보를 가져와야 하는데 XML처리를 해야한다. 이것에 대해 알아보자 📝RestTemplate HTTP 통신을 위한 도구로 RESTful API 웹 서비스와의 상호작용을 쉽게 위부 도메인에
quddnd.tistory.com
전에 했었던 RestTemplate를 바꿔보자
@GetMapping("/jobs")
public ResponseEntity<ResponseData.Items> callApi(
@RequestParam(value = "local") String local
) {
log.info("API 호출: local={}", local);
try {
// API 호출 및 응답 받기
RestTemplate restTemplate = new RestTemplate();
String url = apiUrl + "?serviceKey=" + secretKey + "&pageNo=1" + "&numOfRows=200";
ResponseEntity<ResponseData> responseEntity = restTemplate.getForEntity(url, ResponseData.class);
// API 응답 확인 및 처리
ResponseData responseData = responseEntity.getBody();
if (responseData != null && responseData.getBody() != null && responseData.getBody().getItems() != null) {
List<ResponseData.Item> filteredItems = new ArrayList<>();
String targetPrefix = local.split(" ")[0]; // local 파라미터를 공백을 기준으로 분리하고 첫 번째 단어를 선택
for (ResponseData.Item item : responseData.getBody().getItems().getItem()) {
if (item.getCompAddr() != null && item.getCompAddr().startsWith(targetPrefix)) {
filteredItems.add(item);
}
}
responseData.getBody().getItems().setItem(filteredItems);
}
return new ResponseEntity<>(responseData.getBody().getItems(), HttpStatus.OK);
/* 오류처리 */
- RestTemplate로 요청을 받는다.
- local 파라미터로 받아온 것으로 필터링한다.
1. WebClientConfig 설정
- 재사용성 있는 WebClient를 위해 Config를 만들어주자
@Configuration
public class WebClientConfig {
@Value("${url}")
private String apiUrl;
@Bean
public WebClient webClient() {
HttpClient httpClient = HttpClient.create() //타임아웃 설정을 위한 객체
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.responseTimeout(Duration.ofMillis(5000))
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)));
return WebClient.builder()
.baseUrl(apiUrl) //기초 URI 설정
.clientConnector(new ReactorClientHttpConnector(httpClient)) //타임아웃 설정
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) //헤더 타입 JSON
.build();
}
}
2. 기능 똑같이 구현하기
@GetMapping("/jobs")
public Mono<ResponseEntity<List<ResponseData.Item>>> callApi(@RequestParam(value = "local") String local) {
log.info("API 호출: local={}", local);
try {
// API 호출 및 응답 받기
return webClient.get()
.uri(uriBuilder -> uriBuilder //파라미터를 통한 URI 구성
.queryParam("serviceKey", secretKey)
.queryParam("pageNo", 1)
.queryParam("numOfRows", 200)
.build())
.retrieve() // 응답을 받아온다.
.bodyToMono(ResponseData.class) //받은 응답을 직접만든 DTO로 변환
.map(responseData -> {
// API 응답 확인 및 처리
if (responseData != null && responseData.getBody() != null && responseData.getBody().getItems() != null) {
List<ResponseData.Item> filteredItems = responseData.getBody().getItems().getItem().stream()
.filter(item -> item.getCompAddr() != null && item.getCompAddr().startsWith(local.split(" ")[0]))
.collect(Collectors.toList());
responseData.getBody().getItems().setItem(filteredItems);
}
return ResponseEntity.ok(responseData.getBody().getItems().getItem());
});
} catch (Exception e) {
// 그 외의 예외 발생
log.error("API 호출 중 에러 발생", e);
return Mono.just(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
}
'Spring > 개인공부_실습' 카테고리의 다른 글
[Spring REST API]로그인 구현하기 - JWT + Security (1) | 2024.06.03 |
---|---|
[Spring REST API]로그인 구현하기 - 기본 (0) | 2024.06.03 |
[React + Spring]리액트와 스프링으로 API 받아서 처리하기 (0) | 2024.05.20 |
[Spring]Spring - RestTemplate (0) | 2024.05.19 |
[React + Spring]리액트와 스프링 - 연동 (0) | 2024.05.17 |