Java - RestTemplate의 응답 처리하기

일반인3호·2024년 5월 13일

Java

목록 보기
3/4

Legacy 프로젝트를 Java 17버전으로, Spring Boot 3.2.4 버전으로 업그레이드 한 시점에서 RestTemplate 대신 RestClient를 사용했었다(새로운 기술은 써보자는 마인드)
그럼에도 회사에서는 아직 Java 1.8 버전을 쓰는 프로젝트가 대부분..
RestTemplate으로 API 데이터를 땡겨 오는 건 껌이지 라고 생각했다가 과연 이게 최선일까 하는 생각에 써본다

초기 구현

RestTemplate을 조금 들여다 보면 대부분의 기능들이 execute()로 이루어져 있다 또한 이는 doExecute()의 호출로 연계되는데
너무 깊숙하게는 들어가지 말고 doExecute()의 구조만 잠깐 살펴보자

@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
	...
}

실행 시킬 url, 호출할 API의 HttpMethod 타입, 요청 콜백함수, 응답 추출
이렇게 4가지로 구분된다

getForObject, postForObject, put, delete 등의 기능은 execute의 HttpMethod에 각 타입을 지정해주는 것과 같다

나머지 처리는 CallBack 함수들의 총체..

그러다 보니 url과 HttpMethod만 잘 정해주면 API에 요청하고 응답을 반환받는 데까지 오류가 발생할 일이 잘 없다

간단하게 getForObject를 사용해보자
응답 타입은 대체로 사용하는 Json 타입 전체를 받아 보기 위해 String.class로 받아보자

이렇게 되면 String을 Dto와 같은 구조로 매핑시키기 위해 ObjectMapper 등의 처리를 거쳐야 한다

String get = restTemplate.getForObject(getUrl(pageNo++, numOfRows), String.class);

HashMap<String, String>[] dataMaps = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModule(new JavaTimeModule()).readValue(jsonData, new TypeReference<HashMap<String,String>[]>(){});

보통의 공공기관 데이터의 경우 일정 구조의 목록 형태로 데이터를 내려주게 된다
하지만 꼭 첫 번째 데이터에 데이터 개수라든지 페이지 번호라든지 하는 예외 항목들이 존재하고, 받고 싶은 데이터의 필드 이름이 Dto 필드 이름과 일치하지 않으면 자동으로 맞춰주지 않기 때문에 불가피하게 Map 형태로 받는 경우들이 생기더라

일반적인 execute, getForObject 등의 함수들로 HashMap의 List를 받아올 수 있을까?

execute 함수를 잘 보면 응답 타입에 관한 파라미터는 명시적으로 나와있지 않다
다만, 모든 함수의 경우 responseExtractor를 거쳐서 응답 타입이 정해지게 된다

Map 타입의 레퍼런스들은 class에 내부 타입이 정해져 있지 않기 때문에 execute, getForObject 같은 함수들을 쓰면 raw use 경고를 intelliJ에서 피할 수가 없다

아무래도 제네릭을 거쳐 직렬화 하는 데에 안정성이 떨어진다는 의미라고 해석할 수 있지 않을까

exchange()

일반적으로 spring framework의 response를 사용하는 경우를 위해 ResponseEntity를 반환하는 함수들이 여럿 구현되어 있다

사실 생각해보면 REST API를 사용하는 곳에 요청을 보낼 경우 응답 상태나 결과 등을 체크해야 하기 때문에 ResponseEntity를 포함하는 함수를 사용하는게 적절해보이긴 하다

하지만 이들 중에서 exchange 만이 responseType으로 class가 아닌 ParameterizedTypeReference를 인자로 받아준다

내부 구조를 살펴보면 exchange나 getForEntity나 차이는 없다

왜 exchange에서만 가능하게 만들어줬는지는 잘 모르겠다
어차피 내부에서는

ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);

형태로 동일하게 사용되는데 말이다

그래서 동일한 url로 exchange 를 사용하게 되면 objectMapper를 주입받거나 하는 등의 List<HashMap<>>으로 변환할 필요 없이 Response의 body 타입이 매핑된다


ResponseEntity<List<HashMap<String, String>>> exchange = restTemplate.exchange(
        getUrl(pageNo++, numOfRows),
        HttpMethod.GET,
        HttpEntity.EMPTY,
        new ParameterizedTypeReference<List<HashMap<String, String>>>() {}
);

사람들이 exchange를 주로 사용하는 이유에 status등을 활용한 예외처리도 있겠지만 위와 같은 타입 문제를 해결하기 위해서도 있지 않을까 한다

profile
'답답하면 니가 블로그 쓰던지' '예, 그래서 제가 써보겠습니다' 복붙이 난무하는 검색에 질려서 쓰는 블로그

0개의 댓글