๊ฒฐ์ ์น์ธ POST ์์ฒญ ์ RestTemplate ๋ก๊ทธ์์ ๋ฐ์ํ ์ค๋ฅ
๊ฒฐ์ ๊ธฐ๋ฅ์ ๊ฐ๋ฐํ๋ฉด์, ๊ฒฐ์ ์น์ธ ์์ฒญ์ ๋ณด๋ด๋ RestTemplate์ ํ์์์์ ์ ์ฉํ๊ณ , ์์ฒญ๊ณผ ์๋ต์ ๋ํ ๋ก๊น ์ ์ถ๊ฐํ๊ณ ์ ํ์ต๋๋ค.
Baeldung์ "RestTemplage Logging" ๊ณผ ๋ง๋๋ ๊ฐ๋ฐ์๋์ ๋ก๊น ์ ์ฐธ๊ณ ํด์ RestTemplate ์ ๋ก๊ทธ๋ฅผ ์ถ๊ฐ ํ์ง๋ง ์๋ต์ ๋ฐ์ ๋, FileNotFoundException ์ด ๋ฐ์ํ๋ฉฐ ์๋์ ๊ฐ์ ์คํ ํธ๋ ์ด์ค๋ฅผ ํ์ธํ์ต๋๋ค.
java.io.FileNotFoundException: https://api.tosspayments.com/v1/payments/confirm
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1993)
java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:224)
org.springframework.http.client.SimpleClientHttpResponse.getBody(SimpleClientHttpResponse.java:89)
org.springframework.http.client.BufferingClientHttpResponseWrapper.getBody(BufferingClientHttpResponseWrapper.java:66)
airdnb.be.config.RestTemplateConfig$LoggingInterceptor.printResponse(RestTemplateConfig.java:65)
airdnb.be.config.RestTemplateConfig$LoggingInterceptor.intercept(RestTemplateConfig.java:51)
์คํ ํธ๋ ์ด์ค์์ HttpURLConnection.java:1993์ ๋ณด๋ฉด, ์๋ต ์ฝ๋๊ฐ 404 ๋๋ 410์ผ ๋ FileNotFoundException์ด ๋ฐ์ํฉ๋๋ค.
if (respCode >= 400) {
if (respCode == 404 || respCode == 410) {
throw new FileNotFoundException(url.toString());
} else {
throw new java.io.IOException(
"Server returned HTTP" + " response code: " + respCode + " for URL: " + url.toString());
}
}
ClientHttpRequestInterceptor์ ResponseErrorHandler์ ์์
๋ก๊น ์ ์ํด ClientHttpRequestInterceptor๋ฅผ ์ฌ์ฉํ๋ฉด, 404์ 410 ์ํ ์ฝ๋๋ฅผ ์ฒ๋ฆฌํ๋ ResponseErrorHandler๋ฅผ ์ค๋ฒ๋ผ์ด๋ํด๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
์ด์ ๋ ClientHttpRequestInterceptor๊ฐ ๋จผ์ ํธ์ถ๋ ํ, ResponseErrorHandler๊ฐ ์๋ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
๋ฐ๋ผ์, Logging ํ๊ธฐ ์ํด ์ถ๊ฐํ Interceptor ์์ ๋ฐ์ํ IOException ๋๋ฌธ์ ResponseErrorHandler ๊ฐ ํธ์ถ ๋์ง ์์ต๋๋ค.
์ถ์ฒ : https://terasolunaorg.github.io/guideline/5.1.1.RELEASE/en/ArchitectureInDetail/RestClient.html
ํด๊ฒฐ ๋ฐฉ๋ฒ
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด, ์๋ต ๋ก๊น ์ ์ํด getBody()๋ฅผ ํธ์ถํ๊ธฐ ์ ์ getStatusCode()๋ฅผ ํธ์ถํด์ผ ํฉ๋๋ค.
์ฆ, ClientHttpResponse ์ getStatusCode()๋ฅผ ๋จผ์ ํธ์ถํ ํ getBody()๋ฅผ ํธ์ถํฉ๋๋ค.
...?
@Slf4j
class LoggingInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) {
...
printResponse(UUID 6์๋ฆฌ, ClientHttpResponse);
...
}
private void printRequest(String sessionNumber, HttpRequest req, byte[] body) {
...
}
private void printResponse(String sessionNumber, ClientHttpResponse response) throws IOException {
>>> HttpStatusCode code = response.getStatusCode();
>>> String body = new BufferedReader(
new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
log.info("[{}] Status:{}, Headers:{}, Body:{}",
sessionNumber, code, response.getHeaders(), body);
}
}
getStatusCode()์ getBody() ํธ์ถ ์์์ ์๋ฏธ
- RestTemplate ResponseErrorHandler cannot handle 404 FileNotFound
* getBody() ๋ post, put ์์ฒญ ์ ๋ฐ์ํ๋ 400,500๋ฒ๋ ์๋ต์์๋ง ์๋ฌ๋ฅผ ๋ฐ์ํ๋ค๊ณ ํฉ๋๋ค. ์ด์ ๋ํ ์ด์ ์ญ์ ์์ 2๊ฐ์ ๋งํฌ ์ค 2๋ฒ์งธ ๋ค์ด๊ฐ๋ฉด ํ์ธ์ด ๊ฐ๋ฅํฉ๋๋ค.
ํด๋น ๋งํฌ์์๋ getStatusCode() ๋ฅผ ํธ์ถํ ์ดํ์ getBody() ๋ฅผ ํธ์ถํ๋ฉด errorStream ์ด null ์ด ์๋๋ผ๊ณ ์๊ธฐํฉ๋๋ค.
Why does calling response.getBody() not throw an IOException after you called response.getStatusCode()?
It is because getStatusCode calls getInputStream internally.
Thus, errorStream will be not null when getBody is called.
* errorStream ์ getBody() ๊ตฌํ๋ถ์ ์์ต๋๋ค.
@Override
public InputStream getBody() throws IOException {
InputStream errorStream = this.connection.getErrorStream();
this.responseStream = (errorStream != null ? errorStream : this.connection.getInputStream());
return this.responseStream;
}
Spring์์๋ getStatusCode()๊ฐ ๋ด๋ถ์ ์ผ๋ก getInputStream()์ ํธ์ถํ์ฌ, ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ errorStream์ด ์ค์ ๋๋๋ก ์ค๊ณ๋์ด ์์ต๋๋ค.
์ค๋ฅ ์๋ต(4xx, 5xx)์ด ๋ฐ์ํ๋ฉด HttpURLConnection์์ getInputStream()์ด IOException์ ๋ฐ์์ํค๋ฉด์ errorStream์ ์ค์ ํฉ๋๋ค.
์ด๋ก ์ธํด ์ดํ getBody()๋ฅผ ํธ์ถํ ๋๋ errorStream์ด null์ด ์๋๊ฒ ๋์ด, ์๋ฌ ์๋ต ๋ณธ๋ฌธ์ ์ฝ์ ์ ์์ต๋๋ค.
๊ทธ๋์, getBody() ๋ฅผ ๋จผ์ ํธ์ถํ๋ฉด getInputStream ์์ IOException ์ธ FileNotFoundException ์ด ๋ฐ์ํ๋ ๊ฒ์ด์์ต๋๋ค.
---
์ค๋ฅ๋ฅผ ํด๊ฒฐํ๋ ๊ณผ์ ์์ ๊ทธ ์ด์์ ๋ฐฐ์์ ์ป๊ฒ ๋จ์ ๋ค์ ํ๋ฒ ๋๋๋๋ค.
์ด์์ผ๋ก RestTemplate์ ๋ก๊ทธ๋ฅผ ์ถ๊ฐํ๋ฉด์ ๋ง์ฃผํ๋ ์ค๋ฅ์ ๋ํ ๊ธ์ ๋ง์น๊ฒ ์ต๋๋ค.