Disposable

김기현·2025년 8월 4일

Spring WebFlux

목록 보기
20/28

Spring WebFlux: Disposable

스프링 WebFlux에서 Disposable은 Reactor 프로젝트에서 제공하는 인터페이스로, 자원 해제와 관련된 중요한 역할을 한다.
비동기 및 논블로킹 환경에서 자원을 적절하게 해제하고 정리하는 메커니즘을 제공한다.


Disposable 인터페이스 정의

@FunctionalInterface
public interface Disposable {
    void dispose();

    default boolean isDisposed() {
        return false;
    }

    public interface Composite extends Disposable {
        boolean add(Disposable var1);

        default boolean addAll(Collection<? extends Disposable> ds) {
            boolean abort = this.isDisposed();
            Iterator var3 = ds.iterator();

            while(var3.hasNext()) {
                Disposable d = (Disposable)var3.next();
                if (abort) {
                    d.dispose();
                } else {
                    abort = !this.add(d);
                }
            }

            return !abort;
        }

        void dispose();

        boolean isDisposed();

        boolean remove(Disposable var1);

        int size();
    }

    public interface Swap extends Disposable, Supplier<Disposable> {
        boolean update(@Nullable Disposable var1);

        boolean replace(@Nullable Disposable var1);
    }
}

핵심 메소드는 dispose()이다. dispose()메소드는 해당 Disposable객체가 관리하는 리소스나 작업을 해제하고 정리하도록 지시하는 역할을 한다.


Disposable이 사용되는 맥락

1. 구독(Subscription) 해제

MonoFlux같은 Publishersubscribe() 메소드로 구독하면 일반적으로 Disposable객체가 반환된다. 이 Disposable객체는 구독을 명시적으로 취소하거나, 구독과 관련된 자원을 해제할 때 사용된다.

2. 리소스 관리 및 종료 후크

  • 일부 Spring WebFlux 컴포넌트나 Reactor 오퍼레이터는 내부적으로 Disposable을 사용하여 리소스 라이프사이클을 관리한다.
  • 예를 들어 Mono.using()이나 Flux.using()과 같은 리소스 관리 오퍼레이터에 리소스 해제를 위한 Disposable을 반환하거나 리소스가 Disposable을 구현하는 경우 이를 활용하여 자동으로 리소스를 정리할 수 있다.

3. Disposable 조합

  • Disposable.composite()와 같은 유틸리티 메소드를 사용하여 여러 Disposable인스턴스를 하나의 Disposable로 묶을 수 있다.
  • 이렇게 하면 하나의 dispose()호출로 모든 구성 요소 Disposable을 한 번에 해제할 수 있다.
  • 이는 여러 비동기 작업의 자원 해제를 일괄적으로 처리할 때 유용하다.

Disposable의 중요성

메모리 누수 방지

  • 명시적으로 dispose()를 호출하지 않거나, 리액티브 스트림이 정상적으로 완료되지 않는 경우, 구독이 유지되어 관련 리소스(ex: 스레드, 네트워크 연결, 메모리 버퍼)가 해제되지 않고 계속 남아있을 수 있다.
  • 이는 메모리 누수나 리소스 고갈로 이어질 수 있다.

성능 최적화

  • 불필요하게 활성화된 리소스를 적시에 해제함으로써 시스템 리소스를 효율적으로 사용하고 성능을 최적화할 수 있다.

예측 가능한 종료

  • 애플리케이션이나 특정 컴포넌트가 종료될 때, 모든 활성 Disposable객체를 dispose()하여 깨끗하게 정리하고 예측 가능한 방식으로 종료되도록 돕는다.

cancel() vs dispose()

DisposableSubscription 인터페이스와 밀접한 관련이 있다. Subscription 인터페이스에는 cancel() 메소드가 있다.

Subscription.cancel()

  • 리액티브 스트림의 표준에 따라 구독을 취소하고, 더 이상 요소를 받지 않으며 상류(upstream)에게 더 이상 요청하지 않음을 알린다.

Disposable.dispose()

  • cancel()과 유사하게 구독을 취소하는 역할도 하지만, Disposable은 좀 더 넓은 의미에서 "자원 해제 및 정리"를 나타내는 인터페이스이다.
  • subscribe()가 반환하는 Disposable은 내부적으로 cancel()을 호출하고 관련된 추가적인 정리 작업을 수행한다.

일반적으로 MonoFlux를 구독하고 받은 Disposable 인스턴스에 대해 dispose()를 호출하는 것이 구독 취소 및 자원 해제를 위한 권장 방식이다.


Disposable을 사용한 자원 정리 예제

@SpringBootTest
public class DisposableTest {

    @Test
    void dispose() throws InterruptedException {
        System.out.println("--- Flux.interval 예제: Disposable을 이용한 자원 해제 ---");

        // 1초마다 숫자를 발행하는 Flux 생성
        Flux<Long> tickingFlux = Flux.interval(Duration.ofSeconds(1));

        // Flux를 구독하고 Disposable 객체 받기
        // 이 시점부터 1초마다 숫자가 출력되기 시작
        Disposable subscription = tickingFlux.subscribe(
                data -> System.out.println("받은 데이터: " + data),
                error -> System.err.println("에러 발생: " + error),
                () -> System.out.println("완료 시그널") // 이 경우 interval은 무한 스트림이므로 완료 시그널은 오지 않음
        );

        System.out.println("구독 시작. 5초 후에 구독을 해제합니다...");

        // 메인 스레드는 5초 동안 대기
        Thread.sleep(5000);

        // 5초 후, subscription.dispose()를 호출하여 구독을 취소하고 내부 리소스(타이머 스레드) 해제
        if (!subscription.isDisposed()) { // 이미 해제되었는지 확인
            subscription.dispose();
            System.out.println("구독이 성공적으로 해제되었습니다. 더 이상 데이터가 오지 않습니다.");
        } else {
            System.out.println("구독이 이미 해제된 상태입니다.");
        }
    }
}
profile
백엔드 개발자를 목표로 공부하는 대학생

0개의 댓글