티스토리 뷰

RxJs에서는 Observable를 구독(subscribe) 함으로서 스트림을 통해 데이터를 주고 받고 하고 있습니다. 하지만 Observable를 더 이상 관찰할 필요가 없을 경우 구독취소(unsubscribe)를 해야합니다.


그럼 RxJs를 도입한 Angular(Angular 4)에서는 구독취소(unsubscribe)를 언제 어떻게 해야할까요?

오늘 얘기할 내용은 바로 이 구독취소를 언제, 어떻게, 왜 해야하는지에 대해 얘기하고자 합니다.


2017. 09. 15 Update

- takeUntil 내용 추가


ps) 글에 피드백을 주신분들께 감사드립니다.

왜 구독취소를 해야할까요?


이 얘기를 하기위해서는 Observable이 언제 실행되는지 알아야합니다. 기본적으로 Observablesubscribe(구독)을 했을때 실행을 합니다. 그 이후 들어오는 스트림(데이터)은 subscribe 메서드 안에서 처리가 됩니다. 그리고 구독을 취소하거나 구독이 완료(complated)되기전까지 계속 동작하게되어 있습니다.


그러다보니 구독취소(unsubscribe)를 안하는경우 계속해서 관찰하게 되어 메모리 낭비가 되거나 혹은 로직이 잘못될 수 있습니다.

그럼 언제 구독을 취소해야하나요?


일반적으로 Angular에서 unsubscribe(구독취소)하는 위치는 ngOnDestroy이 실행될 때 입니다. 이건 ComponentDirective 둘다 동일합니다.

여기서 예외는 Async Pipe(파이프)입니다. Async Pipe는 내부적으로 subscribe(구독)과 unsubscribe(구독취소) 처리를 알아서 하고 있습니다.

그래서 어떻게 구독 취소를 해야할까요?


오늘 얘기할 내용중 가장 핵심인 내용입니다. 가장 기본적인 방법은 아래와 같이 unsubscribe 메서드를 호출 하는것 입니다.

ngOnDestroy() {
  this.productsSubscription.unsubscribe();
  this.categorysSubscription.unsubscribe();
  this.userInfoSubscription.unsubscribe();
}

사실 unsubscribe 메서드는 subscribe 메서드의 리턴값인 Subscription 객체에 존재합니다. 그러다보니 여러개의 Observable이 있는경우 ngOnDestroy 코드가 위와 같이 반복되기 쉽습니다. 또한 저 Subscription 객체를 내부에 선언해야한다는 불편함도 있습니다.

take Method

그래서 RxJs에서는 take 같은 구독 취소를 편하게 할 수 있는 operator(연산자)들이 존재합니다.

this.productions$
  .take(1)
  .subscribe(products => {
    //.. code
  });

위의 소스에서 take(1) 는 한번 실행 후 자동으로 구독이 취소됩니다. ajax 호출같은 스트림을 처리할 때 유용한 unsubscribe(구독취소) 패턴입니다.

takeWhile

그럼 이벤트를 다루는 스트림에서는 어떻게 해야할까요? 이벤트는 이벤트 관찰이 필요한 경우 계속 구독해야하는 경우가 대다수일텐데요.

이럴 경우는 takeWhile 라는 연산자(operator)를 이용하면 편리합니다. 이 패턴은 제가 주로 사용합니다.

alive = true;

ngOnInit() {
  this.filterChange$
    .takeWhile(() => this.alive)
    .subscribe(filter => {
      // 필터가 변경되었을때의 처리
    });

  this.keywordChange$
    .takeWhile(() => this.alive)
    .subscribe(keyword => {
      // 키워드가 변경되었을때의 처리
    });
}

ngOnDestroy() {
  this.alive = false;
}

subscribe 전에 takeWhile 연산자를 이용해 this.alivetrue일때만 구독하도록 처리하고 ngOnDestory에서 this.alive 값을 false로 변경함으로 구독취소 처리를 하는 방식입니다. 이 패턴을 사용하면 구독시 매번 Subscription 객체를 선언할 필요가 없습니다.


2017. 09. 15 Update

takeUntil

또 다른 방법으로는 takeUntil 연산자를 이용하는 방법이 있습니다. takeUntil은 연산자에 Observable를 넘겨 미러링을 한뒤 넘겨준 Observable이 데이터를 받거나 완료처리가 되면 미러링을 중단하고 처음 Observable은 구독취소가 됩니다.


예시를 보면 거의 takeWhile과 방식이 비슷합니다.

unsubscribe: Subject = new Subject(void);

public ngOnInit() {
  this.tagService.getTags({category: 'keyword'})
    .takeUntil(this.unsubscribe)
    .subscribe(tags => {
      this.tags = tags;
    });
}

public ngOnDestroy() {
  this.unsubscribe.next();
  this.unsubscribe.complete();
}


정리


그럼 어떤 방식이 제일 좋을까요?? async 파이프(pipe)를 사용하면 일단 Angular 자체에서 구독과 취소처리를 하기 때문에 가장 좋은 방법이라고 할 수 있겠습니다만 그게 불가능한 상황일 경우 위에서 얘기한 어떠한 방법을 사용해도 사실 크게 상관은 없습니다. 편한 방식을 선택하시면 될 것 같습니다.


하지만 명시적으로 unsubscribe를 이용하여 구독취소를 하는것 보다 operator(연산자)를 이용하여 구독취소 처리를 해주는게 스트림의 흐름을 파악하기 편리하기때문에 되도록이면 operator를 활용하시는 편이 좋을것 같습니다.


참고한 사이트: http://reactivex.io/documentation/operators

댓글