๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
[Error Handling]

๐Ÿ˜ก Spring WebClient๋กœ ํ†ต์‹  ์‹œ WebClientResponseException: 204 No Content from POST ์—๋Ÿฌ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

by ํŒกํŽ‘ํ 2024. 10. 25.
728x90

๐Ÿšจ Error :

 

๊ฐœ๋ฐœ ๋„์ค‘์— ๋””์Šค์ฝ”๋“œ ์›นํ›…์œผ๋กœ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ๋™์ž‘์€ ์ •์ƒ์ ์œผ๋กœ ๋˜๋‚˜ ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ–ˆ๋‹ค.


๐Ÿค“ ์›์ธ :

 WebClient์— ๋Œ€ํ•ด ๊นŠ๊ฒŒ ์•Œ์ง€ ๋ชปํ•œ ์ƒํƒœ์—์„œ ์‚ฌ์šฉํ•˜์—ฌ ๊ฝค๋‚˜ ์• ๋ฅผ ๋จน์—ˆ๋‹ค. ๊ฒฐ๋ก ๋ถ€ํ„ฐ ์–˜๊ธฐํ•˜๋ฉด ๋””์Šค์ฝ”๋“œ ์›นํ›…์€ ์‘๋‹ต ์ƒํƒœ ์ฝ”๋“œ๋กœ 204 No Content๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , WebClient์—์„œ toEntity() ๋ฉ”์„œ๋“œ๋Š” 200 ๋Œ€ ์ƒํƒœ ์ฝ”๋“œ๋Š” ์—๋Ÿฌ๊ฐ€ ์•„๋‹ˆ๋ผ๊ณ  ํŒ๋‹จํ•˜์ง€๋งŒ 204 ์ƒํƒœ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ ์—๋Ÿฌ๋ผ๊ณ  ํŒ๋‹จํ•œ๋‹ค.

 

์™ธ๋ถ€ ์„œ๋ฒ„์™€์˜ ํ†ต์‹ ์—์„œ api ์‘๋‹ต ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์ž„์˜๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์ด์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ๋”ฐ๋กœ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

return webClient
                .post()
                .uri(discordSecurityWebHookUrl)
                .header("Content-Type", "application/json")
                .bodyValue(json)
                .retrieve()
                .toEntity(String.class);
  • ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋˜ ๊ธฐ์กด ์ฝ”๋“œ ๋‚ด์šฉ์ด๋‹ค.
  • toEntity(Class<T> responseType) ๋ฉ”์„œ๋“œ๋Š” WebClient์˜ ์‘๋‹ต์„ ResponseEntity<T>๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
  • ์ฆ‰, WebClient๋ฅผ ํ†ตํ•ด HTTP ์š”์ฒญ์„ ๋ณด๋‚ธ ํ›„ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ์˜ ์‘๋‹ต์„ ResponseEntity ๊ฐ์ฒด๋กœ ๊ฐ์‹ธ์„œ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ toEntity ๋ฉ”์„œ๋“œ๋Š” ์‘๋‹ต ๋ฐ”๋””๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฏ€๋กœ 204 ์ƒํƒœ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋˜๋ฉด ๋ฐ”๋””๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์ด ๊ฒฝ์šฐ WebClient๋Š” ์ด๋ฅผ ์—๋Ÿฌ๋กœ ๊ฐ„์ฃผํ•˜์—ฌ WebClientResponseException์„ ๋˜์ง„๋‹ค.

 

๐Ÿš’ ํ•ด๊ฒฐ :

return webClient
        .post()
        .uri(discordSecurityWebHookUrl)
        .header("Content-Type", "application/json")
        .bodyValue(json)
        .retrieve()
        .bodyToMono(String.class) 
        .map(ResponseEntity::ok) 
        .onErrorResume(WebClientResponseException.class, ex -> {
            // 204 No Content ์ƒํƒœ ์ฝ”๋“œ ์ฒ˜๋ฆฌ
            if (ex.getStatusCode() == HttpStatus.NO_CONTENT) {
                return Mono.empty(); // 204 ์ƒํƒœ์ฝ”๋“œ์ธ ๊ฒฝ์šฐ ๋นˆ Mono ๋ฐ˜ํ™˜
            }

            // ๊ธฐํƒ€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
            log.error("Error :", ex);
            return Mono.error(ex);
        });
.bodyToMono(String.class)
  • toEntity๋Š” ์ƒํƒœ์ฝ”๋“œ 204๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์œผ๋‹ˆ bodyToMono๋กœ ์‘๋‹ต ๋ฐ”๋””๋ฅผ String์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ Mono<String>์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•œ๋‹ค.
.map(ResponseEntity::ok)
  • Mono<String> ๊ฐ’์„ ์ƒํƒœ ์ฝ”๋“œ 200 OK๋กœ ์„ค์ •ํ•œ ResponseEntity<String>์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
  • toEntity๋ฅผ ๋‘ ๋‹จ๊ณ„๋กœ ๋‚˜๋ˆˆ ๊ฒƒ์ด๋‹ค.
.onErrorResume(WebClientResponseException.class, ex -> {...});
  • ์œ„์˜ ์ƒํƒœ๋กœ๋ผ๋ฉด ๋‹ค์‹œ 204 No Content๋กœ ์ธํ•ด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง์ด๋‹ค.
if (ex.getStatusCode() == HttpStatus.NO_CONTENT)
  • 204 No Content์ผ ๊ฒฝ์šฐ ๋นˆ ๋ชจ๋…ธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•˜๊ณ 
return Mono.error(ex);
  • ๋‹ค๋ฅธ 400-500 ๋Œ€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ•ด๋‹น ์—๋Ÿฌ๋ฅผ ์ถœ๋ ฅํ•˜๊ณ  ์—๋Ÿฌ ์ •๋ณด๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ฉด 204 No Content๋กœ ์‘๋‹ต์ด ์™€๋„ ๋นˆ Mono๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋˜์ง€ ์•Š๊ฒŒ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.


๐Ÿค” ์˜๋ฌธ์  :

์—†์Œ

 

 

 

์ฐธ๊ณ 

Chat GPT

728x90