서버 간의 통신
다른 서버가 API를 호출해서 사용할 수 있게 구성되어 웹 요청을 보내고 응답 받을 수 있게 도와주는 "RestTemplate"에 대해 알아보겠습니다.
RestTemplate
- 스프링에서 HTTP 통신 기능을 손쉽게 사용하도록 설계된 템플릿이다.
- HTTP 서버와의 통신을 단순화한 해당 템플릿을 사용하면 RESTful 원칙을 따르는 서비스를 편리하게 만들 수 있다.
- 기본적으로 동기 방식으로 처리되며, 비동식 방식으로 사용하고 싶은 경우 AsyncRestTemplate을 사용하면 된다.
- 다만 현업에서는 많이 쓰이나 지원 중단된 상태라서 WebClient 방식도 함께 알아둘 것을 권장한다.
RestTemplate의 특징
- HTTP 프로토콜의 메서드에 맞는 여러 메서드를 제공한다
- RESTful 형식을 갖춘 템플릿이다
- HTTP 요청 후에 JSON, XML, 문자열 등의 다양한 형식으로 응답을 받을 수 있다.
- 블로킹(blocking) I/O 기반의 동기 방식을 사용한다.
- 다른 API를 호출할 때 HTTP 헤더에 다양한 값을 설정할 수 있다.
RestTemplate의 동작 원리
- 애플리케이션
- 우리가 직접 작성하는 애플리케이션 코드 구현부를 의미한다.
- 애플리케이션에서는 RestTemplate을 선언하고 URI 와 HTTP 메서드, Body 등을 설정한다.
- RestTemplate
- 외부 API로 요청을 보내게 되면 HttpMessageConverter 를 통해 RequestEntity를 요청 메시지로 변환한다.
- 변환된 요청 메시지를 ClientHttpRequestFactory를 통해 ClientHttpRequest로 가져온 후 외부 API로 요청을 보낸다.
- 외부에서 요청에 대한 응답을 받으면 ResponseErrorHandler로 오류를 확인하고, 오류가 발생하면 ClientHttpResponse 에서 응답 데이터를 처리한다.
- 응답 데이터가 정상적이라면 다시 한 번 HttpMessageConverter를 거쳐 자바 객체로 변환 후 애플리케이션으로 반환한다.
RestTemplate 제공 메서드
RestTemplate 예시
@Controller
@RequestMapping("/api/v1/crud-api")
public class CrudController {
@GetMapping
public String getName() {
return "crud-api";
}
@GetMapping(value = "/{variable}")
public String getVariable(@PathVariable String variable) {
return variable;
}
@GetMapping("/param")
public String getNameWithParam(@RequestParam String name) {
return "Hello" + name + "!";
}
@PostMapping
public ResponseEntity<MemberDto> getMember(
@RequestBody MemberDto request,
@RequestParam String name,
@RequestParam String email,
@RequestParam String organization
) {
System.out.println(request.getName());
System.out.println(request.getEmail());
System.out.println(request.getOrganization());
MemberDto memberDto = new MemberDto();
memberDto.setName(name);
memberDto.setEmail(email);
memberDto.setOrganization(organization);
return ResponseEntity.status(HttpStatus.OK).body(memberDto);
}
@PostMapping("/add-header")
public ResponseEntity<MemberDto> addHeader(
@RequestHeader("my-header") String header,
@RequestBody MemberDto memberDto
) {
System.out.println(header);
return ResponseEntity.status(HttpStatus.OK).body(memberDto);
}
}
RestTemplate 구성
- RestTemplate은 별도의 유틸리티 클래스로 생성하거나 서비스 또는 비즈니스 계층으로 구현된다.
- 서버 프로젝트에 요청을 날리기 위해 서버의 역할을 수행하면서 다른 서버로 요청을 보내는 클라이언트 역할도 수행한다.
- 클라이언트는 서버를 대상으로 요청을 보내고 응답을 받는 역할을 한다.
GET 형식의 RestTemplate
@Service
public class RestTemplateService {
public String getName() {
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api")
.encode()
.build()
.toUri();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
return responseEntity.getBody();
}
public String getNameWithPathVariable() {
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api/{name}")
.encode()
.build()
.expand("test") //복수의 값을 넣어할 경우 (,) 추가하여 구분한다.
.toUri();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
return responseEntity.getBody();
}
public String getNameWithPathParameter() {
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api/param")
.queryParam("name", "test")
.encode()
.build()
.toUri();
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
return responseEntity.getBody();
}
}
- RestTemplate를 생성하고 사용하는 가장 보편적인 방법은 UriComponentsBuilder를 사용하는 방법이다.
- UriComponentsBuilder
- 스프링 프레임워크에서 제공하는 클래스로 여러 파라미터를 연결해서 URI 형식으로 만드는 기능을 수행한다.
- UriComponentsBuilder는 빌더 형식으로 객체를 생성한다.
- fromUriString( ) 메서드에서는 호출부의 URL을 입력한다.
- path( ) 메서드에 세부 경로를 입력한다.
- encode( ) 메서드는 인코딩 문자셋을 설정할 수 있는데, 인자를 전달하지 못하면 기본적으로 UTF-8 형식으로 실행된다.
- getForEntity( ) 에 URI와 응답받는 타입을 매개변수로 사용한다.
POST 형식의 RestTemplate
@Service
public class RestTemplateService {
public ResponseEntity<MemberDto> postWithParamAndBody() {
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/v1/crud-api")
.queryParam("name", "test")
.queryParam("email", "test@gmail.com")
.queryParam("organization", "notebook")
.encode()
.build()
.toUri();
MemberDto memberDto = new MemberDto();
memberDto.setName("test!!");
memberDto.setEmail("test@gmail.com");
memberDto.setOrganization("studio");
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<MemberDto> responseEntity
= restTemplate.postForEntity(uri, memberDto, MemberDto.class);
return responseEntity;
}
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("test");
memberDto.setEmail("test@gmail.com");
memberDto.setOrganization("studio");
RequestEntity<MemberDto> requestEntity = RequestEntity
.post(uri)
.header("my-header", "API")
.body(memberDto);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<MemberDto> responseEntity
= restTemplate.exchange(responseEntity, MemberDto.class);
return responseEntity;
}
}
- POST 형식으로 외부 API에 요청하는 방법
- 파라미터에 값을 추가하는 작업과 RequestBody에 값을 담는 작업이 있다.
- RequestBody에 값을 담기 위해서는 데이터 객체를 생성한다.
- postForEntity( ) 메서드를 사용하는 경우에는 파라미터로 데이터 객체를 넣으면 된다.
- 대부분 외부 API는 토큰키를 받아 서비스 접근을 인증하는 방식으로 작동되는데, 이 때 토큰 값을 헤더에 담아 전달하는 방식으로 사용된다.
- 헤더를 설정하기 위해서는 RequestEntity를 정의해서 사용하는 방법이 가장 편한 방법이다.
- RequestEntity 를 생성하고 post( ) 메서드로 URI를 설정한 후에 header( ) 메서드에서 헤더의 키 이름과 값을 설정하면 된다.
- RequestEntity 설정에서 post( ) 메서드 대신에 다른 형식의 메서드로 정의만 하면 exchange( ) 메서드로 쉽게 사용이 가능하다.
RestTemplate 커스텀 설정
- RestTemplate은 HTTPClient를 추상화한다.
- HttpClient의 종류에 따라 기능에 차이가 있는데 가장 큰 차이는 커넥션 풀(Connection Pool) 이다.
- RestTemplate은 기본적으로 커넥션 풀을 지원하지 않는다.
- 지원하지 않으면 매번 호출할 때마다 포트를 열어 커넥션을 생성하게 된다.
- TIME_WAIT 상태가 된 소켓을 다시 사용하려고 하면 재사용하지 못하게 된다.
- 해당 기능을 활성화하는 가장 대표적인 방법은 아파치에서 제공하는 HttpClient로 대체하는 방법이다.
의존성 추가(Maven -> pom.xml)
<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);
return new RestTemplate(factory);
}
반응형
'Book > 스프링부트 핵심가이드' 카테고리의 다른 글
스프링부트를 이용한 서버 간의 통신(of. WebClient) (0) | 2023.12.02 |
---|---|
스프링부트 액추에이터 기능 (0) | 2023.11.30 |
스프링부트 액추에이터의 기본 개념 (0) | 2023.11.28 |
댓글