RestTemplate vs WebClient vs RestClient

Soyun_p·2025년 2월 27일
0

📖 지식

목록 보기
4/10
post-thumbnail

이번에 프로젝트에서 맡게된 OAuth를 진행하다가 찾아보게된 RestTemplate vs WebClient vs RestClient!

RestTemplate, WebClient, RestClient 모두 HTTP 클라이언트 역할 을 한다. 세 라이브러리 모두 Spring 어플리케이션에서 HTTP 요청을 보내고 응답을 처리하는 데 사용된다

그래서 OAuthHTTP 요청의 관계가 무엇이냐?!
1. 인증요청: OAuth 프로세스는 클라이언트가 인증 서버에 HTTP GET 또는 POST 요청을 보내는 것으로 시작한다
2. 토큰 교환: 인증 코드를 액세스 토큰으로 교환할 때 HTTP POST 요청이 사용된다
3. API 호출: 액세스 토큰을 사용하여 보호된 리소스에 접근할 때, HTTP 요청의 헤더나 쿼리 파라미터에 토근을 포함시킨다

그럼 이제 정리 시작~ ✨

🪼 RestTemplate

RestTemplate Spring Framework에서 제공하는 HTTP 통신을 위한 템플릿으로, RESTful 웹 서비스를 호출하는데 사용하는 클래스이다

특징
🔹Spring 3.0 부터 지원되는 HTTP 통신 템플릿이다
🔹 RESTful원칙을 준수하여 HTTP 메서드(GET, POST, PUT, DELETE 등)에 적합한 메서드를 제공한다
🔹JSON, XML, String다양한 형식의 응답을 처리할 수 있다
🔹HttpClient를 추상화하여 사용자가 쉽게 REST API를 호출할 수 있도록 한다

장점
🔹 사용하기 간편하고, 복잡한 설정 없이 RESTful 서비스를 호출할 수 있다
🔹 RestTemplate동기적인 HTTP 호출을 수행하므로 호출 결과를 바로 반환받아 처리할 수 있다
🔹 JSON, XML다양한 데이터 포맷을 지원한다

단점
🔹 동기식, 블로킹 방식으로 동작하여 고성능이 요구되는 환경에서는 제한적일 수 있다
🔹 비동기 및 리액티브 프로그래밍 지원이 부족하다
🔹 Spring 5 이후로 유지보수 모드로 전환되어 더 이상의 주요 업데이트가 없다

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class RestTemplateExample {
    
    public void simpleGetRequest() {
        RestTemplate restTemplate = new RestTemplate();
        
        // 1. 단순 GET 요청
        String url = "https://api.example.com/users/1";
        String response = restTemplate.getForObject(url, String.class);
        System.out.println("Response: " + response);
        
        // 2. 응답 엔티티와 함께 GET 요청
        ResponseEntity<User> responseEntity = restTemplate.getForEntity(url, User.class);
        User user = responseEntity.getBody();
        System.out.println("Status Code: " + responseEntity.getStatusCode());
        
        // 3. 헤더를 포함한 요청
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer token123");
        
        HttpEntity<String> entity = new HttpEntity<>(headers);
        ResponseEntity<User> userResponse = restTemplate.exchange(
                url, 
                HttpMethod.GET, 
                entity, 
                User.class);
    }
    
    public void postRequest() {
        RestTemplate restTemplate = new RestTemplate();
        String url = "https://api.example.com/users";
        
        // POST 요청
        User newUser = new User("John", "Doe");
        User createdUser = restTemplate.postForObject(url, newUser, User.class);
    }
}

GET, POST 등의 메서드를 직관적으로 호출
✅ 다양한 메서드 옵션(getForObject, getForEntity 등)
✅ 동기적 블로킹 방식으로 응답을 즉시 반환

🪼 WebClient

Spring 5에서 도입된 비동기적이고 반응형인 HTTP 클라이언트이다

특징
🔹 비동기 및 논블로킹: Spring WebFlux반응형 프로그래밍 모델을 기반으로 구축되어있다
🔹 함수형 프로그래밍 스타일: 유창하고 표현력 있는 API를 제공하여 HTTP 요청을 구성하고 응답을 처리할 수 있다

장점
🔹 Mono, Flux를 활용한 Reactive 프로그래밍이 가능하다
🔹 Spring Security와 연동하여 인증 처리가 가능하다
🔹 유창하고 함수형 스타일의 많은 API를 제공한다
🔹 스트리밍을 지원해 대용량 데이터를 효율적으로 처리할 수 있다

단점
🔹MonoFlux 개념을 이해해야한다
🔹 비동기 및 반응형 코드의 특성상 디버깅이 더 복잡할 수 있다
🔹 web-flux 의존성을 주입해야하는 번거로움이 있다

import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class WebClientExample {
    
    public void simpleGetRequest() {
        WebClient webClient = WebClient.create("https://api.example.com");
        
        // 1. 단순 GET 요청
        Mono<User> userMono = webClient.get()
                .uri("/users/{id}", 1)
                .retrieve()
                .bodyToMono(User.class);
        
        // 구독하여 결과 처리
        userMono.subscribe(user -> 
            System.out.println("User name: " + user.getName())
        );
        
        // 2. 헤더를 포함한 요청
        Mono<User> userWithAuthMono = webClient.get()
                .uri("/users/{id}", 1)
                .header("Authorization", "Bearer token123")
                .retrieve()
                .bodyToMono(User.class);
    }
    
    public void postRequest() {
        WebClient webClient = WebClient.create("https://api.example.com");
        
        // POST 요청
        User newUser = new User("John", "Doe");
        
        Mono<User> createdUserMono = webClient.post()
                .uri("/users")
                .bodyValue(newUser)
                .retrieve()
                .bodyToMono(User.class);
    }
    
    public void handleErrors() {
        WebClient webClient = WebClient.create("https://api.example.com");
        
        Mono<User> userMono = webClient.get()
                .uri("/users/{id}", 999) // 존재하지 않는 사용자
                .retrieve()
                .onStatus(status -> status.is4xxClientError(),
                        response -> Mono.error(new RuntimeException("API not found")))
                .bodyToMono(User.class);
    }
}

✅ 체이닝 빙식의 API 제공
MonoFlux를 통한 비동기 식 리액티브 프로그래밍
subscribe() 메서드를 통해 결과 처리
✅ 오류 처리를 위한 onStatus 메서드 제공

🪼 RestClient

Spring Framework 6.1에서 도입된 새로운 동기식 HTTP 클라이언트이다

특징
🔹 현대적이고 유창한(fluent) API를 제공한다
🔹 동기식 블로킹 방식을 기본으로 하되, 필요시 비동기 호출도 지원한다
🔹 RestTemplate의 인프라를 기반으로 한 추상화 계층을 제공한다
🔹 HTTP 라이브러리에 대한 추상화를 제공한다
🔹 Java 객체를 HTTP 요청으로, HTTP 응답을 Java 객체로 변환하는 편리한 기능을 제공한다

장점
🔹 유창한 API를 통해 코드 가독성작성 용이성을 향상시킨다
🔹 기본 URI, 경로 변수, 요청 헤더 등을 쉽게 작성할 수 있다
🔹 Spring Framework의 다른 기능들과 잘 통합된다

단점
🔹비교적 최신 기술로 커뮤니티 지원과 예제가 아직 충분하지 않다
🔹Spring 6.1 이상의 최신 버전에서만 사용 가능하다
🔹WebClient의 완전한 리액티브 기능에 비해 제한적인 비동기를 지원한다

import org.springframework.web.client.RestClient;

public class RestClientExample {
    
    public void simpleGetRequest() {
        RestClient restClient = RestClient.create("https://api.example.com");
        
        // 1. 단순 GET 요청
        User user = restClient.get()
                .uri("/users/{id}", 1)
                .retrieve()
                .body(User.class);
        
        // 2. 헤더를 포함한 요청
        User userWithAuth = restClient.get()
                .uri("/users/{id}", 1)
                .header("Authorization", "Bearer token123")
                .retrieve()
                .body(User.class);
    }
    
    public void postRequest() {
        RestClient restClient = RestClient.create("https://api.example.com");
        
        // POST 요청
        User newUser = new User("John", "Doe");
        
        User createdUser = restClient.post()
                .uri("/users")
                .body(newUser)
                .retrieve()
                .body(User.class);
    }
    
    public void handleErrors() {
        RestClient restClient = RestClient.create("https://api.example.com");
        
        try {
            User user = restClient.get()
                    .uri("/users/{id}", 999) // 존재하지 않는 사용자
                    .retrieve()
                    .onStatus(status -> status.is4xxClientError(),
                            (request, response) -> { 
                                throw new RuntimeException("API not found"); 
                            })
                    .body(User.class);
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
    
    // 비동기 요청 예제
    public void asyncRequest() {
        RestClient restClient = RestClient.create("https://api.example.com");
        
        // CompletableFuture를 사용한 비동기 요청
        CompletableFuture<User> futureUser = restClient.get()
                .uri("/users/{id}", 1)
                .retrieve()
                .toEntity(User.class)
                .thenApply(responseEntity -> responseEntity.getBody());
        
        // 비동기 요청 결과 처리
        futureUser.thenAccept(user -> 
            System.out.println("User name: " + user.getName())
        );
    }
}

RestTemplateWebClient의 장점을 결합한 현대적 API
✅ 함수형 스타일의 체이닝 방식 제공
✅ 동기식 호출을 기본으로 하면서도 CompletableFuture를 통한 비동기 호출 지원
✅ 오류 처리를 위한 onStatus 메서드 제공

0개의 댓글