본문 바로가기
Programing/Java & Spring

WebFlux, WebClient 사용하여 API 호출 - 1편

by 슈퍼와이비 2023. 9. 26.
반응형

📌 Java8 Reactor 프레임워크

  • Java 8 이전에도 자바에서 리액티브 프로그래밍을 수행할 수 있었지만, Java 8에서는 리액티브 스트림(Reactive Streams)이라는 개념을 표준 라이브러리로 도입했습니다.
  • 리액티브 스트림은 비동기 및 논블로킹 환경에서 데이터 스트림을 처리하는데 사용되는 표준 인터페이스를 정의합니다. 이 인터페이스는 Publisher, Subscriber, Subscription, Processor 등을 포함합니다.
  • Java 8의 Reactor 프레임워크는 이 리액티브 스트림 스펙을 구현하고, 리액티브 프로그래밍을 위한 유용한 도구와 함수를 제공합니다. Reactor는 FluxMono라는 두 가지 핵심 타입을 제공하여 데이터 스트림을 다룰 수 있습니다.

 

📌 Spring WebFlux와 Reactor

  • Spring WebFlux는 Spring Framework의 모듈 중 하나로, 리액티브 프로그래밍을 지원하는데 Reactor를 활용합니다.
  • Spring WebFlux는 백엔드에서 비동기 및 논블로킹 웹 애플리케이션을 개발하는데 사용되며, 데이터 스트림 처리를 위해 Reactor의 FluxMono를 활용합니다.
  • Spring WebFlux는 Java 8의 리액티브 스트림 스펙을 준수하고, 이를 기반으로 구축되었습니다. 따라서 Reactor와 Spring WebFlux를 사용하여 리액티브 애플리케이션을 개발할 수 있습니다.
  • Reactor의 Flux는 다수의 값 또는 이벤트를 처리하기 위해, Mono는 하나의 값 또는 이벤트를 처리하기 위해 사용됩니다. Spring WebFlux는 이러한 타입을 사용하여 웹 요청과 응답을 처리하고, 비동기적으로 데이터를 다루며, 높은 확장성과 성능을 제공합니다.

요약하면, Spring WebFlux는 Java 8의 Reactor 프레임워크를 활용하여 리액티브 웹 애플리케이션을 개발하는데 사용되며, Reactor는 리액티브 스트림 스펙을 구현하는 핵심 라이브러리입니다. 이 두 가지 기술을 함께 사용하여 비동기 및 논블로킹 애플리케이션을 구축할 수 있습니다.

 

 

📌 WebClient

Spring WebFlux는 서버 측 리액티브 프로그래밍을 지원하고, WebClient는 클라이언트 측 리액티브 웹 통신을 위한 도구로 사용됩니다

  • WebClientSpring Framework의 일부로 제공되는 리액티브 웹 클라이언트입니다.
  • 이것은 외부 서비스나 API에 HTTP 요청을 보내고, 비동기 방식으로 응답을 처리하기 위한 도구입니다.
  • WebClientSpring WebFlux의 클라이언트 측 구현으로 사용되며, 리액티브 스트림과 함께 외부 리소스와 통신할 때 유용합니다.
  • 주로 백엔드 서버가 외부 서비스와 통신하거나 데이터를 가져오는 데 사용됩니다.

두 기술은 리액티브 프로그래밍 모델과 비동기 처리를 기반으로 하며, 함께 사용하여 완전한 리액티브 애플리케이션을 개발할 수 있습니다.

 

📌 WebClient 생성

✏️ build.grale 파일에 spring-boot-starter-webflux를 추가합니다.

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.4'
	id 'io.spring.dependency-management' version '1.1.3'
}

group = 'api.study'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'io.projectreactor:reactor-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

 

✏️ API를 호출하기 위해 WebClient를 초기화 하는 방법을 살펴보겠습니다. 

import java.time.Duration;

import javax.net.ssl.SSLException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;

import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;

public class ApiserviceApplication {

	public static void main(String[] args) throws SSLException {
		
		ConnectionProvider provider = ConnectionProvider.builder("custom-provider")
				.maxConnections(20)
				.maxIdleTime(Duration.ofSeconds(58))
				.maxLifeTime(Duration.ofSeconds(58))
				.pendingAcquireTimeout(Duration.ofSeconds(60))
				.pendingAcquireMaxCount(-1)
				.evictInBackground(Duration.ofSeconds(30))
				.lifo()
				.build();
		
		SslContext sslContext = SslContextBuilder
				.forClient()
				.trustManager(InsecureTrustManagerFactory.INSTANCE)
				.build();
		
		WebClient client = WebClient.builder()
				.clientConnector(
						new ReactorClientHttpConnector(
								HttpClient.create(provider)
								.secure(t -> t.sslContext(sslContext))
								.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 20000)
								.doOnConnected(
										conn -> conn.addHandlerLast(new ReadTimeoutHandler(30))  //sec
										.addHandlerLast(new WriteTimeoutHandler(60)) //sec
										)
								.responseTimeout(Duration.ofSeconds(60))
								)
						).build();
		
	}
	
}

 

 

📌 clientConnector 메소드

WebClient의 clientConnector 메서드는 Spring WebFlux에서 비동기 HTTP 클라이언트의 연결 설정을 구성하기 위해 사용됩니다. 이 메서드를 사용하여 커스텀 클라이언트 커넥터를 설정하거나 HTTP 클라이언트에 대한 여러 연결 속성을 구성할 수 있습니다.

 

여기서 Connector는 ClientHttpConnector 인터페이스를 구현하는 클래스를 받는 함수형 인터페이스입니다. 

ClientHttpConnector는 HTTP 클라이언트의 연결 설정을 다루는 인터페이스로, 다양한 클라이언트 커넥터 구현체를 지원합니다.

일반적으로 사용하는 클라이언트 커넥터 구현체 중 하나는 ReactorClientHttpConnector입니다. 

이 구현체는 Reactor 기반의 비동기 HTTP 클라이언트를 사용하며, Spring WebFlux와 함께 사용됩니다. 예제와 같이 clientConnector 메서드를 사용하여 ReactorClientHttpConnector를 설정할 수 있습니다

 

📌 ConnectionProvider

ConnectionProvider는 리액티브 프로그래밍 모델을 사용하여 데이터베이스나 외부 서비스와의 연결 관리를 담당하는 Spring의 기능 중 하나입니다. 이를 통해 애플리케이션이 비동기 및 논블로킹 방식으로 데이터베이스 또는 외부 서비스와 효율적으로 통신할 수 있습니다.

Spring에서는 ConnectionProvider 인터페이스와 구현체를 제공합니다. 주요 목적은 연결을 생성, 풀링, 관리하고, 연결이 사용 가능한지 여부를 결정하는 것입니다. 이를 통해 여러 클라이언트 또는 스레드가 동시에 데이터베이스나 외부 서비스와 통신할 때 공유 연결을 사용하거나 연결을 재사용할 수 있습니다.

 

📌 ConnectionProvider 메소드 설명

ConnectionProvider의다양한 메소드가 사용되었습니다. 아래에서 주어진 메소드들에 대한 간단한 설명입니다. 

  1. ConnectionProvider.builder("custom-provider"): ConnectionProvider를 생성하기 위한 빌더를 시작합니다.
    • "custom-provider"는 이 ConnectionProvider의 이름을 설정합니다. 이름은 식별 용도로 사용됩니다.
  2. maxConnections(20):  maxConnections 메서드는 풀링된 최대 연결 수를 설정합니다.
    • 여기에서는 최대 20개의 연결을 유지하도록 설정되었습니다.
  3. maxIdleTime(Duration.ofSeconds(58)): 연결이 유휴 상태로 유지될 수 있는 최대 시간을 설정합니다.
    • Duration.ofSeconds(58)은 58초 동안 유휴 상태의 연결을 유지하도록 설정합니다.
  4. maxLifeTime(Duration.ofSeconds(58)): 연결의 최대 수명을 설정합니다.
    • Duration.ofSeconds(58)은 58초 동안의 연결 수명을 설정합니다.
  5. pendingAcquireTimeout(Duration.ofSeconds(60)):  대기 중인(획득되지 않은) 연결의 최대 대기 시간을 설정합니다.
    • Duration.ofSeconds(60)은 60초 동안 대기할 수 있는 시간을 의미합니다.
  6. pendingAcquireMaxCount(-1):  최대 대기 중인 연결 요청의 수를 설정합니다.
    • -1은 제한이 없음을 의미하며, 대기 중인 요청 수에 제한을 두지 않음을 나타냅니다.
  7. evictInBackground(Duration.ofSeconds(30)): 백그라운드에서 연결을 해제할 주기를 설정합니다.
    • Duration.ofSeconds(30)은 30초마다 백그라운드에서 연결 해제 작업을 실행합니다.
  8. lifo(): 연결을 가져올 때 Last-In-First-Out(LIFO) 순서로 가져오도록 설정합니다.
    • LIFO는 가장 최근에 반환된 연결을 가장 먼저 사용하도록 하는 방식입니다.
  9. build(): ConnectionProvider 객체를 최종적으로 생성합니다.

 

ConnectionProvider를 사용하면 데이터베이스 연결, 원격 서비스 연결 또는 기타 외부 리소스에 대한 연결 관리를 자동화하고 최적화할 수 있습니다. 이는 리액티브 스트림과 함께 사용하여 여러 요청을 동시에 처리하고 논블로킹 방식으로 데이터를 가져올 때 특히 유용합니다.

 

Spring 프레임워크와 Spring Data 프로젝트에서 다양한 데이터베이스에 대한 ConnectionProvider 구현체를 제공하므로, 특정 데이터베이스 기술을 사용하는 경우에도 리액티브 방식으로 데이터베이스와 통신할 수 있습니다.

 

📌 ReactorClientHttpConnector

ReactorClientHttpConnectorSpring WebFlux에서 사용되는 HTTP 클라이언트 커넥터 중 하나로, Reactor 프레임워크를 기반으로 한 비동기 HTTP 클라이언트를 생성하고 Spring WebFlux 애플리케이션과 통합하는 데 사용됩니다. 이 커넥터는 Spring의 WebClient를 통해 원격 서버와의 비동기 HTTP 통신을 담당합니다.

  • 비동기 및 논블로킹: ReactorClientHttpConnector는 비동기 및 논블로킹 프로그래밍 모델을 따릅니다. 이는 요청을 보내고 응답을 기다리는 동안 다른 작업을 계속 수행할 수 있도록 해주며, 서버로부터 데이터를 받을 때까지 블로킹하지 않습니다.
  • Reactor 기반: ReactorClientHttpConnector는 Reactor 프레임워크의 Mono 및 Flux 타입을 사용하여 비동기적으로 HTTP 요청 및 응답을 처리합니다. 이러한 리액티브 타입은 비동기적인 데이터 스트림 처리에 적합하며, 리액티브 프로그래밍 모델과 통합됩니다.
  • Spring WebClient와 통합: ReactorClientHttpConnector는 Spring의 WebClient와 함께 사용됩니다. WebClient는 HTTP 요청을 정의하고 보내는 데 사용되며, 이 커넥터를 통해 원격 서버와의 통신을 처리합니다.
  • HTTP 요청 구성: ReactorClientHttpConnector를 사용하면 HTTP 요청의 구성을 세부적으로 조정할 수 있습니다. 예를 들어, 요청 헤더, 쿼리 매개변수, 요청 본문 및 인증과 같은 요청 구성 요소를 설정할 수 있습니다.
  • 풀링 및 연결 관리: 이 커넥터는 HTTP 연결 풀링과 연결 관리를 효과적으로 처리하여 리소스를 효율적으로 활용합니다.

ReactorClientHttpConnector를 사용하면 Spring WebFlux 애플리케이션에서 비동기 HTTP 요청을 보내고 비동기적으로 응답을 처리할 수 있습니다. 이것은 비동기 및 논블로킹 웹 애플리케이션을 개발할 때 중요한 요소 중 하나이며, Reactor와 Spring WebFlux와의 통합을 간편하게 수행할 수 있도록 합니다.

 

📌 다음 섹션에서는 WebClient를 사용해서 API를 전송, 처리하는 프로세스를 살펴보도록 하겠습니다.

WebFlux, WebClient 사용하여 API 호출 - 2편 (tistory.com)

 

WebFlux, WebClient 사용하여 API 호출 - 2편

📌 JSON 테스트를 위한 사이트 소개 JSON을 사용하는 서비스를 테스트하기 위한 플랫폼입니다. callback과 같은 여러 매개변수를 지원하여 JavaScript 및 기타 웹 애플리케이션을 테스트할 수 있습니다

whybk.tistory.com

 

반응형