spring boot : RestTemplate

김아무개·2023년 6월 8일
0

Spring Boot 🍃

목록 보기
34/95
post-custom-banner

보고 배운 인강

Client 입장이 되는 서버와
Server 입장인 서버를 띄워서

RestTemplate를 이용하여

Client에서 Server로 데이터를 요청받는 구조에 대해 학습!


RestTemplate

정보 제공: Chat GPT

Spring Framework 에서 제공하는 HTTP 통신을 간편히 처리할 수 있는 클래스이다.

RestTemplate은 클라이언트 측에서 HTTP 요청을 보내고, 응답을 받아오는 기능을 제공한다.


URL , 요청 메서드 , 요청 헤더 , 요청 본문 등을 설정할 수 있다.
또한 응답 데이터를 다양한 방식으로 파싱하고 , 응답 상태 코드 , 헤더 등을 확인할 수 있다.


Spring 5 버전부터는 RestTemplate 대신 WebClient를 사용하는 것을 권장하고 있다.


이번 포스팅에서는 RestTemplate를 이용해서 학습할 예정이지만,

RestTemplate과 WebClient의 차이점에 대해 알아보자면 다음과 같다.

RestTemplate과 WebClient의 차이점

1. 동기 vs 비동기:

RestTemplate

동기 방식으로 동작.
HTTP 요청을 보내고 해당 응답을 기다린 후 결과를 반환한다.

WebClient

비동기 방식으로 동작.
요청을 보내고 기다리지 않고 다른 작업을 수행할 수 있다.
응답은 콜백 메서드를 통해 처리된다.

요청이 완료되기를 기다리지 않고
다른 작업을 동시에 처리할 수 있으므로 성능과 확장성 면에서 이점이 있다.


2. 의존성

RestTemplate

Spring Framework의 일부로 제공되기 때문에 Spring MVC와 함께 사용된다.
Spring Boot에서는 기본적으로 RestTemplate을 자동 구성하고 제공한다.

WebClient

Spring 5부터 도입된 모듈이며, Spring WebFlux와 함께 사용된다.
Spring MVC와는 다른 리액티브 스택을 사용하며, 추가 의존성을 선언해야 한다.



간단 실습

1. Client와 Server 프로젝트 각각 생성

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 두개면 충분하다.


2. Client - Server 주고받을 DTO 설계

간단하게 User 클래스의 String name , int age 두 필드만 생성하여 주고받기로 계획.

Client에서는
UserRequestDtoUserResponseDto 파일로 요청을 보내고, 응답을 받는다.
학습을 위해 내용은 같지만, Request와 Response 파일을 분리하여 작성!

Server에서는
User 파일로 값을 받아서 요청 받은 user 정보를 JSON 형태로 반환한다.

Client 에서 사용할 DTO 작성

dto / UserRequest.java

@Getter
@Setter
@ToString
@Builder
public class UserRequest {

    private String name;
    private int age;

}

dto / UserResponse.java

@Getter
@Setter
@ToString
public class UserResponse {

    private String name;
    private int age;

}

Server 에서 사용할 DTO 작성

dto / User.java

@ToString
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String name;
    private int age;

}

3. Client - service 작성

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();
    }
}

사용된 클래스들 소개

UriComponentsBuilder

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 객체를 만들어 반환한다.


RequestEntity

주로 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()
요청에 필요한 값을 입력


RestTemplate

정말 많은 메서드를 지원한다;
매개변수 타입과 반환 형태를 보고 골라서 사용......

실습하면서 사용한 메서드에 대해서만 알아본다면

exchange()

HTTP 요청을 보내고 응답을 받는 가장 일반적인 메서드.

매개변수로
RequestEntity 와 , 응답을 받을 클래스를 받고

ResponseEntity<응답받을클래스> 를 반환한다.


postForEntity() , getForEntity() , patchForEntity()

명시된 HTTP 메서드 요청을 보내고, 응답을 받는 메서드이다.

RequestEntity를 입력해주지 않아도,
값을 요청하고 응답받을 수 있다.

매개변수로
URI(URI객체 또는 String) , DTO객체 , 응답받을 클래스.class 를 입력해주면 된다.

헤더값을 보내지 않을 경우에 사용하는 것으로 배웠다.

ResponseEntity<응답받을클래스> 를 반환한다.


postForObject(), getForObject(), patchForObject()

ForEntity()와의 차이점으로는
이 메서드는 응답 본문만 반환받는다.

매개변수로
URI(URI객체 또는 String) , 응답받을 클래스.class 를 입력해주면 된다.

ResponseEntity 없이 응답받을클래스 만 반환한다.


json 객체를 응답받을 때는 xxForEntity()를 사용하고
String 문자열만 있는 응답을 받을 때는 xxForObject()를 사용하는 것으로 배웠다.

그때 그때 상황에 맞게 사용!


4. Client - controller 작성

@RestController
@RequestMapping("/api/client")
@RequiredArgsConstructor
public class ApiController {

    private final RestTemplateService restTemplateService;

    @GetMapping("/user/exchange")
    public UserResponse getUserExchange() {
        return restTemplateService.exchange();
    }
}

5. Server - controller 작성

@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);
    }
}

6. 실행 후 로그

request API

Client log

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

Server log

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)
profile
Hello velog! 
post-custom-banner

0개의 댓글