Client 입장이 되는 서버와
Server 입장인 서버를 띄워서
RestTemplate를 이용하여
Client에서 Server로 데이터를 요청받는 구조에 대해 학습!
정보 제공: Chat GPT
Spring Framework 에서 제공하는 HTTP 통신을 간편히 처리할 수 있는 클래스이다.
RestTemplate은 클라이언트 측에서 HTTP 요청을 보내고, 응답을 받아오는 기능을 제공한다.
URL , 요청 메서드 , 요청 헤더 , 요청 본문 등을 설정할 수 있다.
또한 응답 데이터를 다양한 방식으로 파싱하고 , 응답 상태 코드 , 헤더 등을 확인할 수 있다.
Spring 5
버전부터는 RestTemplate 대신 WebClient
를 사용하는 것을 권장하고 있다.
이번 포스팅에서는 RestTemplate를 이용해서 학습할 예정이지만,
RestTemplate과 WebClient의 차이점에 대해 알아보자면 다음과 같다.
동기 방식으로 동작.
HTTP 요청을 보내고 해당 응답을 기다린 후 결과를 반환한다.
비동기 방식으로 동작.
요청을 보내고 기다리지 않고 다른 작업을 수행할 수 있다.
응답은 콜백 메서드를 통해 처리된다.
요청이 완료되기를 기다리지 않고
다른 작업을 동시에 처리할 수 있으므로 성능과 확장성 면에서 이점이 있다.
Spring Framework의 일부로 제공되기 때문에 Spring MVC
와 함께 사용된다.
Spring Boot에서는 기본적으로
RestTemplate을 자동 구성하고 제공
한다.
Spring 5부터 도입된 모듈이며, Spring WebFlux
와 함께 사용된다.
Spring MVC와는 다른 리액티브 스택을 사용
하며, 추가 의존성을 선언
해야 한다.
Client 프로젝트와 Server 프로젝트를 각각 생성하고 ,
둘 다 로컬 컴퓨터에서 실행할 것이기 때문에 port 번호를 변경해준다.
ex )
Client server.port = 8080
Server server.port = 8888
port 변경 방법
1. application.properties 파일 오픈
경로 : src/main/resources/application.properties
2. 아래 문장에서 8080 대신 원하는 포트번호를 넣어 작성해주면 된다.
server.port = 8080
프로젝트를 생성할 때 dependency는 Spring Web과 , Lombok 두개면 충분하다.
간단하게 User 클래스의 String name , int age 두 필드만 생성하여 주고받기로 계획.
Client에서는
UserRequestDto
와 UserResponseDto
파일로 요청을 보내고, 응답을 받는다.
학습을 위해 내용은 같지만, Request와 Response 파일을 분리하여 작성!
Server에서는
User
파일로 값을 받아서 요청 받은 user 정보를 JSON 형태로 반환한다.
@Getter
@Setter
@ToString
@Builder
public class UserRequest {
private String name;
private int age;
}
@Getter
@Setter
@ToString
public class UserResponse {
private String name;
private int age;
}
@ToString
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
}
Service 파일에서
RestTemplate를 이용, 서버에 값을 요청하고
서버로 부터 응답을 받아온 값을 출력하는
간단한 코드를 작성해본다.!
@Slf4j
@Service
public class RestTemplateService {
public UserResponse exchange() {
// post 요청시 html header 값 추가
URI uri = UriComponentsBuilder
.fromUriString("http://localhost")
.port(8888)
.path("/api/server/user/{userId}/name/{userName}")
.expand(100, "zhyun") // 차례대로 경로 변수에 매칭됨. map을 사용하는 방법도 좋음
.encode(StandardCharsets.UTF_8)
.build()
.toUri();
log.info("url ==== {}", uri);
// http body -> object -> object mapper -> json -> rest template -> http body json
RequestEntity<UserRequest> requestEntity = RequestEntity
.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization", "abcd")
.header("custom-header", "zzzz")
.body(UserRequest.builder()
.name("zhyun")
.age(123)
.build());
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<UserResponse> response = restTemplate.exchange(requestEntity, UserResponse.class);
return response.getBody();
}
}
URI를 생성하고 조작하는 유틸리티 클래스이다.
URI 스트링을 생성하기 위해
체인 형태
로 메서드를 호출하면서
각 요소를 추가
하거나 변경
할 수 있는 방법을 제공
한다.
.fromUriString()
fromUriString()을 통해서
스키마를 포함한 URL을 직접 입력해 줄 수도 있다.
ex 1.
UriComponentsBuilder.fromUriString("http://localhost:8080/api/user")
ex 2.
UriComponentsBuilder.fromUriString("http://localhost:8080")
ex 3.
UriComponentsBuilder.fromUriString("http://localhost")
.schema()
프로토콜 지정.
"http" , "https" 등등
.host()
리소스가 위치한 서버의 도메인 이름이나 IP 주소를 지정.
"localhost" , "www.naver.com" 등등
.port()
서비스에 접근하기 위해 사용되는 네트워크 포트 번호.
8080 , 80 , 3306 등등
.encode()
특수문자나 한글같은 ASCII 문자가 아닌 것이 들어가는 경우,
인코딩 설정을 필수로 해주어야 한다.
.path()
리소스의 경로.
예를 들면,
URL이 " http://localhost:8080/api/user?name=zh " 일 때
[ /api/user ] 가 리소스 경로이다.
.expand()
path에 경로 변수( PathVariable )이 사용된 경우,
expand() 메서드에 경로 변수에 매핑할 값들을 입력해준다.
ex 1.
UriComponentsBuilder.
.....
.....
.path("/api/user/{userId}/name/{userName}")
.expand(12321, "zhyun")
ex 2.
UriComponentsBuilder.
.....
.....
.path("/api/user/{userId}")
.expand(2)
.queryParam()
URI에 추가 정보(쿼리 파라미터)를 전달하기 위해 사용된다.
UriComponentsBuilder.
.....
.....
.queryParam("키2", "값2")
.queryParam("키1", "값1")
.queryParam("키3", "값3")
.build().toUri()
셋트로 사용!
작성한 메서드를 조합해서 URI 객체를 만들어 반환한다.
주로 RESTful 웹 서비스에서 HTTP 요청을 생성하고 전송하는 데 사용되며
HTTP 요청의 메서드, 헤더, 바디 등을 포함한다.
.get(URI uri)
.post(URI uri)
.put(URI uri)
.patch(URI uri)
.delete(URI uri)
.options(URI uri)
HTTP 메서드.
매개변수로 URI 객체를 받는다.
.contentType(MediaType contentType)
Media Type을 입력해준다.
MediaType객체가 제공되어 옵션을 선택하여 입력해주면 된다.
.header()
header 에 넣어 보낼 키-값 쌍이 있을 때 사용한다.
키-값 쌍이 여러개인 경우
아래와 같이 다양한 방식으로 입력해줄 수 있다.
.body()
요청에 필요한 값을 입력
정말 많은 메서드를 지원한다;
매개변수 타입과 반환 형태를 보고 골라서 사용......
실습하면서 사용한 메서드에 대해서만 알아본다면
HTTP 요청을 보내고 응답을 받는 가장 일반적인 메서드.
매개변수로
RequestEntity
와 , 응답을 받을 클래스
를 받고
ResponseEntity<응답받을클래스>
를 반환한다.
명시된 HTTP 메서드 요청을 보내고, 응답을 받는 메서드이다.
RequestEntity를 입력해주지 않아도,
값을 요청하고 응답받을 수 있다.
매개변수로
URI(URI객체 또는 String)
, DTO객체
, 응답받을 클래스.class
를 입력해주면 된다.
헤더값을 보내지 않을 경우에 사용하는 것으로 배웠다.
ResponseEntity<응답받을클래스>
를 반환한다.
ForEntity()와의 차이점으로는
이 메서드는 응답 본문만 반환
받는다.
매개변수로
URI(URI객체 또는 String)
, 응답받을 클래스.class
를 입력해주면 된다.
ResponseEntity 없이 응답받을클래스
만 반환한다.
json 객체를 응답받을 때는 xxForEntity()
를 사용하고
String 문자열만 있는 응답을 받을 때는 xxForObject()
를 사용하는 것으로 배웠다.
그때 그때 상황에 맞게 사용!
@RestController
@RequestMapping("/api/client")
@RequiredArgsConstructor
public class ApiController {
private final RestTemplateService restTemplateService;
@GetMapping("/user/exchange")
public UserResponse getUserExchange() {
return restTemplateService.exchange();
}
}
@Slf4j
@RestController
@RequestMapping("/api/server")
public class ServerApiController {
@PostMapping("/user/{userId}/name/{userName}")
public ResponseEntity<User> post(
@RequestBody User user,
@PathVariable int userId,
@PathVariable String userName,
@RequestHeader("x-authorization") String authorization,
@RequestHeader("custom-header") String customHeader) {
log.info("userId : {}, userName : {}", userId, userName);
log.info("authorization : {}, custom : {}", authorization, customHeader);
log.info("client req : {}", user.toString());
return ResponseEntity
.status(HttpStatus.OK)
.body(user);
}
}
2023-06-09T03:47:25.473+09:00 INFO 29688 --- [nio-8080-exec-4] c.e.client.service.RestTemplateService : url ==== http://localhost:8888/api/server/user/100/name/zhyun
2023-06-09T03:47:25.479+09:00 INFO 29632 --- [nio-8888-exec-3] c.e.s.controller.ServerApiController : userId : 100, userName : zhyun
2023-06-09T03:47:25.480+09:00 INFO 29632 --- [nio-8888-exec-3] c.e.s.controller.ServerApiController : authorization : abcd, custom : zzzz
2023-06-09T03:47:25.480+09:00 INFO 29632 --- [nio-8888-exec-3] c.e.s.controller.ServerApiController : client req : User(name=zhyun, age=123)