상세 컨텐츠

본문 제목

[스프링 3팀] 12장. 서버 간 통신

24-25/Spring 3

by Igumi 2025. 1. 24. 10:00

본문

728x90

12. 서버 간 통신

최근에 개발되는 서비스들은 마이크로서비스 아키텍처(MSA)를 주로 채택하고 있습니다. 각 애플리케이션은 자신이 가진 기능을 API로 외부에 노출하고, 다른 서버가 그러한 API를 호출해서 사용할 수 있게 구성되므로 각 서버가 다른 서버의 클라이언트가 되는 경우도 많습니다. 이번 장에서는 이러한 트렌드에 맞춰 다른 서버로 웹 요청을 보내고 응답을 받을 수 있게 도와주는 RestTemplateWebClient에 대해 살펴보겠습니다.

 

12.1 RestTemplate이란?

RestTemplate은 스프링에서 HTTP 통신 기능을 손쉽게 사용하도록 설계된 템플릿입니다. 이 템플릿을 사용하면 RESTful 원칙을 따르는 서비스를 편리하게 만들 수 있습니다. 기본적으로 동기 방식으로 처리되며, 비동기 방식으로 사용하고 싶을 경우 AsyncRestTemplate을 사용하면 됩니다. 다만 RestTemplate은 현업에서는 많이 쓰이나 지원 중단된 상태라서 향후 빈번하게 쓰이게 될 WebClient 방식도 함께 알아둘 것을 권장합니다. 

📌 RestTemplate 특징
▪ HTTP 프로토콜의 메서드에 맞는 여러 메서드 제공
▪ RESTful 형식을 갖춘 템플릿
▪ HTTP 요청 후 JSON, XML, 문자열 등의 다양한 형식으로 응답 받을 수 있음
▪ 블로킹(blocking) I/O 기반의 동기 방식 사용
▪ 다른 API를 호출할 때 HTTP 헤더에 다양한 값을 설정할 수 있음

12.1.1 RestTemplate의 동작 원리

1. 우리가 직접 작성하는 애플리케이션 코드 구현부에서 RestTemplate을 선언하고 URL와 HTTP 메서드, Body 등을 설정2. 외부 API로 요청을 보내게 되면 RestTemplate에서 HttpMessageConverter를 통해 RequestEntity를 요청 메시지로 변환3. RestTemplate에서는 변환된 요청 메시지를 ClientHttpRequestFactory를 통해 ClientHttpRequest로 가져온 후 외부 API로 요청을 보냄

4. 외부에서 요청에 대한 응답을 받으면 RestTemplate은 ResponseErrorHandler로 오류를 확인하고, 오류가 있다면 ClientHttpResponse에서 응답 데이터를 처리함

5. 받은 응답 데이터가 정상적이라면 다시 한번 HttpMessageConverter를 거쳐 자바 객체로 변환해서 애플리케이션으로 반환

12.1.2 RestTemplate의 대표적인 메서드

RestTemplate은 getForObject, getForEntity, postForLocation, postForObject, delete, put, patchForObject, optionsForAllow, exchange, execute 등 편리하게 외부 API로 요청을 보낼 수 있도록 다양한 메서드를 제공합니다. 

12.2 RestTemplate의 사용하기

요청을 보낼 서버 용도로 별도의 프로젝트를 하나 생성하고 다른 프로젝트에서 RestTemplate을 통해 요청을 보내는 방식으로 실습을 진행했습니다. 

12.2.1 서버 프로젝트 생성하기

 RestTemplate의 동작을 확인하기 위한 서버 용도의 프로젝트를 생성합니다.  한 컴퓨터 안에서 두 개의 프로젝트를 가동시켜야 하기 때문에 톰캣의 포트를 변경합니다. 프로젝트에는 spring-boot-starter-web 모듈만 의존성으로 추가하고, serverBox라는 이름으로 프로젝트를 생성합니다. 그리고 애플리케이션이 가동되는 톰캣의 포트를 변경하기 위해 application.properties 파일에 server.port 속성을 추가합니다.

server.port = 9090

 

컨트롤러에는 GET과 POST 메서드 형식의 요청을 받기 위한 코드를 구성합니다. 

@RestController
@RequestMapping("/api/v1/crud-api")
public class CrudController {

    // GET 형식의 요청이 들어오는 상황의 케이스 구현
    // 아무 파라미터가 없는 경우
    @GetMapping
    public String getName() {	
        return "Flature";
    }

    // PathVariable을 사용하는 경우
    @GetMapping(value = "/{variable}")
    public String getVariable(@PathVariable String variable) {	
        return variable;
    }

    // RequestParameter를 사용하는 경우
    @GetMapping("/param")
    public String getNameWithParam(@RequestParam String name) {
        return "Hello. " + name + "!";
    }

    // POST 형식의 요청을 받기 위한 두 개의 메서드 구현
    // 예제의 간소화를 위해 요청 파라미터와 요청 바디를 함께 받도록 구현
    @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);
    }

    // 임의의 HTTP 헤더를 받도록 구현
    @PostMapping(value = "/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);
    }
}

 

사용된 MemberDto 객체는 아래와 같습니다. MemberDto 클래스는 name, email, organization 총 3개의 필드를 가지고 있습니다. 

public class MemberDto {

    private String name;
    private String email;
    private String organization;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getOrganization() {
        return organization;
    }

    public void setOrganization(String organization) {
        this.organization = organization;
    }

    @Override
    public String toString() {
        return "MemberDTO{" +
            "name='" + name + '\'' +
            ", email='" + email + '\'' +
            ", organization='" + organization + '\'' +
            '}';
    }

}

 

12.2.2 RestTemplate 구현하기

앞서 생성한 서버 프로젝트에 요청을 날리기 위해 서버의 역할을 수행하면서 다른 서버로 요청을 보내는 클라이언트의 역할도 수행하는 새로운 프로젝트를 생성합니다.

1. RestTemplate을 포함하는 프로젝트 생성
▪ groupId : com.springboot
▪ artifactId : rest
▪ name : rest
▪ Developer Tools : Lombok, Spring Configuration Processor
▪ Web : Spring Web

2. SwaggerConfiguration 클래스 파일과 의존성 추가
▪ RestTemplate은 이미 spring-boot-starter-web 모듈에 포함된 기능 -> pom.xml에 별도로 의존성 추가할 필요 X
▪ 프로젝트 구조로는 클라이언트로부터 요청을 받는 컨트롤러와 RestTemplate을 활용해 다른 서버에 통신 요청을 하는 서비스 계층으로 작성

 

📍GET 형식의 RestTemplate 작성

  • RestTemplate을 생성하고 사용하는 방법 중 가장 보편적인 방법은 UriComponentsBuilder를 사용하는 방법
  • UriComponentsBuilder는 스프링 프레임워크에서 제공하는 클래스로서 여러 파라미터를 연결해서 URI 형식으로 만드는 기능 수행
service/RestTemplateService.java

@Service
public class RestTemplateService {

    public String getName() {	// PathVariable이나 파라미터를 사용하지 않는 호출 방법
        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() { // path() 메서드 내에 입력한 세부 URI 중 중괄호({ }) 부분을 사용해 개발 단계에서 쉽게 이해할 수 있는 변수명을 입력하고 expand() 메서드에서는 순서대로 값을 입력
        URI uri = UriComponentsBuilder	// UricomponentsBuilder는 빌더 형식으로 객체를 생성
            .fromUriString("http://localhost:9090") // fromUriString() 메서드에서는 호출부의 URL을 입력하고, 이어서 path() 메서드에 세부 경로를 입력
            .path("/api/v1/crud-api/{name}")
            .encode() // encode() 메서드는 인코딩 문자셋을 설정할 수 있고, 인자를 전달하지 않으면 기본적으로 UTF-8로 코드 실행
            .build() // build() 메서드를 통해 빌더 생성을 종료하고 UriComponents 타입이 리턴됨
            .expand("Flature") // 복수의 값을 넣어야할 경우 , 를 추가하여 구분
            .toUri(); // toUri() 메서드를 통해 URI 타입으로 리턴받음, 만약 URI 객체를 사용하지 않고 String 타입의 URI를 사용한다면 toUriString() 메서드로 대체해서 사용하면 됨

        // 이렇게 생성된 uri는 restTemplate이 외부 API를 요청하는데 사용되며, getForEntity()에 파라미터로 전달됨
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class); // getForEntity()는 URI와 응답받는 타입을 매개변수로 사용

        return responseEntity.getBody();
    }

    public String getNameWithParameter() { // 파라미터로 전달하는 예제
        URI uri = UriComponentsBuilder
            .fromUriString("http://localhost:9090")
            .path("/api/v1/crud-api/param")
            .queryParam("name", "Flature") // queryParam() 메서드를 사용해 (키, 값) 형식으로 파라미터를 추가할 수 있음
            .encode()
            .build()
            .toUri();

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);

        return responseEntity.getBody();
    }

 

📍POST 형식의 RestTemplate 작성

service/RestTemplateService.java

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

    // RequestBody에 값을 담는 작업 수행
    MemberDto memberDto = new MemberDto(); // RequestBody에 값을 담기 위해 데이터 객체를 생성
    memberDto.setName("flature!!");
    memberDto.setEmail("flature@gmail.com");
    memberDto.setOrganization("Around Hub Studio");

    RestTemplate restTemplate = new RestTemplate();
    // postForEntity() 메서드를 사용할 경우에는 파라미터로 데이터 객체를 넣으면 됨
    // postForEntity() 메서드로 서버 프로젝트의 API를 호출하면 서버 프로젝트의 콘솔 로그에는 RequestBody 값이 출력되고 파라미터 값은 결과값으로 리턴됨
    ResponseEntity<MemberDto> responseEntity = restTemplate.postForEntity(uri, memberDto,
        MemberDto.class);

    return responseEntity;
}

// 헤더를 추가하는 예제. 대부분의 외부 API는 토큰 키를 받아 서비스 접근을 인증하는 방식으로 작동하고, 토큰값을 헤더에 담아 전달하는 방식이 가장 많이 사용됨
// 헤더를 설정하기 위해서는 RequestEntity를 정의해서 사용하는 방법이 가장 편한 방법
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를 생성하고 post() 메서드로 URI를 설정한 후 header() 메서드에서 헤더의 키 이름과 값 설정
    RequestEntity<MemberDto> requestEntity = RequestEntity 
        .post(uri)
        .header("my-header", "Wikibooks API")
        .body(memberDTO);

    RestTemplate restTemplate = new RestTemplate();
    // exchange() 메서드는 모든 형식의 HTTP 요청을 생성할 수 있음
    // RequestEntity의 설정에서 post() 메서드 대신 다른 형식의 메서드로 정의만 하면 exchange() 메서드로 쉽게 사용할 수 있기 때문에 대부분 exchange() 메서드를 사용
    ResponseEntity<MemberDto> responseEntity = restTemplate.exchange(requestEntity,
        MemberDto.class);

    return responseEntity;
}

 

📍프로젝트에서 쉽게 API를 호출할 수 있도록 Swagger 설정

pom.xml 파일에 Swagger 의존성을 추가한 후 아래와 같이 Swagger 설정 코드를 작성합니다.

config/SwaggerConfiguration.java

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            .select()
            //.apis(RequestHandlerSelectors.any())
            .apis(RequestHandlerSelectors.basePackage("com.springboot.rest"))
            .paths(PathSelectors.any())
            .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
            .title("Spring Boot Open API Test with Swagger")
            .description("설명 부분")
            .version("1.0.0")
            .build();
    }
}

 

앞에서 작성한 서비스 코드를 연결하는 컨트롤러 코드를 아래와 같이 작성합니다.

controller/RestTemplateController.java

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

    @GetMapping("/path-variable")
    public String getNameWithPathVariable(){
        return restTemplateService.getNameWithPathVariable();
    }

    @GetMapping("/parameter")
    public String getNameWithParameter(){
        return restTemplateService.getNameWithParameter();
    }

    @PostMapping
    public ResponseEntity<MemberDto> postDto(){
        return restTemplateService.postWithParamAndBody();
    }

    @PostMapping("/header")
    public ResponseEntity<MemberDto> postWithHeader(){
        return restTemplateService.postWithHeader();
    }

}

 

여기까지 작성했다면 애플리케이션을 실행하고 postDto() 메서드에 해당하는 POST API를 호출하면 됩니다.

⚠️ 앞서 생성한 2개의 프로젝트가 모두 가동돼 있는 상태에서 진행되어야 합니다.

 

flature!!
flature@gmail.com
Around Hub Studio

 

위의 출력 결과는 서버 프로젝트가 파라미터의 값과 Body 값을 정상적으로 전달받았다는 것을 의미합니다.

12.2.3 RestTemplate 커스텀 설정

RestTemplate은 기본적으로 커넥션 풀을 지원하지 않습니다. 이 기능을 지원하지 않으면 매번 호출할 때마다 포트를 열어 커넥션을 생성하게 되는데, TIME_WAIT 상태가 된 소켓을 다시 사용하려고 접근한다면 재사용하지 못하게 됩니다. 이를 방지하기 위해서는 커넥션 풀 기능을 활성화해서 재사용할 수 있게 하는 것이 좋습니다. 이 기능을 활성화하는 가장 대표적인 방법은 아파치에서 제공하는 HttpClient로 대체해서 사용하는 방식입니다.

 

아파치의 HttpClient를 사용하기 위해 아래와 같이 의존성을 추가합니다. RestTemplate의 설정을 더욱 쉽게 추가하고 변경할 수 있게 됩니다. 

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpClient</artifactId>
</dependency>

 

HttpClient를 생성하는 방법은 두 가지가 있는데 HttpClientBuilder.create() 메서드를 사용하거나 HttpClients.custom() 메서드를 사용하는 것입니다. 

12.3 WebClient란?

최신 버전에서는 RestTemplate이 지원 중단되어 WebClient를 사용할 것을 권고하고 있습니다. 이러한 흐름에 맞춰 현재 빈번히 사용되고 있는 RestTemplate과 앞으로 많이 사용될 WebClient를 모두 알고 있는 것이 좋습니다.

Spring WebFlux는 HTTP 요청을 수행하는 클라이언트로 WebClient를 제공합니다. webClient는 리액터(Reactor) 기반으로 동작하는 API입니다. 리액터 기반이므로 스레드와 동시성 문제를 벗어나 비동기 형식으로 사용할 수 있습니다.

📌 webClient의 특징
 ▪ 논블로킹(Non-Blocking) I/O 지원
▪ 리액티브 스트림(Reactive Streams)의 백 프레셔(Back Pressure) 지원
▪ 적은 하드웨어 리소스로 동시성 지원
▪ 함수형 API 지원
▪ 동기, 비동기 상호작용 지원
▪ 스트리밍 지원

12.3.1 WebClient 구성

webClient를 사용하려면 WebFlux 모듈에 대한 의존성을 추가해야 합니다. 아래와 같이 pom.xml 파일에 의존성을 추가합니다.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

12.4 WebClient 구현

WebClient를 생성하는 방법은 크게 두 가지입니다. 

    ▪ create() 메서드를 이용한 생성
    ▪ builder()를 이용한 생성

 

📍앞서 생성한 서버 프로젝트의 GET 메서드 컨트롤러에 접근할 수 있는 WebClient 생성

  • 첫 번째 getName() 메서드는 builder()를 활용해 WebClient를 만들고 다른 두 개의 메서드에서는 create()를 활용해 WebClient를 생성
  • WebClient는 우선 객체를 생성한 후 요청을 전달하는 방식으로 동작
  • 일반적으로 WebClient 객체를 이용할 때는 이처럼 WebClient 객체를 생성한 후 재사용하는 방식으로 구현하는 것이 좋음
service/WebClientService.java

@Service
public class WebClientService {

    public String getName() {
        WebClient webClient = WebClient.builder() // builder()를 통해 
            .baseUrl("http://localhost:9090") // baseUrl() 메서드에서 기본 URL을 설정
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // defaultHeader() 메서드로 헤더의 값을 설정
            .build();

        return webClient.get()
            .uri("/api/v1/crud-api")
            .retrieve()
            .bodyToMono(String.class)
            .block();
    }

    public String getNameWithPathVariable() { // pathVariable 값을 추가해 요청을 보내는 예제
        WebClient webClient = WebClient.create("http://localhost:9090");

        ResponseEntity<String> responseEntity = webClient.get()
            .uri(uriBuilder -> uriBuilder.path("/api/v1/crud-api/{name}") // uri() 메서드 내부에서 uriBuilder를 사용해 path를 설정하고 build() 메서드에 추가값을 넣는 것으로 pathVariable을 추가할 수 있음
                .build("Flature"))
            .retrieve().toEntity(String.class).block(); // toEntity()를 사용하면 ResponseEntity 타입으로 응답을 전달받을 수 있음

        ResponseEntity<String> responseEntity1 = webClient.get()
            .uri("/api/v1/crud-api/{name}", "Flature")
            .retrieve()
            .toEntity(String.class)
            .block();

        return responseEntity.getBody();
    }

    public String getNameWithParameter() { // 쿼리 파라미터를 함께 전달하는 방법 제시
        WebClient webClient = WebClient.create("http://localhost:9090");

        // 쿼리 파라미터를 요청에 담기 위해서는 uriBuilder를 사용하며, queryParam() 메서드를 사용해 전달하려는 값을 설정
        return webClient.get().uri(uriBuilder -> uriBuilder.path("/api/v1/crud-api")
                .queryParam("name", "Flature")
                .build())
            .exchangeToMono(clientResponse -> { // clientResponse 결과값으로 상태 값에 따라 if문 분기를 만들어 상황에 따라 결과값을 다르게 전달할 수 있음
                if (clientResponse.statusCode().equals(HttpStatus.OK)) {
                    return clientResponse.bodyToMono(String.class);
                } else {
                    return clientResponse.createException().flatMap(Mono::error);
                }
            })
            .block();
    }

 

📌 예제에서 소개된 메서드 외 builder()를 사용할 경우 확장할 수 있는 메서드
▪ defaultHeader() : WebClient의 기본 헤더 설정
▪ defaultCookie() : WebClient의 기본 쿠키 설정
▪ defaultUriVariable() : WebClient의 기본 URI 확장값 설정
▪ filter() : WebClient에서 발생하는 요청에 대한 필터 설정

 

일단 빌드된 WebClient는 변경할 수 없습니다. 다만 다음과 같이 복사해서 사용할 수는 있습니다.

WebClient webClient = WebClient.create("http://localhost:9090");
WebClient clone = webClient.mutate().build();

 

다시 코드로 돌아가보면,

  • WebClient는 HTTP 메서드를 get(), post(), put(), delete() 등의 네이밍이 명확한 메서드로 설정 가능
  • URI를 확장하는 방법으로 uri() 메서드를 사용 가능
  • retrieve() 메소드는 요청에 대한 응답을 받았을 때 그 값을 추출하는 방법 중 하나
  • retrieve() 메서드는 bodyToMono() 메서드를 통해 리턴 타입을 설정해서 문자열 객체를 받아오게 되어있음
  • WebClient는 기본적으로 논블로킹(Non-Blocking) 방식으로 동작하기 때문에 기존에 사용하던 코드의 구조를 블로킹 구조로 바꿔줄 필요가 있음
  • 위의 예제에서는 block() 메서드를 추가하여 블로킹 형식으로 동작하게끔 설정

📍POST 요청 예제

service/WebClientService.java

public ResponseEntity<MemberDto> postWithParamAndBody() {
    WebClient webClient = WebClient.builder()
        .baseUrl("http://localhost:9090")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .build();

    MemberDto memberDTO = new MemberDto();
    memberDTO.setName("flature!!");
    memberDTO.setEmail("flature@gmail.com");
    memberDTO.setOrganization("Around Hub Studio");

    return webClient.post().uri(uriBuilder -> uriBuilder.path("/api/v1/crud-api")
            .queryParam("name", "Flature")
            .queryParam("email", "flature@wikibooks.co.kr")
            .queryParam("organization", "Wikibooks")
            .build())
        .bodyValue(memberDTO)
        .retrieve()
        .toEntity(MemberDto.class)
        .block();
}

public ResponseEntity<MemberDto> postWithHeader() {
    WebClient webClient = WebClient.builder()
        .baseUrl("http://localhost:9090")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .build();

    MemberDto memberDTO = new MemberDto();
    memberDTO.setName("flature!!");
    memberDTO.setEmail("flature@gmail.com");
    memberDTO.setOrganization("Around Hub Studio");

    return webClient
        .post()
        .uri(uriBuilder -> uriBuilder.path("/api/v1/crud-api/add-header")
            .build())
        .bodyValue(memberDTO)
        .header("my-header", "Wikibooks API")
        .retrieve()
        .toEntity(MemberDto.class)
        .block();
}

 

  • webClient를 생성하고 사용하는 방법은 위의 GET 요청을 만드는 방법과 다르지 않지만, HTTP 바디 값을 담는 방법과 커스텀 헤더를 추가하는 방법을 눈여겨보자
  • return webClient.post().uri(uriBuilder -> uriBuilder.path("/api/v1/crud-api")에서부터는 webClient에서 post() 메서드를 통해 POST 메서드 통신을 정의
  • uri()는 uriBuilder로 path와 parameter를 설정
  • 그 후 bodyValue() 메서드를 통해 HTTP 바디 값을 설정
  • HTTP 바디에는 일반적으로 데이터 객체(DTO, VO 등)을 파라미터로 전달
  • 아래의 pustWithHeader() 메서드는 POST 요청을 보낼 때 헤더를 추가해서 보내는 예제
  • 전반적인 내용은 동일하며, header() 메서드를 사용해 헤더에 값을 추가
  • 일반적으로 임의로 추가 헤더에는 외부 API를 사용하기 위해 인증된 토큰값을 담아 전달

QUIZ

  1. ____________은 스프링에서 HTTP 통신 기능을 손쉽게 사용하도록 설계된 템플릿이다.
  2. 외부에서 요청에 대한 응답을 받으면 RestTemplate은 ResponseErrorHandler로 오류를 확인하고, 오류가 있다면 ____________ 에서 응답 데이터를 처리한다.
  3. RestTemplate을 생성하고 사용하는 방법 중 가장 보편적인 방법은 ____________ 를 사용하는 방법이다.
  4. ____________ 메서드는 인코딩 문자셋을 설정할 수 있고, 인자를 전달하지 않으면 기본적으로 UTF-8로 코드를 실행한다.
  5. UricomponentsBuilder로 객체를 생성할 때, toUri() 메서드를 통해 URI 타입으로 리턴받게 되는데, 만약 URI 객체를 사용하지 않고 String 타입의 URI를 사용한다면 ____________ 메서드로 대체해서 사용하면 된다.
  6. Spring WebFlux는 HTTP 요청을 수행하는 클라이언트로 ____________ 를 제공한다. 
  7. retrieve() 메서드는 ____________ 메서드를 통해 리턴 타입을 설정해서 문자열 객체를 받아오게 된다.

PROGRAMMING QUIZ

1. GET과 POST 메서드 형식의 요청을 받기 위해 컨트롤러 코드를 작성하려고 합니다. PathVariable을 사용하는 경우 필요한 코드를 완성해보세요.

// 필요한 어노테이션을 넣어 코드 완성
public String getVariable(@PathVariable String variable) {	
   return variable;
}

 

2. 서버 프로젝트의 GET 메서드 컨트롤러에 접근할 수 있는 WebClient를 생성하는 코드를 완성해보세요. (주석 참고)

@Service
public class WebClientService {

    public String getName() {
        // builder()를 통해 
            // 특정 메서드를 사용해 기본 URL을 "http://localhost:9090"로 설정
            // 특정 메서드를 사용해 HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE로 헤더의 값을 설정
            .build();

        return webClient.get()
            .uri("/api/v1/crud-api")
            // 요청에 대한 응답을 받았을 때 그 값을 추출하는 메서드
            // 특정 메서드를 사용해 String 타입으로 객체 받아오기
            .block();
    }

정답

더보기

RestTemplate, ClientHttpResponse, UriComponentsBuilder, encode(), toUriString(), WebClient, bodyToMono()

// PathVariable을 사용하는 경우
    @GetMapping(value = "/{variable}")
    public String getVariable(@PathVariable String variable) {	
        return variable;
    }
@Service
public class WebClientService {

    public String getName() {
        WebClient webClient = WebClient.builder() // builder()를 통해 
            .baseUrl("http://localhost:9090") // baseUrl() 메서드에서 기본 URL을 설정
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // defaultHeader() 메서드로 헤더의 값을 설정
            .build();

        return webClient.get()
            .uri("/api/v1/crud-api")
            .retrieve()
            .bodyToMono(String.class)
            .block();
    }

[출처] 장정우, 「스프링부트 핵심 가이드」 7장

 

Corner Spring 3

ⓒ Nini

728x90

관련글 더보기