Overview
RestTemplate에 대해 이해해보자
- RestTemplate
- HttpHeaders
- MultiValueMap
- HttpEntity
- URI , UriComponentsBuilder
- 모든 과정을 요약
1. RestTemplate 이란?
RestTemplate은 스프링 프레임워크에서 제공하는 HTTP 클라이언트 라이브러리이며, RESTful 웹 서비스를 호출하고 그 결과를 받아오는 기능을 제공한다.
참조문서
https://adjh54.tistory.com/234
[Java] Spring Boot Web 활용 : RestTemplate 이해하기
해당 글에서는 RestTemplate에 대해 이해하고 활용 방법에 대해 확인해 봅니다. 1) RestTemplate 💡 RestTemplate - HTTP 통신을 위한 도구로 RESTful API 웹 서비스와의 상호작용을 쉽게 외부 도메인에서 데이터
adjh54.tistory.com
1. 의존성 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web' // Spring Boot Web
}
2) RestTemplate 특징
1. HTTP 요청 및 응답에 대해 동기식 요청으로 블로킹하여 데이터를 주고받습니다.
1.1. 동기식 요청(Synchronous request) & 블로킹 요청(Blocking Request)로킹됩니다.
💡 동기식 요청(Synchronous request) & 블로킹 요청(Blocking Request)
- RestTemplate은 기본적으로 ‘동기식 요청’ 처리를 수행하며 요청을 보내고 응답을 받을 때까지 블로킹됩니다.
[ 더 알아보기 ]
💡 블로킹 요청(Blocking Request)
- 클라이언트에서 요청을 보내면 결과가 반환될 때까지 대기를 하는 것을 의미합니다. unblocked 상태가 되면 다음 요청에 대해 처리를 수행합니다.
1.2. 비동기식 요청(Asynchrouse Request) & 논 블로킹 요청(Non-Blocking Request)
💡 비동기식 요청(Asynchrouse Request) & 논 블로킹 요청(Non-Blocking Request)
- webflux에서 제공하는 Non-Blocking Request을 수행하면서 요청을 보내고 결과가 반환되지 않더라도 다른 작업을 수행할 수 있는 것을 의미합니다.
💡 [참고] 비동기식 요청 & 논 블로킹 요청에 사용되는 WebClient에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
비교 | 동기식(Synchronous) | 비동기식(Asynchronous) |
실행 모델 | 요청과 응답이 동시에 일어남 | 요청과 응답이 동시에 일어나지 않음 |
블로킹 | 요청에 대한 응답이 올 때까지 블로킹 됨 | 요청에 대한 응답을 기다리지 않고 다른 작업을 수행할 수 있음 |
성능 | 대량의 요청이 동시에 발생하면 성능 저하가 발생할 수 있음 | 대량의 요청을 효율적으로 처리할 수 있음 |
코드 복잡도 | 비교적 간단하고 직관적임 | 코드가 복잡해지고 디버깅이 어려움 |
2. HTTP 요청 및 응답에 대한 다양한 메서드를 제공합니다
💡 RestTemplate의 경우 HTTP 요청 및 응답에 대한 HTTP Method 별(GET, POST, PUT, PATCH, DELETE) 다양한 메서드를 제공합니다.
Method | HTTP Method | Return Type | 설명 |
getForObject() | GET | Object | GET 요청에 대한 결과를 객체로 반환합니다 |
getForEntity() | GET | ResponseEntity | GET 요청에 대한 결과를 ResponseEntity로 반환합니다 |
postForLocation() | POST | URI | POST 요청에 대한 결과로 헤더에 저장된 URI 반환합니다 |
postForObject() | POST | Object | POST 요청에 대한 결과를 객체로 반환합니다 |
postForEntity() | POST | ResponseEntity | POST 요청에 대한 결과를 ResponseEntity로 반환합니다 |
put() | PUT | void | PUT 요청을 실행합니다 |
patchForObject() | PATCH | Object | PATCH 요청을 실행하고 결과를 객체로 반환합니다 |
delete() | DELETE | void | DELETE 요청을 실행합니다 |
headForHeaders() | HEADER | HttpHeaders | 헤더 정보를 추출하고 HTTP HEAD 메서드를 사용합니다 |
optionsForAllow() | OPTIONS | Set<HttpMethod> | 지원되는 HTTP 메서드를 추출합니다 |
exchange() | any | ResponseEntity | 헤더를 생성하고 모든 요청 방법을 허용합니다 |
execute() | any | T | 요청/응답 콜백을 수정합니다 |
3. HTTP 요청 및 응답을 자동으로 변환하고 역직렬화합니다.
💡 HTTP 요청 및 응답을 ‘자동’으로 변환하고 역직렬화하는 기능을 제공합니다. 이를 위해 RestTemplate은 기본적으로 MessageConverter를 사용합니다.
💡 MessageConverter는 요청 및 응답 바디의 데이터 형식을 변환하고, 요청 및 응답 헤더의 콘텐츠 형식을 설정합니다. RestTemplate은 기본적으로 다양한 MessageConverter를 제공하며, 애플리케이션에서 직접 추가할 수 있습니다.
[ 더 알아보기 ]
💡 직렬화(Serialization)
- 자바 객체를 외부 저장소에 저장하거나 네트워크를 통해 전송하기 위해 객체를 데이터 스트림으로 변환하는 과정입니다. 이 과정에서 객체의 필드 값들이 데이터 스트림에 쓰여집니다.
💡 역직렬화(Deserialization)
- 데이터 스트림으로부터 객체를 재구성하는 과정입니다. 이 과정에서 데이터 스트림에서 읽은 값들이 객체의 필드 값으로 설정됩니다. 이 과정에서는 데이터 스트림이 어떤 객체인지 알아야 하므로 객체의 클래스 정보가 함께 전달되어야 합니다.
// RestTemplate 생성
RestTemplate restTemplate = new RestTemplate();
// 요청 매개변수 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<RequestDto> request = new HttpEntity<>(requestDto, headers);
// HTTP 요청 및 응답 처리
ResponseDto responseDto = restTemplate.exchange(url, HttpMethod.POST, request, ResponseDto.class).getBody();
3) RestTemplate vs WebClient
💡 동기적으로 처리되는 RestTemplate와 비 동기로 처리되는 WebClient를 비교합니다.
구분 | RestTemplate | WebClient |
요청/응답 | 동기 | 비동기 |
Connection pool | Apache Http Client | Netty |
기본 인증 | 지원 | 지원 |
OAuth2 인증 | 지원 | 지원 |
JSON 처리 | Jackson, Gson 등 | Jackson, Gson 등 |
XML 처리 | JAXB, Jackson 등 | Jackson 등 |
multipart/form-data | 지원 | 지원 |
쿠키 | 지원 | 지원 |
헤더 설정 | 지원 | 지원 |
요청 시간 초과 설정 | 지원 | 지원 |
SSL 인증서 검증 | 지원 | 지원 |
로깅 | 지원 | 지원 |
성능 | 느림 | 빠름 |
2. HttpHeaders 이란?
// Request Header 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
메서드 | 설명 |
setContentType(MediaType mediaType) | Content-Type 헤더 설정 |
setAccept(List<MediaType> acceptableMediaTypes) | Accept 헤더 설정 |
add(String headerName, String headerValue) | 특정 헤더 추가 |
addAll(Map<String, String> headers) | 여러 개의 헤더 추가 |
setBearerAuth(String token) | Authorization 헤더에 Bearer 토큰 추가 |
setBasicAuth(String username, String password) | Authorization 헤더에 Basic 인증 정보 추가 |
setIfNoneMatch(String etag) | If-None-Match 헤더 추가 |
setIfModifiedSince(ZonedDateTime ifModifiedSince) | If-Modified-Since 헤더 추가 |
set(String headerName, String headerValue) | 특정 헤더 설정 |
[ 더 알아보기 ]
💡 Content-Type 헤더
- HTTP 요청 또는 응답에서 ‘전송되는 데이터의 형식’을 지정합니다. 이 헤더는 브라우저나 서버가 어떻게 처리해야 할지를 결정합니다.
- 예를 들어, text/plain은 일반적인 텍스트 데이터를 나타내고, application/json은 JSON 형식의 데이터를 나타냅니다.
💡 Accept 헤더
- 클라이언트가 서버에게 요청한 ‘데이터 유형’을 알려줍니다. 이 헤더는 클라이언트가 서버로부터 어떤 유형의 데이터를 받기를 원하는지 지정합니다.
- 예를 들어, text/html은 HTML 문서를 나타내고, application/json은 JSON 형식의 데이터를 나타냅니다.
💡 If-None-Match 헤더
- ‘캐시 된 리소스를 다시 가져오는 것을 방지’ 하기 위해 사용됩니다. 이 헤더는 이전에 클라이언트에서 가져온 리소스의 ETag 값을 서버에 전송하여, 새로운 ETag 값이 없는 경우, 서버는 304 Not Modified 응답을 반환하여 클라이언트에게 새로운 리소스를 다시 가져올 필요가 없음을 알려줍니다.
💡 If-Modified-Since 헤더
- 클라이언트가 마지막으로 리소스를 가져온 시간’을 지정합니다. 이 헤더는 클라이언트가 리소스가 수정되었는지 여부를 확인하기 위해 사용됩니다. 서버가 클라이언트가 지정한 시간 이후에 리소스를 수정한 경우, 서버는 새로운 리소스를 반환하고, 그렇지 않은 경우 304 Not Modified 응답을 반환하여 클라이언트에게 새로운 리소스를 다시 가져올 필요가 없음을 알려줍니다.
💡 [참고] MediaType 별 종류 설명
MediaType | Content Type | 설명 |
APPLICATION_JSON | application/json | JSON 형식 |
TEXT_PLAIN | text/plain | 일반 텍스트 형식 |
APPLICATION_XML | application/xml | XML 형식 |
APPLICATION_ATOM_XML | application/atom+xml | Atom 피드 형식 |
APPLICATION_FORM_URLENCODED | application/x-www-form-urlencoded | HTML 폼 형식 |
APPLICATION_OCTET_STREAM | application/octet-stream | 임의의 바이너리 데이터 |
APPLICATION_PDF | application/pdf | PDF 형식 |
APPLICATION_RSS_XML | application/rss+xml | RSS 피드 형식 |
APPLICATION_XHTML_XML | application/xhtml+xml | XHTML 형식 |
IMAGE_GIF | image/gif | GIF 이미지 형식 |
IMAGE_JPEG | image/jpeg | JPEG 이미지 형식 |
IMAGE_PNG | image/png | PNG 이미지 형식 |
MULTIPART_FORM_DATA | multipart/form-data | 여러 개의 다른 형식의 파트를 하나의 요청에 함께 전송 |
TEXT_HTML | text/html | HTML 형식 |
TEXT_XML | text/xml | XML 형식 |
7. 모든 과정 요약
💡 해당 RestTemplate의 경우 Spring MVC 기준으로 ‘서비스 레이어’에 등록하여 사용하는 것을 권장합니다.
💡 과정 : 객체 생성 → Header 구성 → Body 구성 → Header, Body를 하여 객체에 넣어둡니다 → API내에 데이터를 포함하여 전송합니다. → 결과값을 반환받습니다.
// RestTemplate 생성
RestTemplate restTemplate = new RestTemplate();
// Request Header 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// Request Body 설정
JSONObject requestBody = new JSONObject();
requestBody.put("key", "value");
// Request Entity 생성
HttpEntity entity = new HttpEntity(requestBody.toString(), headers);
// API 호출
String url = "<https://example.com/api>";
ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
// Response Body 출력
System.out.println(response.getBody());
3. MultiValueMap 이란? + LinkedMultiValueMap
문서
https://velog.io/@nimoh/Spring-MultiValueMap%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C
velog
velog.io
Extension of the Map interface that stores multiple values. ( Spring api )
-> 여러 값을 저장하는 맵 인터페이스의 확장
void | add(K key, V value)
Add the given single value to the current list of values for the given key.
|
void | addAll(K key, java.util.List<V> values)
Add all the values of the given list to the current list of values for the given key.
|
V | getFirst(K key)
Return the first value for the given key.
|
void | set(K key, V value)
Set the given single value under the given key.
|
void | setAll(java.util.Map<K,V> values)
Set the given values under.
|
java.util.Map<K,V> | toSingleValueMap()
Returns the first values contained in this MultiValueMap.
|
기존 Map과 차이점
위의 공식문서를 자세히보면 MultiValueMap이 Map 인터페이스를 상속할 때 Value값을 List로 감싼 채로 상속받는 것을 볼 수 있다. 이 말은 즉 하나의 Key와 하나 이상의 value로 이루어진 리스트를 쌍으로 받겠다는 말이다.
이를 실험하기 위해 실행해보면 결과는 다음과 같다.
Map<String, Integer> basicMap = new HashMap<>();
MultiValueMap<String,Integer> multiValueMap = new LinkedMultiValueMap<>();
basicMap.put("test",1);
basicMap.put("test",2);
multiValueMap.add("test",1);
multiValueMap.add("test",2);
System.out.println("basicMap = " + basicMap);
System.out.println("multiValueMap = " + multiValueMap);
=> 실행결과
multiValueMap = {test=[1, 2]} // list 방식
basicMap = {test=2}
주의사항
이라기 보다 MultiValueMap 인터페이스는 스프링 프레임워크에 내장된 것이므로 일반 자바환경에서는 사용할 수 없다. 일반 자바 환경에서 연습해보려다가 안돼서 화내는 일은 없길 바란다.
4. HttpEntity 이란?
문서
HttpEntity (Spring Framework 6.1.3 API)
hasBody public boolean hasBody() Indicates whether this entity has a body.
docs.spring.io
public class HttpEntity
extends Object
Represents an HTTP request or response entity, consisting of headers and body.
Often used in combination with the RestTemplate, like so:
공식 문서에는 위와 같이 나와있다.
해석을 하면 '헤더와 바디로 이루어진 HTTP request, response 엔티티를 나타낸다.' 라고 나와있다.
예제 코드를 보면
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<String> entity = new HttpEntity<>("Hello World", headers);
URI location = template.postForLocation("https://example.com", entity);
HttpHeaders 라는 객체를 생성하여 Header의 ContentType을 설정하여 요청을 보낼 수 있는걸로 보인다.
그 아래 Spring Boot의 Controller 사용 예제 코드도 나와 있다.
@GetMapping("/handle")
public HttpEntity<String> handle(){
HttpHeaders responseHeaders=new HttpHeaders();
responseHeaders.set("MyResponseHeader","MyValue");
return new HttpEntity<>("Hello World",responseHeaders);
}
위의 코드를 보면 View에서 @GetMapping 으로 인한 요청시, Http Header를 설정하여 반환 할 수 있는 걸로 보인다.
이 외에도 공식문서를 보면 getBody(), getHeader() 등의 메서드를 통해 Http body와 header를 반환받을 수 있고,
MultiValueMap<>을 사용하여 Header 와 Body를 매핑 하여 요청, 응답 할 수 있는걸로 보인다.
5. URI이란? + UriComponentsBuilder
문서
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-uri-building.html
URI Links :: Spring Framework
In views such as Thymeleaf, FreeMarker, or JSP, you can build links to annotated controllers by referring to the implicitly or explicitly assigned name for each request mapping. Consider the following example: @RequestMapping("/people/{id}/addresses") publ
docs.spring.io
UriComponentsBuilder (Spring Framework 6.1.3 API)
Request to have the URI template pre-encoded at build time, and URI variables encoded separately when expanded. In comparison to UriComponents.encode(), this method has the same effect on the URI template, i.e. each URI component is encoded by replacing no
docs.spring.io
간단하게 URI 구성도 가능합니다.
RestTemplate restTemplate = new RestTemplate();
URI uri = UriComponentsBuilder.fromUriString("https://nid.naver.com").path("/oauth2.0/token")
.queryParam("grant_type", "authorization_code").queryParam("client_id", "")
.queryParam("client_secret", "").queryParam("code", code).queryParam("state", state).encode()
.build().toUri();
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
모든 과정을 요약해보자
// RestTemplate 생성
RestTemplate restTemplate = new RestTemplate();
// Request Header 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// Request Body 설정
JSONObject requestBody = new JSONObject();
requestBody.put("key", "value");
// Request Entity 생성
HttpEntity entity = new HttpEntity(requestBody.toString(), headers);
// API 호출
String url = "<https://example.com/api>";
ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
// Response Body 출력
System.out.println(response.getBody());
--------------------------------------------------------------------------------------------
// RestTemplate 생성
RestTemplate restTemplate = new RestTemplate();
// Request Header 설정
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
// Request Body 설정 / 바디 구성 : MultiValueMap
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("redirect_uri", "http://localhost:80/user/kakao-callback");
params.add("code", code);
// 헤더 + 바디 결합
HttpEntity<MultiValueMap<String, String>> reqMsg = new HttpEntity<>(params, headers);
// API 호출
ResponseEntity<OAuthToken> response = rt1.exchange("https://kauth.kakao.com/oauth/token",
HttpMethod.POST, reqMsg, OAuthToken.class);
// Response Body 출력
System.out.println(response.getBody());
'SpringBoot' 카테고리의 다른 글
Springboot - Client IP , x-Forward-For 헤더 (0) | 2024.03.30 |
---|---|
Springboot - @ResponseBody , @RequestBody (0) | 2024.02.06 |
Springboot - @JsonNaming , @JsonProperty json 형식에 코딩 컨벤션의 스네이크 케이스를 카멜 노이션으로 변경하기 (0) | 2024.02.06 |
Springboot - OAuth 2.0(Open Authorization) 인증 방식의 이해 (1) | 2024.02.06 |
Springboot - Gmail의 STMP서버활용 이메일 전송 (0) | 2024.02.06 |