RestTemplate · WebClient · FeignClient 비교 (OAuth 로그인 예시로)

허진혁·2023년 10월 29일
1

givemeticon 프로젝트

목록 보기
6/10
post-thumbnail

📚 글을 작성한 이유

OAuth 로그인을 구현하기 위해 스프링에서 HTTP 통신을 위해 RedisTemplate을 사용했었는데 다른 방식도 존재함을 인지하게 되어, 블로그에 정리하려 해요. 이 글은 RestTemplate, WebClient 및 FeignClient를 다룰 거에요. 각 라이브러리의 사용법, 장점 그리고 어떤 상황에서 어떤 라이브러리를 선택해야 하는지에 대한 나만의 근거를 만드는 것이 목표에요.

1️⃣ ResTemplate

RestTemplate이란?

스프링 공식문서는 다음과 같이 설명해요.

RestTemplate is a synchronous client to perform HTTP requests. It is the original Spring REST client and exposes a simple, template-method API over underlying HTTP client libraries.

RestTemplate은 스프링 프레임워크의 일부로서, RESTful 웹 서비스와 통신하기 위한 편리한 클라이언트 라이브러리에요. RestTemplate을 사용하면 HTTP 요청을 쉽게 생성하고 서버로 보낼 수 있으며, HTTP 응답을 처리하는 기능으로 활용할 수 있어요.

RestTemplate은 요청과 응답을 Java 객체로 매핑하기 위한 다양한 컨버터를 제공하므로 JSON, XML 등 다양한 데이터 형식을 처리할 수도 있어요.

RestTemplate은 동기식으로 동작하기 때문에, 대량의 요청을 처리할 때는 성능 이슈가 발생할 수 있어요. 하지만 스프링 4부터 AsyncRestTemplate을 지원해주기 때문에 비동기로 처리할 수도 있게 되었어요.

RestTemplate을 사용한 OAuth 로그인 예시

RestTemplateConfig 설정하기

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

토큰 얻기 위한 http 요청 보내기

restTemplate을 통해 요청을 보낸다는 것은 다음과 같은 내용을 포함하면 되요. (당연한 내용이지만)

  • URL
  • HttpHeader
  • HttpResponse
public String requestAccessToken(OAuthLoginParams params) {
        String url = authUrl + "/oauth2.0/token";
        HttpEntity<MultiValueMap<String, String>> request = generateHttpRequest(params);

        NaverToken naverToken = restTemplate.postForObject(url,
																													request,
																													NaverToken.class);

        Objects.requireNonNull(naverToken);
        return naverToken.accessToken();
    }

private HttpEntity<MultiValueMap<String, String>> generateHttpRequest(OAuthLoginParams params) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> body = params.makeBody();

        body.add("grant_type", OAuthConstant.GRANT_TYPE);
        body.add("client_id", clientId);
        body.add("client_secret", clientSecret);
        return new HttpEntity<>(body, httpHeaders);
    }

🧐 post 요청에 대한 메서드의 특징들

  1. postForObject()

    • 반환값: 이 메서드는 서버의 응답을 자바 객체로 직접 매핑해요. 즉, 서버에서 받은 응답을 지정된 클래스의 객체로 변환하는 작업을 하게 되는 것이죠.
    • 특징: 서버의 응답을 기대하는 객체의 형식으로 바로 변환하여 반환한다는 점을 활용해서 우리가 만든 객체를 반환하도록 할 수 있어요. 서버 응답을 특정 클래스 타입으로 예상하고, 해당 클래스의 인스턴스로 받는 것이죠.
  2. postForEntity()

    • 반환값: 이 메서드는 ResponseEntity 객체를 반환합니다. ResponseEntity는 HTTP 응답의 모든 정보를 포함하며, 이 정보와 응답 본문을 함께 다룰 수 있어요.
    • 특징:HTTP 응답의 상태 코드, 헤더, 본문 등에 대한 제어가 가능하게 되요.
    ResponseEntity<NaverToken> response = restTemplate.postForEntity(url,
    																  request,
    															      NaverToken.class);
  3. exchange()

    • 반환값: 이 메서드는 ResponseEntity를 반환하며, exchange 메서드를 통해 HTTP 요청 메서드 (GET, POST, PUT, DELETE 등)와 다양한 HTTP 헤더를 설정할 수 있어요.
    • 특징: exchange 메서드는 가장 유연한 방법으로 HTTP 요청을 수행할 수 있습니다. 요청 메서드, 헤더, 요청 본문, 그리고 기대하는 응답 형식을 모두 자유롭게 설정이 가능해요.
HttpEntity<NaverToken> requestEntity = new HttpEntity<>(request, headers);
ResponseEntity<NaverToken> response = restTemplate.exchange(url,
															request, 
															NaverToken.class);

요약하면, postForObject는 응답을 객체로 변환하는 간단한 방법을 제공하고, postForEntity는 HTTP 응답의 상세 정보를 처리하는 데 유용해요. exchange는 가장 유연한 방법으로 HTTP 요청을 수행하며, 요청 및 응답 처리를 완전히 제어할 수 있게 되는 것이죠.

RestTemplate의 장점

  1. Spring의 통합: RestTemplate은 스프링의 일부로서 자연스럽게 스프링 애플리케이션과 통합됩니다. 스프링의 다른 기능과 잘 작동하며, 설정 및 관리가 상대적으로 간단해요.
  2. 일반적인 사용 사례에 적합: 간단한 HTTP 통신을 위한 경우나, 이미 기존에 RestTemplate을 사용 중인 경우에는 추가적인 러닝 커브 없이 계속 사용할 수 있어요.
  3. 모듈화 및 확장성: RestTemplate은 다양한 확장 모듈을 제공하며, 커스터마이징 가능합니다. Interceptor, MessageConverter 등을 쉽게 추가하거나 구성할 수 있어요.
  4. Synchronous API: RestTemplate은 동기적 API로 동작하므로 비동기 처리를 고려하지 않고 싶을 때 사용할 수 있어요.

2️⃣ WebClient

WebClient이란?

스프링 공식문서는 다음과 같이 설명해요.

WebClient is a non-blocking, reactive client to perform HTTP requests. It was introduced in 5.0 and offers a modern alternative to the RestTemplate, with efficient support for both synchronous and asynchronous, as well as streaming scenarios.

In contrast to RestTemplateWebClient supports the following:

  • Non-blocking I/O.
  • Reactive Streams back pressure.
  • High concurrency with fewer hardware resources.
  • Functional-style, fluent API that takes advantage of Java 8 lambdas.
  • Synchronous and asynchronous interactions.
  • Streaming up to or streaming down from a server.

WebClient는 스프링 5부터 도입된 비동기 및 함수형 방식의 HTTP 클라이언트 라이브러리에요. WebClient는 네트워크 통신 작업을 비동기적으로 처리하고, 리액티브 프로그래밍 모델을 따르고 있어요. 이것은 스프링의 리액티브 프레임워크인 스프링 웹 플럭스와 함께 사용되어 비동기적이고 확장 가능한 웹 애플리케이션을 구축하는 데 적합해요.

WebClient을 사용한 OAuth 로그인 예시

의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-webflux'

토큰 얻기 위한 http 요청 보내기

public Mono<String> requestAccessToken(OAuthLoginParams params) {
        String url = authUrl + "/oauth2.0/token";

        return generateHttpRequest(params)
            .flatMap(request -> webClient.post()
                .uri(url)
                .body(BodyInserters.fromValue(request))
                .retrieve()
                .bodyToMono(NaverToken.class)
                .map(naverToken -> {
                    Objects.requireNonNull(naverToken);
                    return naverToken.accessToken();
                }));
    }

    private Mono<MultiValueMap<String, String>> generateHttpRequest(OAuthLoginParams params) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> body = params.makeBody();
        body.add("grant_type", OAuthConstant.GRANT_TYPE);
        body.add("client_id", clientId);
        body.add("client_secret", clientSecret);

        return Mono.just(body);
    }

requestAccessToken 메서드 과정을 공부해면,

  1. generateHttpRequest(params):
    • generateHttpRequest 메서드에서는 OAuth 요청을 생성하는 MultiValueMap을 Mono로 반환하는 작업을 해요. (Mono는 비동기 작업을 나타내는 리액티브 형식의 객체에요.)
  2. flatMap(request -> webClient.post().uri(url).body(BodyInserters.fromValue(request)).retrieve().bodyToMono(NaverToken.class)):
    • flatMap은 Mono의 값을 가져와서 다른 Mono나 Publisher로 변환해요. 여기서는 generateHttpRequest가 반환한 Mono<MultiValueMap<String, String>>의 값을 가져오도록 해두었어요.
    • webClient.post()는 WebClient로 POST요청을 보낼 거에요.
    • .uri(url)는 요청을 보낼 URI를 설정할 거에요.
    • .body(BodyInserters.fromValue(request))는 요청 본문을 설정해요. requestgenerateHttpRequest에서 반환한 MultiValueMap이며, BodyInserters.fromValue를 사용하여 요청 본문으로 변환해요.
    • .retrieve()는 서버로 요청을 보내고 응답을 가져오는 단계로 WebClient의 논블로킹 API 호출을 시작하게 되요.
    • .bodyToMono(NaverToken.class)는 HTTP 응답 본문을 Mono로 변환하는 단계로 이때 NaverToken.class를 지정하여 응답 본문을 NaverToken 클래스로 매핑하는 거에요.
    • .map(naverToken -> { ... })는 Mono에 대한 매핑 단계로 여기서는 받아온 naverToken 객체가 null인지 확인하고, null이 아닌 경우에만 토큰을 추출하여 반환하기 위해 작성했어요.

🧐 응답 본문에 대한 메서드 두 가지 방법

  1. bodyToMono()
    - bodyToMono() 메서드는 HTTP 응답 본문을 Mono로 변환해요. 이 Mono는 본문 데이터만을 포함하며, HTTP 응답의 상태 코드, 헤더 등에는 관심이 없어요.
    - HTTP 응답에서 본문 데이터를 추출하여 비동기적으로 처리할 때 유용한 것 같아요.
    - 예시: webClient.get().uri(url).retrieve().bodyToMono(String.class)
  2. toEntity()
    - toEntity() 메서드는 HTTP 응답을 ResponseEntity로 변환해요. 이 ResponseEntity는 HTTP 응답의 모든 정보를 포함하며, 상태 코드, 헤더, 본문 데이터를 포함한 것이에요.
    - HTTP 응답의 모든 정보를 포함하고 필요한 경우 상태 코드 및 헤더를 확인할 수 있어요.
    - 예시: webClient.get().uri(url).retrieve().toEntity(NaverToken.class)

WebClient의 장점

  1. 비동기 및 논블로킹: WebClient는 비동기식으로 동작하며, 논블로킹 I/O를 사용하여 병렬 처리 및 높은 확장성을 제공합니다.
  2. 함수형 스타일: WebClient는 함수형 프로그래밍 스타일을 채용하며, 람다 표현식을 사용하여 요청 및 응답을 정의할 수 있습니다.
  3. 리액티브 스트림 지원: WebClient는 스프링 5에서 도입된 Project Reactor와 통합되어 있으며, 리액티브 스트림을 통해 비동기 데이터 처리를 제공합니다.
  4. 동기 및 비동기 블록 모드 지원: WebClient는 동기 및 비동기 방식으로 사용할 수 있으므로 기존의 동기식 코드와 함께 사용하기 쉽습니다.

3️⃣ FeignClient

FeignClient이란?

스프링 공식문서는 다음과 같이 설명해요.

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Eureka, Spring Cloud CircuitBreaker, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.

FeignClient는 스프링 클라우드 프레임워크의 일부로, 마이크로서비스 간 통신을 쉽게 구현할 수 있는 라이브러리에요. 주로 마이크로서비스 아키텍처에서 다른 마이크로서비스와의 통신을 위해 사용되며, 스프링 애플리케이션과 통합하여 사용하기 용이해요.

FeignClient는 Netflix에서 개발한 방식인데, 현재는 오픈 소스로 전환되었으며 SpringCloud 프레임워크 프로젝트 중 하나로 들어갔다고 해요.

FeignClient을 사용한 OAuth 로그인 예시

의존성 추가

implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign'

프로퍼티 등록

feign:
  client:
    access-token:
      url: // ...
    user-profile:
      url: // ...

@EnableFeignClients 추가

@SpringBootApplication
@EnableFeignClients
public class GivemeticonApplication {

	public static void main(String[] args) {
		SpringApplication.run(GivemeticonApplication.class, args);
	}

}

AccessTokenFeignClient

@FeignClient(name = "ouath-access-token", url = "${feign.client.access-token.url}")
public interface AccessTokenFeignClient {

    @PostMapping("/login/oauth/access_token")
    String getAccessToken(
            @RequestParam("client_id") String clientId,
            @RequestParam("client_secret") String clientSecret,
            @RequestParam("code") String code
    );
}

FeignClient 는 interface 형식으로 정의하여 REST API 요청을 하는 방식이에요.

이 방식의 과정이 Spring Data JPA 의 JpaRepository를 상속받아 사용하는 interface와 유사한 방식이라고 생각했어요.

정말 간단하게, 파라미터의 @RequestParam 을 통해 RequestBody의 body 를 설정할 수 있어요.

FeignClient의 장점

  1. 선언적 API: RESTful 서비스에 대한 선언적 API를 정의하므로 코드가 간결하고 읽기 쉽습니다. 개발자가 HTTP 요청을 명시적으로 작성할 필요가 없습니다.
  2. 자동화된 클라이언트 생성: FeignClient는 인터페이스를 기반으로 클라이언트 코드를 자동으로 생성하므로 개발자가 클라이언트 코드를 작성할 필요가 없습니다.
  3. 서비스 디스커버리와 로드 밸런싱: 스프링 클라우드와 함께 사용하면 서비스 디스커버리와 로드 밸런싱을 간단히 통합할 수 있습니다.
  4. 인터셉터 및 커스터마이징: FeignClient는 요청 및 응답을 가로채고 수정하는 데 사용할 수 있는 인터셉터를 제공합니다. 이를 통해 보안, 로깅 및 기타 요구사항을 처리할 수 있습니다.
  5. 통합이 쉬움: 스프링 애플리케이션과의 통합이 용이하며, 스프링 부트와 스프링 클라우드와 함께 사용하면 마이크로서비스 아키텍처에서 효과적으로 작동합니다.
  6. 유지보수 용이성: API가 인터페이스로 정의되어 있으므로 API 변경 시 컴파일 오류를 미리 감지할 수 있어 유지보수가 용이합니다.

정리

추천 방법은 사용하는 환경, 프로젝트의 규모 및 복잡성, 개발자의 경험과 선호도 등에 따라 다를 수 있다고 생각해요.

RestTemplate는 간단하고 편리한 접근성을 가지고 있으며,

WebClient는 비동기 및 리액티브 처리를 필요로 하는 경우 유용하며,

FeignClient는 선언적인 방식으로 REST 클라이언트를 작성하고 통신을 자동화하는 데 유리해요.

최신 Spring Framework 버전에서는 WebClient와 OpenFeign을 권장하고 있어요. 또한, 비동기 처리와 리액티브 프로그래밍에 익숙해져 있다면 WebClient를 사용하는 것이 더욱 효과적라고 생각해요. 대량의 요청을 처리하려다 보니 스프링에서 비동기식 방식을 권장하는 것 같아요.

무엇보다도 프로젝트의 특성과 개발자의 편의성을 고려하여 적합한 방법을 선택하는 것이 현명한 선택이라고 될 것이라고 생각해요 !!

참고자료

REST Clients
Spring Cloud OpenFeign
Spring Boot Github 소셜 로그인 구현하기 [ RestTemplate · WebClient · FeignClient 를 비교해보자 ]

profile
Don't ever say it's over if I'm breathing

0개의 댓글