[ChatGPT] Java 코드 리뷰 (1) - http 통신 메서드 개선

톰톰·2023년 2월 4일
1

Open AI사가 개발한 ChatGPT로 세간이 떠들썩하다. 무료로 사용할 수 있다는 소식을 듣고 얼마나 똑똑한지 시험해보기로 했다. 무엇을 시도해보면 좋을까 고민하다가 code review를 요청해보기로 했다.

ChatGPT를 직접 사용해보시려면 여기를 클릭하세요.

1. 질문

[질문] Can you improve my code below?

httpclient 라이브러리를 사용하여 API 요청을 보내고 응답을 받아오는 http 통신 메서드이다.

2. 답변 분석

핵심 리뷰 내용을 하나씩 뜯어보자.

[답변1] try statement 사용

(me) httpClient 인스턴스 생성 및 close 작업을 분리

CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
try {
   ...
finally {
   if (null != closeableHttpClient) {
      try {
	     closeableHttpClient.close();
	  } catch (Exception e) {
	     e.printStackTrace();
      }
 }

(chatGPT) try statement 내에서 httpClient 인스턴스 생성 및 close 작업 동시 수행

try (CloseableHttpClient closeableHttpClient = httpClientBuilder.build(){
....

try statementclose 메소드를 자동 실행해주는 명령어이다. close를 필요로 하는 Statement class의 인스턴스나 Stream 타입의 클래스 들이 작업 완료 후에 수행해야 하는 close() 메소드를 자동을 수행해주기 때문에 굳이 나눠 작성할 필요가 없다.
내 코드는 try문 시작 전 CloseableHttpClient 인스턴스를 생성하고 finally문에서 조건 확인 후 다시 close를 해주었기 때문에 try, if, finally이 중첩되어 가독성이 나빴다. 개선된 코드에선 동일 로직을 1줄로 처리할 수 있었다.

[답변2] HttpPost 인스턴스 생성 위치 변경

(me) try 문 전에 HttpPost 인스턴스 생성

HttpPost httpPost = new HttpPost(requestUrl);
try {
...

(chatGPT) try문 후에 HttpPost 인스턴스 생성

try (CloseableHttpClient closeableHttpClient = httpClientBuilder.build(){
   HttpPost httpPost = new HttpPost(requestUrl);

HttpPost는 post 방식으로 전송하기 위해 url, config, entity 등의 정보를 담는 역할을 수행하며, 실제 post 통신 작업은 CloseableHttpClient의 인스턴스를 통해 이루어진다. 따라서 CloseableHttpClient 인스턴스 생성이 보장되기 전에 HttpPost 인스턴스를 생성할 필요가 없다.
개선된 코드에선 CloseableHttpClient 인스턴스가 정상적으로 생성된 상황에서 HttpPost 인스턴스를 생성하므로, 행여 CloseableHttpClient 인스턴스가 정상적으로 build되지 않은 상황에서 메모리가 낭비될 가능성을 줄였다.

[답변3] Response의 statusCode를 토대로 추가적인 Exception 처리

(me)

HttpResponse httpResponse = closeableHttpClient.execute(httpPost);
String resData = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
return objectMapper.readValue(resData, responseType);

(chatGPT)

HttpResponse httpResponse = closeableHttpClient.execute(httpPost);
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode >= 200 && statusCode < 300) {
   String resData = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
   return objectMapper.readValue(resData, responseType);
} else {
   logger.error("Failed to call post, status code: " + statusCode);
   throw new ServiceException("API_RESPONSE_ERROR", "API call failed. Please contact the administrator.", null);
}

HttpResponseexecute()로 수행한 요청에 대한 응답 결과를 담는 역할을 수행한다. 이 때 getEntity()로 실제 data를 읽기 전 getStatusCode()로 송신한 요청에 대한 statusCode를 먼저 확인하는 것이 올바른 순서다. chatGPT가 개선한 코드는 성공인 경우(200~299)에만 getEntity()를 수행하도록 하였다.
사실 개발 당시 statusCode를 분리할 생각을 못했던 것은 아니었다. 하지만 API를 제공하는 업체가 어떠한 경우에도 200 code를 전달하는 식으로 개발을 해놓은 상태라 굳이 안해도 되겠다고 판단하였다. 하지만 정석은 이러하니 statusCode에 따라 Exception 처리를 하는 습관을 들이는 것이 좋다는 생각이 든다.

(참고) HTTP response status codes

의미code 범위
Informational responses100 - 199
Successful reponses200 - 299
Redirection messages300 - 399
Client error responses400 - 499
Server error responses500 - 599

3. 전체 답변 내용

[답변] Here's an improved version

public <E, T> T callPost(String protocol, String serverHost, String blockChainServerDetailUrl, E requestParam, Class<T> responseType) {
    String blockChainServerBaseUrl = String.format("%s://%s", protocol, serverHost);
    String requestUrl = blockChainServerBaseUrl + blockChainServerDetailUrl;

    HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
    RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(1 * 10000).setConnectTimeout(1 * 10000).setConnectionRequestTimeout(1 * 10000).build();

    try (CloseableHttpClient closeableHttpClient = httpClientBuilder.build()) {
        HttpPost httpPost = new HttpPost(requestUrl);
        httpPost.setConfig(requestConfig);

        String msg = objectMapper.writeValueAsString(requestParam);
        logger.info(msg);

        StringEntity entity = new StringEntity(msg, "UTF-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);

        HttpResponse httpResponse = closeableHttpClient.execute(httpPost);
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        if (statusCode >= 200 && statusCode < 300) {
            String resData = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
            return objectMapper.readValue(resData, responseType);
        } else {
            logger.error("Failed to call post, status code: " + statusCode);
            throw new ServiceException("API_RESPONSE_ERROR", "API call failed. Please contact the administrator.", null);
        }
    } catch (Exception e) {
        logger.error("Failed to call post, error: " + e.getMessage());
        throw new ServiceException("API_RESPONSE_ERROR", "API call failed. Please contact the administrator.", e);
    }
}
  • The code has been encapsulated in a try-with-resources block, which will ensure that the CloseableHttpClient is automatically closed, even in case of an exception.
  • The response code is checked to see if it is between 200 and 299, indicating a successful response. If not, the code will log an error and throw a ServiceException.
  • The ServiceException now includes the original exception in its constructor, so that additional debugging information can be obtained if needed.

4. 마무리

우리 회사는 코드 리뷰 문화가 정착되지 않아서 주니어가 리뷰를 받기가 어려운데, chatGPT를 이용하면 손쉽게 유의미한 수준의 리뷰를 얻을 수 있을 것 같아 기쁘다! 리뷰 내용을 참고해서 차주 리팩토링을 해볼 생각이다.
이 정도 수준이라면 다음엔 좀 더 복잡한 코드로 리뷰를 요청해도 괜찮을 것 같다. 이 글을 보신 분들도 한 번 사용해보시길~!
ChatGPT를 직접 사용해보시려면 여기를 클릭하세요.


References

0개의 댓글