본문 바로가기
JAVA

WebFlux: 반응형 프로그래밍을 이용한 비동기 웹 프레임워크

by 얍얍1234 2024. 6. 9.

오늘은 Spring 프레임워크에서 제공하는 비동기, 논블로킹 웹 프레임워크인 WebFlux에 대해 알아보겠습니다.

WebFlux는 고성능과 확장성을 요구하는 현대의 웹 애플리케이션에서 중요한 역할을 합니다.

WebFlux의 기본 개념, 주요 특징, 사용 방법, 그리고 실전 예제를 통해 WebFlux를 소개하고자 합니다.


1. WebFlux란 무엇인가?

WebFlux는 Spring 5에서 도입된 모듈로, 반응형 프로그래밍(reactive programming) 모델을 기반으로 하는 웹 프레임워크입니다. 이는 비동기(non-blocking) 방식으로 동작하며, 네트워크 I/O나 데이터베이스 액세스와 같은 I/O 작업에서 높은 효율성을 제공합니다. WebFlux는 서버 사이드 애플리케이션뿐만 아니라 클라이언트 사이드 애플리케이션에서도 사용할 수 있습니다.

 


1.1. 반응형 프로그래밍

반응형 프로그래밍은 데이터 스트림과 변화의 전파를 선언적으로 처리하는 프로그래밍 패러다임입니다. 이를 통해 비동기 데이터 처리와 이벤트 기반 시스템을 쉽게 구현할 수 있습니다. 반응형 프로그래밍의 핵심 개념은 Publisher, Subscriber, Subscription, 그리고 Processor입니다.

- Publisher: 데이터를 생성하고 이를 구독자에게 전달하는 역할을 합니다.
- Subscriber: 데이터를 소비하며, Publisher로부터 데이터를 받아 처리합니다.
- Subscription: Publisher와 Subscriber 간의 연결을 나타내며, 데이터 흐름을 제어할 수 있습니다.
- Processor: Publisher와 Subscriber의 역할을 동시에 수행하며, 데이터를 변환하거나 처리할 수 있습니다.

 


1.2. WebFlux의 특징

WebFlux는 다음과 같은 주요 특징을 갖고 있습니다.

- 비동기, 논블로킹 I/O: WebFlux는 Netty, Undertow, 그리고 Servlet 3.1+ 기반의 컨테이너와 같은 비동기, 논블로킹 서버를 지원합니다.
- 반응형 스트림 지원: WebFlux는 Reactive Streams 표준을 따르며, Project Reactor를 기반으로 구현되었습니다.
- 경량성: Spring MVC보다 경량화된 구조로, 고성능 애플리케이션을 구축할 수 있습니다.
- 유연성: 함수형 프로그래밍 스타일과 어노테이션 기반 프로그래밍 스타일 모두를 지원합니다.

 


2. WebFlux의 주요 구성 요소

WebFlux는 반응형 애플리케이션을 구축하기 위해 다양한 구성 요소를 제공합니다. 여기서는 주요 구성 요소인 Router, Handler, 그리고 WebClient를 소개합니다.

 

2.1. Router
Router는 요청을 특정 Handler로 라우팅하는 역할을 합니다. Spring MVC에서의 @RequestMapping과 유사한 역할을 합니다. 함수형 라우팅 DSL을 사용하여 라우트를 정의할 수 있습니다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration
public class RouterConfig {

    @Bean
    public RouterFunction<ServerResponse> route(MyHandler handler) {
        return route(GET("/hello"), handler::hello)
               .andRoute(GET("/goodbye"), handler::goodbye);
    }
}

 


2.2. Handler
Handler는 실제 요청을 처리하는 역할을 합니다. 요청을 받아 적절한 응답을 생성하여 반환합니다.

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
public class MyHandler {

    public Mono<ServerResponse> hello(ServerRequest request) {
        return ServerResponse.ok().bodyValue("Hello, WebFlux!");
    }

    public Mono<ServerResponse> goodbye(ServerRequest request) {
        return ServerResponse.ok().bodyValue("Goodbye, WebFlux!");
    }
}

 


2.3. WebClient
WebClient는 비동기, 논블로킹 방식으로 HTTP 요청을 보내기 위한 클라이언트입니다. RestTemplate의 대체제로 사용할 수 있습니다.

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

public class WebClientExample {

    private final WebClient webClient;

    public WebClientExample(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://jsonplaceholder.typicode.com").build();
    }

    public Mono<String> getPostTitle(int postId) {
        return webClient.get()
                        .uri("/posts/{id}", postId)
                        .retrieve()
                        .bodyToMono(String.class);
    }
}

 


3. WebFlux를 이용한 애플리케이션 개발

이제 WebFlux를 이용해 간단한 애플리케이션을 만들어 보겠습니다. 예제 애플리케이션은 사용자 정보를 관리하는 RESTful API를 구현합니다.

3.1. 프로젝트 설정
Spring Initializr를 사용하여 WebFlux 프로젝트를 생성합니다. 의존성으로는 Spring Reactive Web과 Lombok을 추가합니다.

3.2. 도메인 모델
먼저, 사용자 정보를 나타내는 도메인 모델을 정의합니다.

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Data
@Table("users")
public class User {
    @Id
    private Long id;
    private String name;
    private String email;
}

 


3.3. 리포지토리
ReactiveCrudRepository 인터페이스를 상속받아 사용자 정보를 관리할 리포지토리를 정의합니다.

import org.springframework.data.repository.reactive.ReactiveCrudRepository;

public interface UserRepository extends ReactiveCrudRepository<User, Long> {
}



3.4. 서비스
리포지토리를 이용해 사용자 정보를 처리하는 서비스 클래스를 작성합니다.

import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public Flux<User> getAllUsers() {
        return userRepository.findAll();
    }

    public Mono<User> getUserById(Long id) {
        return userRepository.findById(id);
    }

    public Mono<User> createUser(User user) {
        return userRepository.save(user);
    }

    public Mono<Void> deleteUser(Long id) {
        return userRepository.deleteById(id);
    }
}



3.5. 핸들러 및 라우터
서비스를 이용해 실제 요청을 처리하는 핸들러와 라우터를 작성합니다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration
public class UserRouter {

    @Bean
    public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
        return route(GET("/users"), handler::getAllUsers)
               .andRoute(GET("/users/{id}"), handler::getUserById)
               .andRoute(POST("/users"), handler::createUser)
               .andRoute(DELETE("/users/{id}"), handler::deleteUser);
    }
}

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
public class UserHandler {

    private final UserService userService;

    public UserHandler(UserService userService) {
        this.userService = userService;
    }

    public Mono<ServerResponse> getAllUsers(ServerRequest request) {
        return ServerResponse.ok().body(userService.getAllUsers(), User.class);
    }

    public Mono<ServerResponse> getUserById(ServerRequest request) {
        Long userId = Long.valueOf(request.pathVariable("id"));
        return userService.getUserById(userId)
                          .flatMap(user -> ServerResponse.ok().bodyValue(user))
                          .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> createUser(ServerRequest request) {
        return request.bodyToMono(User.class)
                      .flatMap(userService::createUser)
                      .flatMap(user -> ServerResponse.ok().bodyValue(user));
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        Long userId = Long.valueOf(request.pathVariable("id"));
        return userService.deleteUser(userId)
                          .then(ServerResponse.noContent().build());
    }
}



4. 결론

이번 포스트에서는 WebFlux의 기본 개념과 주요 구성 요소, 그리고 간단한 애플리케이션 구현 방법에 대해 알아보았습니다.

WebFlux는 비동기, 논블로킹 방식의 반응형 프로그래밍 모델을 기반으로 하여, 고성능 웹 애플리케이션을 구축하는 데 매우 유용한 프레임워크입니다.

이를 통해 더 나은 확장성과 성능을 제공하는 현대적인 웹 애플리케이션을 개발할 수 있습니다.

'JAVA' 카테고리의 다른 글

Java 연산자  (1) 2024.06.16
Java 자료형  (0) 2024.06.10
Java 객체지향이란  (0) 2024.06.07
Java 람다 알아보자  (2) 2024.06.02
Java for문에 대해서 알아보자  (0) 2023.10.15