웹 개발을 하다 보면 내부건 외부 서비스 간에 HTTP 통신을 할 때가 매우 많다.
나도 실무에 있을 때 특히 API를 이용할 때 많이 썼고 무척이나 나를 괴롭혔던 기억이 있다.
이에 관해 구글링을 하면 대부분 URL Connection, HttpURLConnection, HttpClient 등 자바에 기본적으로 내장되어있는 API 라이브러리들이 있고 한글로 검색하면 저 3개를 가장 많이 이용하는 것 같다.
근데 개발하면서 구글링한 소스, 옛날부터 애플리케이션에 수정 없이 사용해왔던 코드들을 사용하다 보니 요즘 API에 사용하면 에러가 발생하고 좀 수정해야 하고 호출에 따른 조건이 있을 때에는 골치가 아팠다. 또한 그 내부 코드들을 이해하려다 보면 코드가 지저분해지고 또 Java Stream Class를 통해 처리해야 해서 개발 생산성도 떨어지는 문제가 있었다.
요즘 개발 트렌드와는 거리가 먼 느낌..?
[HttpUrlConnection 예제코드]
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 예제 코드]
[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 받기]
@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());
이번 포스팅은 이러한 방식도 있다는 것을 말하고 싶었다. 예전에는 왜 그랬나 싶지만 익숙한 코드를 잘 작동된다고 해서 수정하지 않는 건 위험을 줄이는 방책일 수도 있지만 개발자는 끊임없이 새롭거나 다른 방식도 사용해봐야 실력이 느는 것 같다.
PUTMapping, DELETEMapping 유의점 (0) | 2021.08.11 |
---|