chapter 12 . 서버 간 통신
1. RestTemplate
스프링에서 HTTP통신 기능을 손쉽게 사용하도록 설계된 템플릿 (다른 서버로 요청하고 응답받을 수 있도록 클래스를 제공)
동기방식으로 처리된다.
RestTemplate 현업에서 많이 쓰이나 지원 중단된 상태 - > webClient 로 대체
2. RestTemplate 동작원리
애플리케이션에서 RestTemplate선언, url과 http메서드, body 등을 설정
- 외부 api로 요청을 보내게되면, RestTemplate에서 HttpMessageConverter를 통해 RequestEntity를 요청 메시지로 변환
- RestTemplate에서는 변환된 메시지를 ClientHttpRequstFactory를 통해 ClientHttpRequest로 가져온 후 외부 api로 요청을 보냄
- 외부에서 요청에 대한 응담을 받으면서 RestTemplate은 ResponseErrorHandler로 오류를 확인하고 , 오류가 있다면 ClientHttpResponse에서 응답 데이터를 처리함.
- 받은 응답 데이터가 정상적이라면 다시 한 번 MessageConverter를 거쳐 자바 객체로 변환해서 애플리케이션으로 반환
RestTemplate 대표적인 메서드
메서드 | HTTP형태 | 설명 |
getForObject | GET | GET 형식으로 요청한 결과를 객체로 반환 |
getForEntity | GET | GET 형식으로 요청한 결과를 ResponseEntity 결과로 반환 |
postForLocation | POST | POST형식으로 요청한 결과를 헤더에 저장된 URI로 반환 |
postForObject | POST | POST형식으로 요청한 결과를 객체로 반환 |
postForEntity | POST | POST형식으로 요청한 결과를 ResponseEntity형식으로 반환 |
delete | DELETE | DELETE 형식으로 요청 |
put | PUT | PUT 형식으로 요청 |
patchForObject | PATCH | PATCH 형식으로 요청한 결과를 객체로 반환 |
optionsForAllow | OPTIONS | 해당 URI에서 지원하는 HTTP 메서드를 조회 |
exchange | any | HTTP 헤더를 임의로 추가할 수 있고, 어떤 메서드 형식에서도 사용할 수 있음 |
execute | any | 요청과 응답에 대한 콜백을 수정 |
RestTemplate 적용
GET 형식의 RestTemplate 작성
1) Controller 부분
@RestController
@RequestMapping("/rest-template")
public class RestTemplateController {
private final RestTemplateService restTemplateService;
public RestTemplateController(RestTemplateService restTemplateService){
this.restTemplateService = restTemplateService;
}
// 아무 값을 받지 않는 메서드
@GetMapping
public String getName() {
return restTemplateService.getName();
}
// 이름과 PathValiable 요청받은 메서드
@GetMapping("/path-variable")
public String getNameWithPathVariable(){
return restTemplateService.getNameWithPathVariable();
}
클래스 생성
Service bean 주입
RestTemplateService 의 인스턴스를 주입 받아 RestTemplateController 에서 사용할 수 있도록 함
1. 아무 값을 받지 않는 메서드 생성 getName() : uri객체를 생성
2. Service 부분
@Service
public class RestTemplateService {
public String getName(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090") // 서버주소
.path("/api/v1/crud-api") // 요청할 url
.encode() // 넣는 값이 없음
.build() // build 객체 생성
.toUri(); // uri 객체로 변환하는 부분
// RestTemplate restTemplate = new RestTemplate();
RestTemplate restTemplate = restTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
// getForEntity() : 파라미터로 전달, URI와 응답받는 타입을 매개변수로 사용
return responseEntity.getBody();
}
// UriComponentsBuilder : 여러 컴포넌트를 연결해서 URI 형식으로 만드는 기능을 수행
public String getNameWithPathVariable(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api/{name}")
.encode()
.build()
.expand("Flature")
.toUri();
// RestTemplate restTemplate = new RestTemplate();
RestTemplate restTemplate = restTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
return responseEntity.getBody();
}
RestTemplate를 생성하고 사용하는 방법
: 가장 보편적인 방법은 uriComponentBuilder !!
스프링에서 제공하는 클래스
여러 파라미터를 연결해서 uri형식으로 만드는 기능을 수행
- fromUriString() : 호출부의 URL을 입력하고 이어서 path()에서 세부 경로 입력
- encode() : 인코딩 문자셋을 설정 / 인자를 전달안하면 기본 utf-8
- path() : 메서드 내에 입력한 세부 uri중 중괄호 부분을 사용해 개발 단계에서 쉽게 이해할 수 있는 변수명을 입력
- expend() : 순서대로 값을 입력하면 됨. 많은 경우 "," 활용
- build() : 빌더 생성을 종료
- toUri() : uri타입으로 리턴. 만약 URI객체를 사용하지 않고 String타입의 URI사용한다면 toUriString()메서드로 대체하여 사용
- getForEntity() : 파라미터로 전달, URI와 응답받는 타입을 매개변수로 사용
public String getNameWithParameter(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api/param")
.queryParam("name", "Flature")
.encode()
.build()
.toUri();
// RestTemplate restTemplate = new RestTemplate();
RestTemplate restTemplate = restTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
return responseEntity.getBody();
}
- queryParam() : (키, 값) 형식으로 파라미터를 추가
POST 형식의 RestTemplate 작성
1. Controller
@PostMapping
public ResponseEntity<MemberDto> postDto(){
return restTemplateService.postWithParamAndBody();
}
@PostMapping("/header")
public ResponseEntity<MemberDto> postWithHeader(){
return restTemplateService.postWithHeader();
}
2. Service
public ResponseEntity<MemberDto> postWithParamAndBody(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api")
.queryParam("name", "Flature")
.queryParam("email", "Flature@wikibooks.co.kr")
.queryParam("organization", "Wikibooks")
.encode()
.build()
.toUri();
// 파라미터에 값을 담는 부분
MemberDto memberDto = new MemberDto();
memberDto.setName("flature!!");
memberDto.setEmail("flature@gmail.com");
memberDto.setOrganization("Around Hub Studio");
// body 에 값을 담는 부분
// RestTemplate restTemplate = new RestTemplate();
RestTemplate restTemplate = restTemplate();
ResponseEntity<MemberDto> responseEntity = restTemplate.postForEntity(uri, memberDto, MemberDto.class);
return responseEntity;
}
POST 형식으로
외부 API에 요청할 때 Body 값과 파라미터 값을 담는 방법
- postForEntity() : 서버의 API를 호출하면서 서버 프로젝트 콘솔 로그에는 RequestBody값이 출력, 파라미터 값은 결과값으로 리턴
1. GET 요청 (요청 파라미터 없는 경우) / 두번째 서버에서 Flature를 리턴함.
2. GET 요청 (이름과 PathValiable 요청받은 메서드)
3. POST 요청
@RequestBody MemberDto request,
@RequestBody 에 담은 DTO 객체의 값은 외부 API 로 가정한 서버 프로젝트 콘솔 창에 출력됨
1) Controller
@PostMapping("/header")
public ResponseEntity<MemberDto> postWithHeader(){
return restTemplateService.postWithHeader();
}
2) Service
public ResponseEntity<MemberDto> postWithHeader(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api/add-header")
.encode()
.build()
.toUri();
MemberDto memberDto = new MemberDto();
memberDto.setName("flature");
memberDto.setEmail("flature@wikibooks.co.kr");
memberDto.setOrganization("Around Hub Studio");
RequestEntity<MemberDto> requestEntity = RequestEntity
.post(uri)
.header("my-header", "Wikibooks API") //key: my-header, value: Wikibooks API
.body(memberDto);
//RestTemplate restTemplate = new RestTemplate();
RestTemplate restTemplate = restTemplate();
ResponseEntity<MemberDto> responseEntity = restTemplate.exchange(requestEntity, MemberDto.class);
return responseEntity;
}
- header() : 메서드에서 헤더의 키 이름과 값을 설정하는 코드
- exchange() : 모든 형식의 HTTP요청을 생성할 수 있음.
♥ 헤더를 추가하는 예제
대부분의 외부 API는 토큰 키를 받아 서비스 접근을 인증하는 방식으로 작동함.
이 때 토큰 값을 헤더에 담아 전달하는 방식이 가장 많이 사용됨.
헤더를 설정하기 위해서는 RequestBody를 정의해서 사용하는 방법이 가장 편한 방법.
RequestEntity 를 생성하고 post()메서드로 URI를 설정한 후,
header() 메서드에서 헤더의 키 이름과 값을 설정하는 코드
대체로 서버 프로젝트의 API명세에는 헤더에 필요한 키 값을 요구하면서 키 이름을 함께 제시하기 때문에
그에 맞춰 헤더의 값을 설정하면 됨.
RestTemplate 커스텀 설정
RestTemplate는 HttpClient 를 추상화하고 있음.
HttpClient는 종류에 따라 기능에 차이가 다소 있는데 가장 큰 차이는 Connection Pool
RestTemplate는 Connection Pool을 지원하지 않음.
매번 호출할 때 마다 포트를 열어 커넥션을 생성하게 되는데
TIME_WAIT 상태가 된 소켓은 다시 사용할 수 없음.
따라서 이 기능을 활성화 하는 가장 대표적인 방법은
HttpClient로 대체해서 사용하는 방식
RestTemplate 커스텀 설정
의존성 추가하기
<!--Http Components : restTemplate Custom -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
커스텀 RestTemplate 객체 생성 메서드
public RestTemplate restTemplate(){
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
/*HttpClient Client = HttpClientBuilder.create()
.setMaxConnTotal(500)
.setMaxConnPerRoute(500)
.build();
*/
// 커넥션 객체 자동 반환
CloseableHttpClient httpClient = HttpClients.custom()
.setMaxConnTotal(500)
.setMaxConnPerRoute(500)
.build();
factory.setHttpClient(httpClient);
factory.setConnectTimeout(2000);
factory.setReadTimeout(5000);
RestTemplate restTemplate = new RestTemplate(factory);
return restTemplate;
}
RestTemplate의 생성자를 보면 ClientHttpRequestFactory 를 매개변수로 받는 생성자가 존재함.
->
ClientHttpRequestFactory는 함수형 인터페이스로
대표적인 구현체로서 SimpleClientHttpRequestFactory 와 HttpComponentsClientHttpRequestFactor가 있음.
별도의 구현체를 설정해서 전달하지 않으면 HttpAccessor에 구현돼 있는 내용에 의해 SimpleClientHttpRequestFactory를 사용
HttpComponentsClientHttpRequestFactory를 사용해서
ClientHttpRequestFactory를 사용하면
RestTemplate의 Timeout 설정이 가능.
HttpComponentsClientHttpRequestFactory는 커넥션 풀을 설정하기 위해
HttpClient를 HttpComponentsClientHttpRequestFactory에 설정할 수 있다.
HttpClient를 생성하는 방법은 두가지가 있는데
<!--web flux : Web Client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
'🚀 부트캠프 - PLAYDATA > 📒 수업 내용 정리' 카테고리의 다른 글
작은 쿠버네티스 경험하기 (0) | 2023.10.20 |
---|---|
[Docker] 10/5 이미지파일 생성, 인증키 생성, 도커 볼륨 생성 (0) | 2023.10.09 |
[Springboot]9/6 연관 관계 매핑 (0) | 2023.09.06 |
[SpringBoot] Entity 와 Repository 설계 / Repository 메서드 설계 규칙 (0) | 2023.09.01 |
[Springboot] IntelliJ 프로젝트 생성 / pom.xml 문서 구성 (0) | 2023.08.31 |