Lazy Evaluation

Single Ko·2024년 8월 9일
0

reactive-programing

목록 보기
5/8

Java Stream의 Lazy 특성 이해

Reactor 라이브러리를 알아보기 전에, 핵심 개념인 Lazy Evaluation을 이해하는 것이 중요합니다. 먼저 Java Stream의 Lazy 특성을 살펴보고, 이것이 Reactor의 동작 방식과 어떻게 연결되는지 알아보겠습니다.

Stream API는 데이터 처리를 위한 강력한 도구입니다. Stream의 중요한 특징 중 하나는 Lazy Evaluation, 즉 필요할 때만 연산을 수행하는 것입니다. 이를 통해 불필요한 연산을 피하고 효율성을 높일 수 있습니다.

예제 코드

import java.util.stream.Stream;

public class LazyStream {
    public static void main(String[] args) {
        Stream.of(1)
      		.peek(i -> System.out.println("Received: " + i)); // (1)

        // stream.toList(); // (2) - Terminal Operator 추가

    }
}

(1) peek() 연산: peek() 연산은 Stream의 각 요소에 대해 Consumer를 실행합니다. 여기서는 "Received: i"를 출력하는 Consumer를 사용했습니다.

(2) 주석 처리된 Terminal Operator: 현재 코드에서는 Terminal Operator가 주석 처리되어 있습니다. Terminal Operator는 Stream의 최종 연산을 수행하고 결과를 반환하는 역할을 합니다.

실행 결과

위 코드를 실행하면 아무런 출력도 나타나지 않습니다. peek() 연산 내부의 System.out.println()이 실행되지 않은 것입니다. Stream은 Lazy하기 때문에, Terminal Operator가 연결될 때까지 실제 연산을 수행하지 않습니다. 즉, stream.toList()와 같이 Terminal Operator를 추가해야 Stream이 실행되고 peek() 연산도 수행됩니다.

(2) 주석을 해제하고 다시 실행하면 "Received: 1"이 출력됩니다.

Lazy Evaluation이 중요한 이유

Reactor의 Mono와 Flux도 Java 8 Stream과 마찬가지로 Lazy Evaluation을 기반으로 동작합니다. Subscriber가 구독하기 전까지는 데이터를 생성하거나 처리하지 않습니다. 이러한 특성은 리액티브 프로그래밍의 핵심이며, 비동기적이고 Non-blocking 방식으로 데이터를 처리하는 데 중요한 역할을 합니다.

Reactor

Reactor 라이브러리는 Java 생태계에서 가장 인기가 많은 리액티브 프로그래밍 라이브러리 입니다. 따라서 우리는 이 Project Reactor library를 알아보겠습니다.

Mono & Flux

Reactor의 핵심에는 MonoFlux라는 두 가지 Publisher가 있습니다. 이 두 가지는 비동기적이고 Non-blocking 방식으로 데이터를 처리하지만, 사용 사례가 다릅니다.
MonoFlux의 차이점을 살펴보고, 어떤 상황에서 어떤 Publisher를 사용해야 하는지 알아보겠습니다.

1. Mono: 0 또는 1개의 데이터

Mono는 0 또는 1개의 데이터를 방출하는 Publisher입니다. 데이터베이스에서 ID를 기반으로 단일 레코드를 조회하는 경우처럼 결과가 하나이거나 없는 경우에 적합합니다. Spring Data JPA를 예로 들면, findById 메서드는 Optional<Customer>를 반환하는데, 이는 Mono<Customer>로 자연스럽게 변환될 수 있습니다.

2. Flux: 0 또는 N개의 데이터 (무한 스트림 가능)

Flux는 0개, 유한 개, 또는 무한 개의 데이터를 방출하는 Publisher입니다. 데이터베이스에서 이름으로 여러 레코드를 조회하는 경우처럼 결과가 여러 개일 수 있는 경우에 적합합니다. Spring Data JPA의 findByFirstName 메서드는 List<Customer> 또는 Stream<Customer>를 반환하는데, 이는 Flux<Customer>로 변환될 수 있습니다.

왜 Mono를 사용 하나요? Flux로 모든 것을 처리할 수 있지 않나요?

Flux가 여러 개의 데이터를 처리할 수 있으므로, Mono를 왜 사용하는지 의문이 들수 있습니다. 하지만 명확하게 Mono를 활용하는 장점이 있습니다. 그 점을 알아보겠습니다.

  • 명확성: Mono를 사용하면 해당 Publisher가 단 하나의 데이터만 방출할 것임을 명확하게 나타낼 수 있습니다. 코드의 가독성과 유지보수성을 향상시킵니다.
  • 간결성: MonoFlux보다 단순하고 가볍습니다. 스트림 처리와 Backpressure 관련 메서드가 없기 때문에, 단일 데이터를 처리하는 경우 더욱 간결하고 효율적인 코드를 작성할 수 있습니다.
  • Backpressure 불필요: Mono는 단일 데이터만 처리하므로 Backpressure를 고려할 필요가 없습니다. Flux를 사용하는 경우, Producer가 Consumer보다 빠르게 데이터를 생성하는 경우 Backpressure를 처리해야 할 수 있습니다.

결론:

MonoFlux는 모두 강력한 Publisher이지만, 사용 사례가 다릅니다. 예상되는 데이터 개수와 코드의 명확성, 간결성을 고려하여 적절한 Publisher를 선택해야 합니다.

  • 단일 데이터 (0 또는 1개): Mono 사용
  • 여러 개의 데이터 (0 또는 N개, 무한 스트림 가능): Flux 사용

MonoFlux를 적절하게 사용하면 리액티브 프로그래밍의 장점을 최대한 활용하여 효율적이고 Non-blocking 방식으로 데이터를 처리할 수 있습니다.

profile
공부 정리 블로그

0개의 댓글