Spring

Spring Rest Client

신동편 2023. 7. 25. 13:14
728x90

Rest Client

 

Rest API 서버에 HTTP 요청(GET, POST, PUT, DELETE 등)을 보낼 수 있는 클라이언트 툴 또는 라이브러리이다.

예시로 UI가 갖춰져 있는 Postman을 떠올릴 수 있겠다.

 


RestTemplate

 

Blocking I/O 기반의 Synchronous API이다.

따라서 순차적으로 실행중인 라인이 끝나기 전까지 다음 라인으로 넘어가지 않는다.

 

spring-boot-starter-web 의존성을 등록하면, RestTemplateAutoConfiguration 이 자동 설정된다. 또, RestTemplateBuilder 를 빈으로 자동 등록해준다.

 


WebClient

 

Non-Blocking I/O 기반의 Asynchronous API이다.

 

WebClientAutoConfiguration 이 자동설정되고, spring-boot-starter-web 이 의존성으로 등록되어 있다면 RestTemplateBuilder를 빈으로 등록해준다.

 

WebClient 사용을 위해서는 webflux를 추가한다.

 


RestTemplate / WebClient

 

@RestController
public class SampleController {
	
    @GetMapping("/hello")
    public String hello() throws InterruptedException {
    	Thread.sleep(3000L);
        return "hello";
    }
    
    @GetMapping("/sample")
    public String sample() throws InterruptedException {
    	Thread.sleep(5000L);
        return "sample";
    }
}

@RestController를 구현하고,

각각의 GetMapping은 3초와 5초동안 sleep한다.

 

 

1. RestTemplateBuilder를 주입받아 RestTemplate 생성

  • getForObject로 get요청 보내기
  • blocking i/o : 호출된 함수가 자신의 작업을 모두 마칠 때까지 호출한 함수에게 제어권을 넘겨주지 않고 대기하게 만듦
  • sync : 호출된 함수의 작업 완료 여부를 신경쓰기 때문에 작업을 마칠 때까지 기다린다.
@Component
public class RestRunner implements ApplicationRunner {

	@Autowired
	public RestTemplateBuilder restTemplateBuilder;

	@Override
	public void run(ApplicationArguments args) throws Exception {
          RestTemplate restTemplate = restTemplateBuilder.build();

          StopWatch stopWatch = new StopWatch();
          stopWatch.start();

          String helloResult = restTemplate.getForObject("http://localhost:8080/hello", String.class);

          String sampleResult = restTemplate.getForObject("http://localhost:8080/sample", String.class);

          stopWatch.stop();
          System.out.println(stopWatch.prettyPrint());
    }
}

 

5초 째에 "hello"를 출력하고, 8초 째에 "sample"을 출력한다.

sync-blocking : /hello GET요청이 끝날 때까지 기다리고, /sample GET요청이 끝날 때까지 기다린다.

따라서 두 요청이 모두 끝나려면 8초가 걸린다.

 

 

2. WebClient.Builder를 주입받아 WebClient 생성

 

  • get().uri().retrieve().bodyToMono(String.class)로 GET요청 보내기
  • Stream API를 사용하기 때문에 subscribe()로 결과를 반환해야 한다.
  • non-blocking i/o : 호출된 함수가 바로 결과를 반환하여, 호출한 함수에게 제어권을 바로 넘겨준다.
  • async : 호출된 함수의 작업 완료 여부를 신경쓰지 않기 때문에, 작업 완료 시 호출된 함수는 전달받은 콜백을 실행하기만 한다.
@Component
public class RestRunner implements ApplicationRunner {

    @Autowired
    WebClient.Builder webClientBuilder;


    @Override
    public void run(ApplicationArguments args) throws Exception {
        WebClient webClient = webClientBuilder.build();

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Mono<String> helloResult = webClient.get().uri("http://localhost:8080/hello")
                                        .retrieve()
                                        .bodyToMono(String.class);
        helloResult.subscribe(result -> {
            System.out.println(result);

            if (stopWatch.isRunning()){
                stopWatch.stop();
            }

            System.out.println(stopWatch.prettyPrint());
            stopWatch.start();
        });

        Mono<String> worldResult =  webClient.get().uri("http://localhost:8080/sample")
                                        .retrieve()
                                        .bodyToMono(String.class);
        worldResult.subscribe(result -> {
            System.out.println(result);

            if (stopWatch.isRunning()){
                stopWatch.stop();
            }

            System.out.println(stopWatch.prettyPrint());
            stopWatch.start();
        });
    }
}

 

3초 째에 "hello"를 출력하고, 5초 째에 "sample"을 출력한다.

async-nonblocking : /hello GET요청과 /sample GET요청이 병렬적으로 수행된다.

따라서 두 요청이 모두 끝나려면 5초가 걸린다.

 


Customizing

 

1. RestTemplate

 

RestTemplateBuilder 빈 재정의

  • org.apache.httpcomponents:httpclient 의존성 추가 필요
  • 마찬가지로 ApplicationContext에서 전역적으로 정의
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args);
    }

    @Bean
    public RestTemplateCustomizer restTemplateCustomizer() {
        return new RestTemplateCustomizer() {
            @Override
            public void customize(RestTemplate restTemplate) {
                restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
            }
        };
    }
}

 

 

2. WebClient

 

webClientBuilder.build()로 빌드하기 전에 필요한 설정 가능

  • baseUrl("http://localhost:8080")
  • defaultCookie( )
  • defaultHeader( )

글로벌 WebClient 객체 사용하기

  • WebClientCustomizer 빈 재정의
  • ApplicationContext에서 전역적으로 적용한다.
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args);
    }

    @Bean
    public WebClientCustomizer webClientCustomizer(){
        return new WebClientCustomizer() {
            @Override
            public void customize(WebClient.Builder webClientBuilder) {
                webClientBuilder.baseUrl("http://localhost:8080");
            }
        };
    }
}
728x90