
RestTemplate
- 간편하게 Rest 방식 API를 호출할 수 있는 Spring 내장 클래스
- json, xml 응답을 모두 받을 수 있다
- Rest API 서비스를 요청 후 응답 받을 수 있도록 설계되어있으며 HTTP 프로토콜의 메소드(ex. GET, POST, DELETE, PUT)들에 적합한 여러 메소드들을 제공
- Spring Framework 5부터는 WebFlux 스택과 함께 Spring은 WebClient 라는 새로운 HTTP 클라이언트를 도입하여 기존의 동기식 API를 제공할 뿐 만 아니라 효율적인 비차단 및 비동기 접근 방식을 지원하여, Spring 5.0 이후 부터는 RestTemplate는 deprecated 됨(WebClient 사용 지향)
특징
- Spring 3.0 부터 지원하는 Spring의 HTTP 통신 템플릿
- HTTP 요청 후 JSON, XML, String 과 같은 응답을 받을 수 있는 템플릿
- Blocking I/O 기반의 동기방식을 사용하는 템플릿
- RESTful 형식에 맞추어진 템플릿
- Header, Content-Tpye등을 설정하여 외부 API 호출
- Server to Server 통신에 사용
동작 원리
- 애플리케이션 내부에서 REST API에 요청하기 위해 RestTemplate의 메서드를 호출한다.
- RestTemplate은 MessageConverter를 이용해 java object를 request body에 담을 message(JSON etc.)로 변환한다. 메시지 형태는 상황에 따라 다름
- ClientHttpRequestFactory에서 ClientHttpRequest을 받아와 요청을 전달한다.
- 실질적으로 ClientHttpRequest가 HTTP 통신으로 요청을 수행한다.
- RestTemplate이 에러핸들링을 한다.
- ClientHttpResponse에서 응답 데이터를 가져와 오류가 있으면 처리한다.
- MessageConverter를 이용해 response body의 message를 java object로 변환한다.
- 결과를 애플리케이션에 돌려준다.
- RestTemplate은 통신 과정을 ClientHttpRequestFactory(ClientHttpRequest, ClientHttpResponse)에 위임합니다. ClientHttpRequestFactory의 실체는 HttpURLConnection, Apache HttpComponents, HttpClient와 같은 HTTP Client
API 호출 클래스 종류
- RestTemplate
- Spring 3부터 지원 되었고 REST API 호출이후 응답을 받을 때까지 기다리는 동기방식
- AsyncRestTemplate
- Spring 4에 추가된 비동기 RestTemplate
- Spring 5.0에서는 deprecated
- WebClient
- Spring 5에 추가된 논블럭, 리엑티브 웹 클리이언트로 동기, 비동기 방식을 지원
지원 메소드

사용 방법
- 결과값을 담을 객체를 생성
- 타임아웃 설정시 HttpComponentsClientHttpRequestFactory 객체를 생성
- RestTemplate 객체를 생성
- header 설정을 위해 HttpHeader 클래스를 생성한 후 HttpEntity 객체에 넣어줌
- 요청 URL을 정의
- exchange() 메소드로 api를 호출
- 요청한 결과를 HashMap에 추가
의존성 추가
- RestTemplate을 생성할 때 어떠한 HTTP Client를 사용할 것인지 ClientHttpRequestFactorty를 전달하여 지정할 수 있다. 기본 생성자의 경우 내부적으로는 ClientHttpRequestFactory의 구현체인 SimpleClientHttpRequestFactory를 사용하여 초기화 합니다. 이 경우 jdk가 기본으로 제공하는 HttpURLConnection을 통해 ClientHttpRequest 객체를 생성한다. 만약, apache에서 제공하는 HttpClient를 사용하고 싶다면 HttpComponentsClientHttpRequestFactory를 생성자에 넘겨주면 된다. HttpClient 사용을 위해서는 Apache HttpClient 라이브러리를 포함하고 있어야 한다.
implementation 'org.apache.httpcomponents:httpcore:4.4.15'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.15</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
사용 예시
public class RestAPI {
@GetMapping("")
public HashMap<String, Object> callAPI(){
HashMap<String, Object> resultMap = new HashMap<String, Object>();
try {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(5000);
HttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(50)
.setMaxConnPerRoute(20).build();
factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
HttpHeader header = new HttpHeader();
HttpEntity<String> entity = new HttpEntity<String>(header);
String url = "api 요청 URL";
UriComponents uri = UriComponentsBuilder.fromHttpUrl(url).queryParam("파라미터명", 값).build(false);
ResponseEntity<Map> response = restTemplate.exchange(uri.toString, HttpMethod.GET, entity, Map.class);
resultMap.put("statusCode", response.getStatusCodeValue());
resultMap.put("header", response.getHeaders());
resultMap.put("body", response.getBody());
} catch (Exception e) {
e.printStackTrace();
}
}
}
요청 URI 설정 방법
String url = "요청 URL";
url = url + "?파라미터명=" + 값;
url = url + "&파라미터명=" + 값;
StringBuffer urlBuffer = new StringBuffer();
urlBuffer.append("요청 URL")
.append("?파라미터명=")
.append(값)
.append("&파라미터명=")
.append(값);
URI uri = UriComponentsBuilder.fromHttpUrl("요청 URL")
.queryParams("파라미터명", 값)
.queryParams("파라미터명", 값)
.queryParams("파라미터명", URLEncoder.encode(값, "UTF-8"))
.build();
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.path("요청 URL")
.queryParam("파라미터명", 값)
.queryParam("파라미터명", 값)
.build();
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("http")
.host("요청 URL")
.path("/{language}/{type}")
.queryParam("파라미터명", 값)
.queryParam("파라미터명", 값)
.build()
.expand("java", "example")
.encode();
- Header는 Spring Framework에서 제공하는 HttpHeaders 클래스에 추가
- 만들어진 Header는 Spring Framework에서 제공하는 HttpEntity 클래스에 추가하여 사용
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application","json",Charset.forName("UTF-8")));
headers.setAccept(Arrays.asList(new MediaType[] { MediaType.APPLICATION_JSON }));
headers.set("헤더이름", "값");
ResponseEntity<String> responseEntity = restTemplate.exchange("요청 URL"
, HttpMethod.GET, new HttpEntity<>(headers), String.class);
Body 만들기
- Body는 보통 key, value의 쌍으로 이루어지기 때문에 Java에서 제공해주는 MultiValueMap 타입을 사용
- 만들어진 Body는 Spring Framework에서 제공하는 HttpEntity 클래스에 추가하여 사용
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("키", "값");
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.exchange("요청 URL"
, HttpMethod.GET, entity, String.class);
Get 방식 예시
- getForEntity("요청 URL", 응답내용 매핑 객체)
- exchange("요청 URL", HttpMethod.GET, 응답내용 매핑 객체)
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(5000);
RestTemplate restTemplate = new RestTemplate(factory);
URI url = UriComponentsBuilder.fromHttpUrl("요청 URL")
.queryParams("파라미터명", 값)
.queryParams("파라미터명", 값)
.queryParams("파라미터명", URLEncoder.encode(값, "UTF-8"))
.build();
ResponseEntity<String> response = restTemplate.exchange(url.toString(), HttpMethod.GET, entity, String.class);
System.out.println("status : " + response.getStatusCode());
System.out.println("body : " + response.getBody());
Post 방식 예시
- postForEntity("요청 URL", 응답내용 매핑 객체)
- exchange("요청 URL", HttpMethod.POST, 응답내용 매핑 객체)
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(5000);
RestTemplate restTemplate = new RestTemplate(factory);
HttpHeader headers = new HttpHeader();
headers.set("헤더이름", "값")
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("키", "값");
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.exchange("요청 URL", HttpMethod.POST, entity, String.class);
System.out.println("status : " + response.getStatusCode());
System.out.println("body : " + response.getBody());
RestTemplate Builder 유틸
- Builder 패턴을 이용하여 RestTemplate로 HTTP 요청을 할 수 있도록 만든 Util 클래스
- HTTP 요청 후 status, header, body를 key로 가진 HashMap<String, Object> 형태로 반환
HashMap<String, Object> result = new HttpUtil()
.url("https://www.naver.com")
.method("get")
.queryString("파라미터명", "값")
.build();
System.out.println(result.get("status").toString());
System.out.println(result.get("header").toString());
System.out.println(result.get("body").toString());
HashMap<String, Object> result = new HttpUtil()
.url("https://www.naver.com")
.method("post")
.contentType("application", "json", "UTF-8")
.header("헤더이름", "값")
.body("파라미터명", "값")
.build();
System.out.println(result.get("status").toString());
System.out.println(result.get("header").toString());
System.out.println(result.get("body").toString());
Util 클래스
package org.example.common.util;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
public class HttpUtil {
private HttpHeaders headers;
private MultiValueMap<String, String> body;
private HttpComponentsClientHttpRequestFactory factory;
private StringBuilder urlBuilder;
private boolean queryStringToken;
private String url;
private String method;
public HttpUtil(){
this.headers = new HttpHeaders();
this.factory = new HttpComponentsClientHttpRequestFactory();
this.factory.setConnectTimeout(5000);
this.factory.setReadTimeout(5000);
this.body = new LinkedMultiValueMap<String, String>();
this.queryStringToken = true;
}
public HttpUtil contentType(String type, String subType, String charSet){
this.headers.setContentType(new MediaType(type, subType, Charset.forName(charSet)));
return this;
}
public HttpUtil connectTimeout(int time){
this.factory.setConnectTimeout(time);
return this;
}
public HttpUtil readTimeout(int time){
this.factory.setReadTimeout(time);
return this;
}
public HttpUtil url(String url) {
this.urlBuilder = new StringBuilder();
urlBuilder.append(url);
return this;
}
public HttpUtil queryString(String name, String value) {
Assert.notNull(urlBuilder, "url 미입력");
if(queryStringToken) {
urlBuilder.append("?")
.append(name)
.append("=")
.append(value);
queryStringToken = false;
} else {
urlBuilder.append("&")
.append(name)
.append("=")
.append(value);
}
return this;
}
public HttpUtil method(String method) {
this.method = method.toUpperCase();
return this;
}
public HttpUtil header(String name, String value){
headers.set(name, value);
return this;
}
public HttpUtil body(String key, String value){
this.body.add(key, value);
return this;
}
public HttpUtil body(HashMap<String, Object> params){
Iterator<String> itr = params.keySet().iterator();
while(itr.hasNext()){
String key = itr.next();
body.add(key, (String)params.get(key));
}
return this;
}
public HashMap<String, Object> build(){
HashMap<String, Object> result = new HashMap<>();
RestTemplate restTemplate = new RestTemplate(factory);
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);
url = urlBuilder.toString();
ResponseEntity<String> response = null;
if ("GET".equals(method)){
response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
} else if("POST".equals(method)) {
response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
}
result.put("status", response.getStatusCode());
result.put("header", response.getHeaders());
result.put("body", response.getBody());
return result;
}
}