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) 해제

Mono나 Flux같은 Publisher를 subscribe() 메소드로 구독하면 일반적으로 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()

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

Subscription.cancel()

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

Disposable.dispose()

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

일반적으로 Mono나 Flux를 구독하고 받은 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개의 댓글