본문 바로가기

Web + APP/Angular

RxJS 구독해제 패턴 심화편

반응형
SMALL

안녕하세요. 꼬동입니다.

 

오늘은 RxJS 구독해제 패턴 심화편을 알아보려합니다.

 

사실 제 주제에 "심화편"이라고 글 쓰는 것도 웃기지만, 폼 좀 잡아보고자 심화편이라고 썼습니다.

 

크큭...

 

글을 시작하기 전에 "심화편" 전에 다룰 수 있는 구독해제 내용은 async / unsubscribe 메소드 / take / takeWhile / takeUntil 등이 있을텐데요.

 

뭐 이런 구독해제 패턴이야 RxJS 홈페이지 들어가면 더 자세히 알려주니, 사용 방법은 홈페이지를 참고하시면 될거 같고, 저는 좀 더 딥하게 알아보려고 합니답.

 

오늘 다룰 주제는 takeWhile vs takeUntilngneat/until-destroy입니다.


takeWhile vs takeUntil

구독해제를 위한 오퍼레이터 두 개를 둘고 왔습니다.

 

takeWhile의 경우엔, 전달 받은 함수의 리턴값이 false가 되는 순간 구독을 해지합니다.

 

takeUntil의 경우엔, 전달받은 observable이 방출할 경우 구독을 해지합니다.

 

자강두천

언뜻 봐선 비슷해보입니다.

 

그런데 은근 둘이 다른 점이 존재합니다.

 

그 다른 점을 명확하게 나타낼 수 있는 예제 코드를 보여드리도록 하겠습니다.

 

takeWhile

import { fromEvent, interval } from 'rxjs';

import { take, takeWhile, tap } from 'rxjs/operators';

console.clear();

let flag = true;

interval(1000)
  .pipe(
    tap(console.log),
    takeWhile(() => flag)
  )
  .subscribe();

fromEvent(document, 'click')
  .pipe(take(1))
  .subscribe(() => (flag = false));

 

https://stackblitz.com/edit/rxjs-qpfyoj?devtoolsheight=60 

 

rxjs-qpfyoj - StackBlitz

Blank starter project for building TypeScript apps.

stackblitz.com

 

보시면 클릭 후 한 번 더 데이터를 받아와서 출력을 합니다.

 

아래와 같이 말이죠.

 

1
2
3
// 클릭 !!!
4
// 종료

 

마치 이별 뒤, 미련이 남은 사람이 한 번 뒤돌아보고 종료되는 그런 아련함이 있습니다.

 

 

takeUntil

takeUntil은 어떨까요. 쿨하게 이별을 감내하는 친구일까요 ?

 

코드는 아래와 같습니다.

import { of } from 'rxjs';
import { fromEvent, interval } from 'rxjs';
import { map, takeUntil, tap } from 'rxjs/operators';

console.clear();

let flag = true;

interval(1000)
  .pipe(
    tap(console.log),
    takeUntil(fromEvent(document, 'click'))
  )
  .subscribe();

 

https://stackblitz.com/edit/rxjs-r5khjm?devtoolsheight=60 

 

rxjs-r5khjm - StackBlitz

Blank starter project for building TypeScript apps.

stackblitz.com

 

이 친구는 takeWhile과 같은 친구와는 다르게 마음을 돌리면 미련 따윈 남기지 않는 그런 친구였습니다.

 

클릭 후 그 뒤엔 신경도 쓰진 않으니까요.

1
2
// 클릭 !!!
// 종료

 

즉, 둘 사이의 문제는 다음과 같습니다.

 

takeWhile의 경우엔 들어오는 알림에 의해 트리거가 되며, 그 후 구독을 취소합니다.

 

takeUntil은 통과한 Observable에 의해 트리거가 되고요.

 

정리하자면, takeWhile은 takeUntil보다 Observable flow를 한 번 더 허용해주는 친구였던거죠.

 

이게 큰 문제가 될 수 있습니다. 만약, takeWhile이 단순히 console에 숫자를 표현하는게 아닌 무거운 Http Call을 했다고 생각해봅시다.

 

뭐 기존에 사용할 땐 상관없는데, 얘를 구독해지 하기 위해선 Http Call을 한 번 더 호출해야한다는 점이 큰 문제가 됩니다.

 

그렇기 때문에 이렇게 질문을 할 수 있습니다.

 

에베베베

 

애ㅔ베베에베ㅔ베베베ㅔ베ㅔㅔ베 takeWhile을 pipeline 제일 처음에 두면 되잖아 헤에에에ㅔㅇ베베베ㅔ베에베베

 

넹 맞습니당.

 

그렇게하면 불필요한 리소스를 줄일 수 있져.

 

하지만 이렇게 하면, inner Observable을 통해서 이를 구독해제 할 수 없다는 점을 알고 계셔야겠져 ?

 

takeWhile과 takeUntil 모두 홀륭한 operators이기 때문에 배척하지 마세요.

 

코드는 문제가 없습니다. 문제가 있는건 항상 저희였죠..

 

 

그렇기 때문에 조심하고 사용한다면, 충분히 좋은 operators가 될 수 있겠습니다.


앞서 말했듯이 구독해제를 하는 방법은 수없이 많습니다.

 

특히 Component가 destroy되는 순간에 구독해제를 해줘야 메모리 누수를 막을 수 있죠. 그렇기에 한 친구씩 찾아서 구독 해제를 시켜줘야합니다.

 

그렇다고 진짜 한 친구 씩 찾아서 unsubscribe 메소드를 호출하고 해야할까요 ?

 

인간이란 편리함을 찾기 위해 사는 동물

 

귀찮음이 많은 사람이 개발을 잘합니다.

 

그 귀찮음이 많고 정말 멋지시고 !! 똑똑하시고 !! 존경스럽구 !! 든든하신 분이 모듈을 하나 추천을 해줬는데요. 그 모듈에 대해 알아보도록 합시다.

 

^^7

@ngneat/until-destroy

https://github.com/ngneat/until-destroy

 

ngneat/until-destroy

🦊 RxJS operator that unsubscribe from observables on destroy - ngneat/until-destroy

github.com

해당 모듈 역시 destroy될 떄 구독해제를 해주는 친절한 모듈입니다.

 

그 전에 참고해야하는 점을 몇 가지 살펴봅시다.

 

  • @ngneat/until-destroy@8+의 경우엔 Angular 10.0.5 version 이상이 되어야합니다. 그 이하 버전은 @ngneat/until-destroy@7을 쓰세용
  • npm install @ngneat/until-destroy / yarn add @ngneat/until-destroy로 설치가 가능합니다.

 

설치했다면 바로 사용해봐야죠. 

 

이 분들이 제공하는 기본 코드는 위와 같습니다.

 

import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';

@UntilDestroy()
@Component({})
export class InboxComponent {
	ngOnInit() {
    	interval(1000)
        	.pipe(untilDestroyed(this))
            .subscribe();
    }
}

 

오메 저도 이거 코드로 된건 적으면서 처음 봤는데 겁나 간단하네영 ;; 직관적이기도 하고

 

게다가 무슨 property를 사용하면, 자동으로 해제를 해준다고도 합니다.

 

@UntilDestroy({ checkProperties: true })
@Component({})
export class HomeComponent {
  // We'll dispose it on destroy
  subscription = fromEvent(document, 'mousemove').subscribe();
}

 

굳이 pipe로다가 호들갑 안 떨고 젠틀하게 구독해제를 할 수 있네요.

 

@UntilDestroy({ arrayName: 'subscriptions' })
@Component({})
export class HomeComponent {
	subscriptions = [
    	fromEvent(document, 'click').subscribe(),
        fromEvent(document, 'mousemove').subscribe()
    ];
    
    ngOnInit() {
    	interval(1000).pipe(untilDestroyed(this));
    }
}

 

위의 코드는 destroy가 되면 subscriptions이라는 이름을 가진 배열에 담긴 subscription을 구독해제를 해준다고 합니다.

 

destroy 시 구독을 끊기 싫으면 아래와 같이도 사용할 수 있네요.

 

@UntilDestroy({ checkProperties: true, blackList: ['subscription1'] })
@Component({})
export class HomeComponent {
  subscription1: Subscription;
  subscription2: Subscription;

  constructor() {
    this.subscription1 = new Subject().subscribe();
    this.subscription2 = new Subject().subscribe();
  }
}

 

해당 모듈을 사용하면, 좀 더 깔끔하고 정확하게 구독해제를 할 수 있겠죠?

 

어서 영업하세요 !!!

 

츄라이 츄라이 !!


다양한 구독해제를 알아봤습니다.

 

사실 "심화편" 이라고 했는데, 글 다 적고 보니 그렇게 심화 아니네요

 

죄송합니당

 

에쿵~


이상 RxJS 구독해제 패턴 심화편였습니다. ^_^

반응형
LIST

'Web + APP > Angular' 카테고리의 다른 글

iOS 한글 buffer 문제  (1) 2021.09.11
Angular Flex-Layout using MediaObserver  (2) 2021.08.28
Async Pipe 뜯어먹기  (4) 2021.05.30
RxJS가 해결하려고 했던 문제 3  (6) 2021.05.20
RxJS가 해결하려고 했던 문제 2  (0) 2021.05.19