๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
TIL

WebClient์™€ RestTemplate ๊ทธ๋ฆฌ๊ณ  RestClient

by bamDal 2025. 2. 21.

 

์™ธ๋ถ€API์™€ ์—ฐ๋™ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” RestTemplate๊ณผ WebClient ๊ฐ„์— ์–ด๋–ค ์ฐจ์ด๊ฐ€ ์žˆ์„์ง€ ์ฐพ์•„๋ณด๊ณ , ์–ด๋–ค ๊ธฐ์ˆ ์„ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ• ์ง€ ๊ณ ๋ฏผํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

 

Webflux ?

๋น„๋™๊ธฐ(Async), ๋…ผ๋ธ”๋กœํ‚น(Non-blocking) ๋ฐฉ์‹์˜ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ง€์›ํ•˜๋Š” ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.

 

WebClient

Spring WebFlux์—์„œ ์ œ๊ณตํ•˜๋Š” HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค.

 

RestTemplate ?

๋ธ”๋กœํ‚น(Blocking) ๊ธฐ๋ฐ˜์˜ ๋™๊ธฐ ๋ฐฉ์‹์˜ Spring MVC์˜ HTTP ํด๋ผ์ด์–ธํŠธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹ค ์ฐจ์žˆ๋Š” ๊ฒฝ์šฐ Queue์—์„œ ๋Œ€๊ธฐ๋ฅผ ํ•˜๊ฒŒ๋˜์–ด ์š”์ฒญ์ด ๋งŽ์•„์ง€๊ฒŒ ๋˜๋ฉด ๋ณ‘๋ชฉ ํ˜„์ƒ์ด ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์„œ๋น„์Šค ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์—ฐ๊ฒฐ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

WebClient VS RestTemplate ๋น„๊ตํ•ด๋ณด๊ธฐ

ํ•˜๋‚˜์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋‚ด์—์„œ ๋‹จ์ผ API ์š”์ฒญ๋งŒ ์ด๋ฃจ์–ด์ง€๋Š” ์ƒํ™ฉ (์š”์ฒญ ์ˆ˜๊ฐ€ ์ ์€ ์ƒํ™ฉ)์—์„œ๋Š” RestTemplate๊ณผ WebClient์˜ ์ฐจ์ด๊ฐ€ ๋ฏธ๋ฏธํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋งŽ์€ ์ˆ˜์˜ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€๋Š” ์•Š์ง€๋งŒ ์•„๋ž˜์™€ ๊ฐ™์€ ์ด์œ ๋กœ ๊ฒฐ๋ก ์„ ๋‚ด๋ฆฌ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • WebClient ์ฝ”๋“œ๋Š” ๊ฐ€๋…์„ฑ์ด ์ข‹์Šต๋‹ˆ๋‹ค.
  • RestTemplate์€ ํ˜„์žฌ ์œ ์ง€๊ด€๋ฆฌ ๋ชจ๋“œ์— ์žˆ์œผ๋ฉฐ Spring์—์„œ WebClient๋ฅผ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„œ๋น„์Šค ํ™•์žฅ์„ฑ๊ณผ MSA ์ „ํ™˜์„ ๊ณ ๋ คํ•˜์—ฌ ์„ค๊ณ„ํ•˜๊ธฐ๋กœ ํ–ˆ์œผ๋ฏ€๋กœ ์ถ”ํ›„์— ๋Œ€๊ทœ๋ชจ ์š”์ฒญ์— ์‚ฌ์šฉํ•  ๊ฒƒ์„ ๋Œ€๋น„ํ•˜์—ฌ webClient ๋ฅผ ์ฑ„ํƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • ์ถ”ํ›„ reactive web ์œผ๋กœ ์ „ํ™˜๋œ๋‹ค๋ฉด, ํ•ด๋‹น ํด๋ผ์ด์–ธํŠธ์—์„œ ๋น„๋™๊ธฐ, ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹์œผ๋กœ ๊ฐ„๋‹จํžˆ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

RestTemplate ์˜ˆ์‹œ ์ฝ”๋“œ

RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.put(HttpHeaders.ACCEPT, Collections.singletonList(MediaType.APPLICATION_JSON_VALUE));
HttpEntity<String> entity = new HttpEntity<>(headers);

String result = restTemplate.exchange("https://example.com", HttpMethod.GET, entity, String.class).getBody();

*์ถœ์ฒ˜: https://poalim.tistory.com/59*

 

WebClient ์ฝ”๋“œ

GeminiResponseDto aiAnswer = webClient.post()
    .uri(geminiApiUrl + geminiApiKey)
    .header("Content-Type", "application/json")
    .bodyValue(requestBody)
    .retrieve()
    .bodyToMono(GeminiResponseDto.class)
    .block();

 

RestClient์˜ ๋“ฑ์žฅ

WebClient๋Š” ์•ž์„œ ๋งํ•œ ์žฅ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, Spring MVC ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—๋Š” ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ๋Š” .block() ์œผ๋กœ ๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, WebFlux ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ spring-webflux ์˜์กด์„ฑ ์ถ”๊ฐ€๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

RestClient

RestClient๋Š” ๋™๊ธฐ์‹ HTTP Client์ž…๋‹ˆ๋‹ค.

webflux์— ๋น„ํ•ด ๋’ค๋Šฆ๊ฒŒ ๋ฐœ๊ฒฌํ•˜์—ฌ ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

webflux๋ณด๋‹ค Spring MVC ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. webFlux์™€ ๋‹ฌ๋ฆฌ ์˜์กด์„ฑ์„ ๋„ฃ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

 

RestClient ์ฝ”๋“œ

ResponseEntity<GeminiResponseDto> aiAnswer = restClient.post()
                .uri(geminiApiUrl + geminiApiKey)
                .contentType(APPLICATION_JSON)
                .body(requestBody)
                .retrieve()
                .toEntity(GeminiResponseDto.class);

 

์ฐธ๊ณ : https://docs.spring.io/spring-framework/reference/integration/rest-clients.html