RestTemplate란
Spring에서 지원하는 객체로 간편하게 Rest 방식 API를 호출할 수 있는 Spring 내장 클래스입니다.
Spring 3.0부터 지원되었고, json, xml 응답을 모두 받을 수 있습니다.
Rest API 서비스를 요청 후 응답 받을 수 있도록 설계되어있으며 HTTP 프로토콜의 메소드(ex. GET, POST, DELETE, PUT)들에 적합한 여러 메소드들을 제공합니다.
※ Spring Framework 5부터는 WebFlux 스택과 함께 Spring은 WebClient 라는 새로운 HTTP 클라이언트를 도입하여 기존의 동기식 API를 제공할 뿐 만 아니라 효율적인 비차단 및 비동기 접근 방식을 지원하여, Spring 5.0 이후 부터는 RestTemplate는 deprecated 되었습니다. (WebClient 사용 지향
RestTemplate의 특징
· Spring 3.0 부터 지원하는 Spring의 HTTP 통신 템플릿
· HTTP 요청 후 JSON, XML, String 과 같은 응답을 받을 수 있는 템플릿
· Blocking I/O 기반의 동기방식을 사용하는 템플릿
· RESTful 형식에 맞추어진 템플릿
· Header, Content-Tpye등을 설정하여 외부 API 호출
· Server to Server 통신에 사용
RestTemplate 동작 원리
1. 애플리케이션 내부에서 REST API에 요청하기 위해 RestTemplate의 메서드를 호출한다.
2. RestTemplate은 MessageConverter를 이용해 java object를 request body에 담을 message(JSON etc.)로 변환한다. 메시지 형태는 상황에 따라 다름
3. ClientHttpRequestFactory에서 ClientHttpRequest을 받아와 요청을 전달한다.
4. 실질적으로 ClientHttpRequest가 HTTP 통신으로 요청을 수행한다.
5. RestTemplate이 에러핸들링을 한다.
6. ClientHttpResponse에서 응답 데이터를 가져와 오류가 있으면 처리한다.
7. MessageConverter를 이용해 response body의 message를 java object로 변환한다.
8. 결과를 애플리케이션에 돌려준다.
※ RestTemplate은 통신 과정을 ClientHttpRequestFactory(ClientHttpRequest, ClientHttpResponse)에 위임합니다. ClientHttpRequestFactory의 실체는 HttpURLConnection, Apache HttpComponents, HttpClient와 같은 HTTP Client
RestTemplate
Spring 3부터 지원 되었고 REST API 호출이후 응답을 받을 때까지 기다리는 동기방식입니다
AsyncRestTemplate
Spring 4에 추가된 비동기 RestTemplate입니다
Spring 5.0에서는 deprecated 되었습니다
WebClient
Spring 5에 추가된 논블럭, 리엑티브 웹 클리이언트로 동기, 비동기 방식을 지원합니다.
RestTemplate 사용 방법
0. 결과값을 담을 객체를 생성합니다.
1. 타임아웃 설정시 HttpComponentsClientHttpRequestFactory 객체를 생성합니다.
2. RestTemplate 객체를 생성합니다.
3. header 설정을 위해 HttpHeader 클래스를 생성한 후 HttpEntity 객체에 넣어줍니다.
4. 요청 URL을 정의해줍니다.
5. exchange() 메소드로 api를 호출합니다.
6. 요청한 결과를 HashMap에 추가합니다.
RestTemplate 사용 예시
public class RestAPI {
@GetMapping("")
public HashMap<String, Object> callAPI(){
// 0. 결과값을 담을 객체를 생성합니다.
HashMap<String, Object> resultMap = new HashMap<String, Object>();
try {
// 1. 타임아웃 설정시 HttpComponentsClientHttpRequestFactory 객체를 생성합니다.
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000); // 타임아웃 설정 5초
factory.setReadTimeout(5000); // 타임아웃 설정 5초
//Apache HttpComponents : 각 호스트(IP와 Port의 조합)당 커넥션 풀에 생성가능한 커넥션 수
HttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(50)//최대 커넥션 수
.setMaxConnPerRoute(20).build();
factory.setHttpClient(httpClient);
// 2. RestTemplate 객체를 생성합니다.
RestTemplate restTemplate = new RestTemplate(factory);
// 3. header 설정을 위해 HttpHeader 클래스를 생성한 후 HttpEntity 객체에 넣어줍니다.
HttpHeader header = new HttpHeader();
HttpEntity<String> entity = new HttpEntity<String>(header);
// 4. 요청 URL을 정의해줍니다.
String url = "api 요청 URL";
UriComponents uri = UriComponentsBuilder.fromHttpUrl(url).queryParam("파라미터명", 값).build(false);
// 5. exchange() 메소드로 api를 호출합니다.
ResponseEntity<Map> response = restTemplate.exchange(uri.toString, HttpMethod.GET, entity, Map.class);
// 6. 요청한 결과를 HashMap에 추가합니다.
// HTTP Status Code
resultMap.put("statusCode", response.getStatusCodeValue());
// 헤더 정보
resultMap.put("header", response.getHeaders());
// 반환받은 실제 데이터 정보
resultMap.put("body", response.getBody());
} catch (Exception e) {
e.printStackTrace();
}// end catch
}//callAPI
}//class
요청 URI 설정 방법
· String 변수 사용, StringBuffer 객체 사용, URI 객체 사용, UriComponents 객체 사용
/**
* String 변수 사용하여 만들기
*/
// 1. 요청 URL
String url = "요청 URL";
// 2. 쿼리스트링 추가
url = url + "?파라미터명=" + 값;
url = url + "&파라미터명=" + 값;
/**
* StringBuffer 객체 사용하여 만들기
* urlBuffer.toString() 으로 사용
*/
StringBuffer urlBuffer = new StringBuffer();
urlBuffer.append("요청 URL")
.append("?파라미터명=")
.append(값)
.append("&파라미터명=")
.append(값);
/**
* URI 객체 사용하여 만들기 (정적 파라미터 설정)
* uri.toString() 으로 사용
*/
// 1. 요청 URL 및 쿼리스트링 추가
URI uri = UriComponentsBuilder.fromHttpUrl("요청 URL")
.queryParams("파라미터명", 값)
.queryParams("파라미터명", 값)
.queryParams("파라미터명", URLEncoder.encode(값, "UTF-8"))
.build();
/**
* UriComponents 객체 사용하여 만들기
* uriComponenets.toUriString() 으로 사용
*/
// 1. 요청 URL 및 쿼리스트링 추가
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.path("요청 URL")
.queryParam("파라미터명", 값)
.queryParam("파라미터명", 값)
.build();
/**
* UriComponents 객체 사용하여 만들기 (동적 주소 사용 : pathVariable)
* uriComponenets.toUriString() 으로 사용
*/
// 1. 요청 URL 및 쿼리스트링 추가
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("http")
.host("요청 URL")
.path("/{language}/{type}") // PathVariable 사용시
.queryParam("파라미터명", 값)
.queryParam("파라미터명", 값)
.build()
.expand("java", "example")
.encode();
Header 만들기
· Header는 Spring Framework에서 제공하는 HttpHeaders 클래스에 추가합니다.
· 만들어진 Header는 Spring Framework에서 제공하는 HttpEntity 클래스에 추가하여 사용합니다.
// 1. HttpHeaders 객체 생성
HttpHeaders headers = new HttpHeaders();
// 2. 헤더 설정 : ContentType, Accept 설정
headers.setContentType(new MediaType("application","json",Charset.forName("UTF-8")));
headers.setAccept(Arrays.asList(new MediaType[] { MediaType.APPLICATION_JSON }));
// 3. 헤더 설정 : Key, Value 쌍으로 설정
headers.set("헤더이름", "값");
// HTTP 요청할 떄 생성한 Header 설정
ResponseEntity<String> responseEntity = restTemplate.exchange("요청 URL"
, HttpMethod.GET, new HttpEntity<>(headers), String.class);
Body 만들기
· Body는 보통 key, value의 쌍으로 이루어지기 때문에 Java에서 제공해주는 MultiValueMap 타입을 사용합니다.
· 만들어진 Body는 Spring Framework에서 제공하는 HttpEntity 클래스에 추가하여 사용합니다.
// 1. MultiValueMap 객체 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
// 2. body : 요청 파라미터 설정
body.add("키", "값");
// 3. 만들어진 header와 body를 가진 HttpEntity 객체 생성
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);
// HTTP 요청
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); // 타임아웃 설정 5초
factory.setReadTimeout(5000); // 타임아웃 설정 5초
// RestTemplate 객체 생성
RestTemplate restTemplate = new RestTemplate(factory);
// 요청 URL 및 쿼리스트링 설정
URI url = UriComponentsBuilder.fromHttpUrl("요청 URL")
.queryParams("파라미터명", 값)
.queryParams("파라미터명", 값)
.queryParams("파라미터명", URLEncoder.encode(값, "UTF-8"))
.build();
// HTTP GET 요청
ResponseEntity<String> response = restTemplate.exchange(url.toString(), HttpMethod.GET, entity, String.class);
// HTTP GET 요청에 대한 응답 확인
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); // 타임아웃 설정 5초
factory.setReadTimeout(5000); // 타임아웃 설정 5초
// RestTemplate 객체 생성
RestTemplate restTemplate = new RestTemplate(factory);
// Header 및 Body 설정
HttpHeader headers = new HttpHeader();
headers.set("헤더이름", "값")
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("키", "값");
// 설정한 Header와 Body를 가진 HttpEntity 객체 생성
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);
// HTTP POST 요청
ResponseEntity<String> response = restTemplate.exchange("요청 URL", HttpMethod.POST, entity, String.class);
// HTTP POST 요청에 대한 응답 확인
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> 형태로 반환
// GET 요청
HashMap<String, Object> result = new HttpUtil()
.url("https://www.naver.com")
.method("get")
.queryString("파라미터명", "값")
.build();
// GET 요청 결과
System.out.println(result.get("status").toString());
System.out.println(result.get("header").toString());
System.out.println(result.get("body").toString());
// POST 요청
HashMap<String, Object> result = new HttpUtil()
.url("https://www.naver.com")
.method("post")
.contentType("application", "json", "UTF-8")
.header("헤더이름", "값")
.body("파라미터명", "값")
.build();
// POST 요청 결과
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;
// 요청 바디 : <Key, Value> 쌍
private MultiValueMap<String, String> body;
// 타임아웃
private HttpComponentsClientHttpRequestFactory factory;
// 요청 URL
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;
}
/**
* content-type 설정 : new MediaType 설정 값
*
* @param type
* @param subType
* @param charSet
* @return
*/
public HttpUtil contentType(String type, String subType, String charSet){
this.headers.setContentType(new MediaType(type, subType, Charset.forName(charSet)));
return this;
}
/**
* connect-timeout 설정<br>
* default : 5초
*
* @param time
* @return
*/
public HttpUtil connectTimeout(int time){
this.factory.setConnectTimeout(time);
return this;
}
/**
* read-timeout 설정<br>
* default : 5초
*
* @param time
* @return
*/
public HttpUtil readTimeout(int time){
this.factory.setReadTimeout(time);
return this;
}
/**
* 요청 URL 설정
*
* @param url
* @return
*/
public HttpUtil url(String url) {
this.urlBuilder = new StringBuilder();
urlBuilder.append(url);
return this;
}
/**
* 쿼리스트링 설정
*
* @param name
* @param value
* @return
*/
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;
}
/**
* 요청 방식 설정(get, post)
*
* @param method
* @return
*/
public HttpUtil method(String method) {
this.method = method.toUpperCase();
return this;
}
/**
* 요청 헤더 설정
*
* @param name
* @param value
* @return
*/
public HttpUtil header(String name, String value){
headers.set(name, value);
return this;
}
/**
* body 요청 파라미터 설정 : key, value
*
* @param key
* @param value
* @return
*/
public HttpUtil body(String key, String value){
this.body.add(key, value);
return this;
}
/**
* body 요청 파라미터 설정 : map
*
* @param params
* @return
*/
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;
}
/**
* HTTP 요청 후 결과 반환(status, header, body)
*
* @return
*/
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;
}
}
'개발일지 > 스프링' 카테고리의 다른 글
XSS 예방 (1) | 2022.12.19 |
---|---|
카카오 로그인 기능 생성 및 셋팅(인가코드 발급받기, 토큰발급받기) (0) | 2022.12.15 |
JPA 네이밍전략, 네이티브 쿼리 사용하기 (0) | 2022.12.12 |
Spring Boot 회원가입 샘플 기능 만들어보기 (0) | 2022.12.08 |
yml 설정하기 - 2 (0) | 2022.12.08 |
댓글