탱구탱구 개발자 일기

웹 개발을 하다 보면 내부건 외부 서비스 간에 HTTP 통신을 할 때가 매우 많다.

나도 실무에 있을 때 특히 API를 이용할 때 많이 썼고 무척이나 나를 괴롭혔던 기억이 있다.

 

이에 관해 구글링을 하면 대부분 URL Connection, HttpURLConnection, HttpClient 등 자바에 기본적으로 내장되어있는 API 라이브러리들이 있고 한글로 검색하면 저 3개를 가장 많이 이용하는 것 같다.

 

근데 개발하면서 구글링한 소스, 옛날부터 애플리케이션에 수정 없이 사용해왔던 코드들을 사용하다 보니 요즘 API에 사용하면 에러가 발생하고 좀 수정해야 하고 호출에 따른 조건이 있을 때에는 골치가 아팠다. 또한 그 내부 코드들을 이해하려다 보면 코드가 지저분해지고 또 Java Stream Class를 통해 처리해야 해서 개발 생산성도 떨어지는 문제가 있었다.

 

요즘 개발 트렌드와는 거리가 먼 느낌..?

 

[HttpUrlConnection 예제코드]

  • HttpStatus Code 분기 처리
  • Stream을 통해 Response Value를 받아와서 String으로 만듦
  • 이걸 다시 Json 형태로 만들어서 사용하던가 일일이 Key값을 통해 Parsing 해야 함
        URL url = new URL(baseUrl+path+"?"+parameter);
 
        HttpURLConnection con = (HttpURLConnection)url.openConnection();
        
        con.setRequestMethod("GET");
        con.setRequestProperty("X-Timestamp", sTime);
        con.setRequestProperty("X-API-KEY", apiKey);
        con.setRequestProperty("X-Customer", customerId);
        con.setRequestProperty("X-Signature", sginature);

        con.setDoOutput(true);
        
        int responseCode = con.getResponseCode();
        BufferedReader br;
        System.out.println("responseCode : "+responseCode);
        if(responseCode==200) {
            br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
        } else {
            br = new BufferedReader(new InputStreamReader(con.getErrorStream(), "UTF-8"));
        }

        String inputLine;
        StringBuffer response = new StringBuffer();
        while ((inputLine = br.readLine()) != null) {
            response.append(inputLine);
        }
        br.close();
        System.out.println(response.toString());

    } catch (Exception e) {
        System.out.println(e);
    }

 

아마 엄청 익숙한 코드일 것 같다. 큰 틀을 같은데 Stream Class를 어떻게 사용할 것인가 분기 처리하는 부분 등에서만 차이가 있고 나머지는 다 비슷비슷하다.

 

또한 저걸 사용하면 예외 처리하는 부분이 엄청나게 많이 발생해서 다중 try-catch를 걸어줘야 할 때도 있던 것 같다.

 


 

근데 Spring3부터 RestTemplate이라는 Http 통신 라이브러리가 지원되어서 이러한 기계적이고 반복적인 Boilerplate code(상용구 코드)들을 없애버릴 수 있다길래 가져와서 적용해봤다.

 

이참에 나중에 Http 통신 라이브러리들을 모아서 정리해놔야겠다.

 

적용대상은 현재 개인 프로젝트로 개발 중인 Keyword 검색 애플리케이션이다.

네이버 광고 API라고도 불리는 searchAd API를 이용해 해당 키워드로 검색 시 연관 키워드를 보여주고 각각의 월 조회수 등을 Response 해주는 꿀 같은 API다.

아래와 같이 사용하면 된다.

 

[RestTemplate 예제 코드]

  • RestTemplate을 이용하면 HttpUrlConnection에서는 일일이 순차적으로 코딩하던걸 한 줄로 합칠 수 있다.
  • Stream Class를 직접 코딩하지 않고 원하는 데이터 타입 or DTO(VO)로 한 번에 받을 수 있다.
  • URI or URL, Header, HttpMethod방식(GET, POST...), Response Type을 인자로 일괄적으로 넘길 수 있다(가독성)
  • URI로 하면 Parameter 조합 시 encoding도 일괄적으로 같이 할 수 있다.
  • HttpStatus 코드 또한 응답 데이터를 가지고 getStatusCode 형태로 메서드를 제공해준다

[URI, Header Setting]

HttpHeaders headers = new HttpHeaders();
        headers.add("X-Timestamp", unixTime);
        headers.add("X-API-KEY", apiKey);
        headers.add("X-Customer", customerId);
        headers.add("X-Signature", xSign);

HttpEntity headerEntity = new HttpEntity(headers);

MultiValueMap<String, String> params = getParams();

URI uri = UriComponentsBuilder.fromUriString(baseUrl + requestUrl)
                .queryParams(params)
                .build().encode()
                .toUri();

 

 

@GetMapping("api/v1/searchs")
    public ResponseEntity<CreateSearchDataResponse> searchs() throws SignatureException {

        //중략

       RestTemplate restTemplate = new RestTemplate();
       ResponseEntity<CreateSearchDataResponse> responseEntity
                = restTemplate.exchange(uri,
                                        HttpMethod.GET,
                                        headerEntity,
                                        CreateSearchDataResponse.class);

        return responseEntity;

    }

 

[Response 받기]

  • 이전처럼 Map에 담아서 key를 통해 가져오는 것도 가능하지만 아래처럼 DTO or VO를 생성하면 바로 꺼내다 쓸 수 있다.
    @Data
    static class CreateSearchDataResponse {

        private List<ResponseData> keywordList;

        @Getter
        static class ResponseData{
            private String relKeyword;
            private String monthlyPcQcCnt;
            private String monthlyMobileQcCnt;
            private String monthlyAvePcClkCnt;
            private String monthlyAveMobileClkCnt;
            private String monthlyAvePcCtr;
            private String monthlyAveMobileCtr;
            private String plAvgDepth;
            private String compIdx;
        }
    }

 

근데 아직 초보라 저런 식으로 DTO를 생성하는 것이 맞는 건지 잘 모르겠다.

처음에는 ResponseData라는 inner class 없이 CreateSearchDataResponse를 바로 넣었는데 HttpStatus code = 200으로

성공했지만 data가 다 null이 떴었다. 또한 아래와 같은 오류도 발생했다.

 

[오류]

Cannot deserialize instance of java.lang.String out of START_ARRAY token

 

이 부분 때문에 몇 시간을 잡아먹었는지 모르겠는데..

바보같이 JSON Array 형태로 넘어오는데 그냥 Object로 받으려고 해서 안된 것 같았다.

 

아무튼 이렇게 하면 따로 JsonObject나 String을 통해서 별도의 일시적인 객체의 생성 없이

DTO만으로 아래와 같이 객체 그래프가 끊기지 않고 참조가 가능하다.

System.out.println("responseEntity = " + responseEntity.getBody().getKeywordList().get(0).getRelKeyword().getClass());

 

 

 

이번 포스팅은 이러한 방식도 있다는 것을 말하고 싶었다. 예전에는 왜 그랬나 싶지만 익숙한 코드를 잘 작동된다고 해서 수정하지 않는 건 위험을 줄이는 방책일 수도 있지만 개발자는 끊임없이 새롭거나 다른 방식도 사용해봐야 실력이 느는 것 같다.

'웹 프로그래밍 > SpringBoot' 카테고리의 다른 글

PUTMapping, DELETEMapping 유의점  (0) 2021.08.11

이 글을 공유합시다

facebook twitter kakaoTalk kakaostory naver band
loading