티스토리 뷰


[번역글] Angular에서 Rx.js의 Observable 관리하기

본 포스팅은 다음의 원문을 번역한 글입니다. 많은 의역이 포함되어 있을 수 있습니다.

원문: Manage your observable subscriptions in Angular with help of rx.js

서브스크립션(Subscription)을 위해 Observable 변수를 사용 할 때, 이 변수를 관리하는 것은 아주 중요합니다. Observable 은 영원히 동작할 수 있으므로 우리는 이 Observable의 사용이 끝났을 때, 중지하는 것이 필요합니다. 만약 우리가 서브스크립션을 중단하지 않고 계속 유지한다면, 불필요한 메모리와 컴퓨팅 자원이 낭비될 것이므로 좋지 않습니다. 물론 우리는 unsubscribe() 라는 함수를 호출하면 됩니다. 하지만 이보다 더 쉽고 좋은 강력한 방법이 rx.js에는 있습니다.

만약 아직 Observable에 대해 완전히 이해하지 못했다면, 이전의 포스팅을 참고해 주세요.

rx.js의 오퍼레이터(operator)를 사용하여 unsubscribe()함수의 호출 없이 Observable을 완료할 수 있습니다. 이 메소드를 사용하여 서브스크립션을 완료하는 방법은 unsubscribe() 함수를 호출하는 것보다 더 선호됩니다.

저는 각 rx.js의 오퍼레이터를 설명하기 위해서 rxmarbles.com의 이미지를 포함할것입니다. 이미지에서 위의 화살표는 여러개의 값을 발산(emit)하는 원래의 Observable을 나타낸다는 것을 기억하세요. 각 사이클은 한 개의 발산(emit)입니다. 그리고 아래의 화살표는 오퍼레이터가 완료되기 전에 갖는 데이터 스트림입니다. 만약 아직 rxmarbles를 잘 모른다면, 최대한 빨리 볼 수 있기를 바랍니다. rx.js 의 오퍼레이터가 어떻가 동작하는지 이해하는데 아주 큰 도움을 줍니다.

.first()

오직 첫번째 값만 필요하다면 fisrt() 를 사용하세요.Observable은 첫번째 값만 전달하고 완료합니다.

Imgur

사용 예

주로 API 요청을 할 때,first 를 사용할 수 있습니다.

import { Component } from '@angular/core';
import { ApiClient } from '../client/apiclient';

@Component({
    selector: 'example-component',
    templateUrl: './template.html',
})
export class ExampleComponent {
    
    constructor(
        public client: ApiClient,
    ){}

    // api call that takes one response and then completes
    public fetchUsers() {
        this.client.fetchUsers()
            .first()
            .subscribe((users: IUser[]) {
                // do stuff with your users here
            })
    }
    
    public ngOnInit() {
        this.fetchUsers();
    }
}

.take()

최대로 수행할 횟수를 지정하기를 원한다면, take를 사용하면 됩니다. take는 지정한 횟수만큼의 값을 발산하는 Observable 을 반환합니다.

Imgur

사용 예

만약 첫 번째 값은 null 일 수 있으나, 두번째 값은 반드시 값이 존재하는 로직이라면 take를 사용하기 적절합니다. 저는 때로는 ngrx/store 로부터 데이터를 선택할때 이 함수를 사용합니다. 데이터를 요청할 때, store는 비어있을 수 있기 때문입니다. 그럼 첫번째 값은 null이 됩니다. 그리고 조금 뒤에 데이터가 받아서 서브스크립션자변수에 채워집니다. 그렇게 되면 take(2)는 아주 적절하게 동작됩니다.

import { Component } from '@angular/core';
import { Store } from '@ngrx/store';

import { IState } from '../reducers';

@Component({
    selector: 'example-component',
    templateUrl: './template.html',
})
export class ExampleComponent {
    
    constructor(
        public store: Store<IState>,
    ){}

    // get the users from your ngrx/store
    // it can happen that it first is empty
    // the second time there might be users, then it completes
    public fetchUsersFromStore() {
        this.store.select(getUsers)
            .take(2)
            .subscribe((users: IUser[]) {
                // do stuff with your users here
            })
    }
    
    public ngOnInit() {
        this.fetchUsersFromStore();
    }
}

.elementAt()

takeObservable 변수가 발산하는 최대 값을 지정한다면, elementAt은 인덱스를 지정하여 받을 수 있도록 합니다. 즉, elementAt(2) 를 사용하면 Observable 변수가 세 번째로 발산하는 값을 받습니다. elementAt 함수는 세번째 값이 올때까지 기다리며, 그 후 종료합니다.

Imgur

사용 예

예를 들면 세 번째 클릭했을 때, 무언가 동작을 취하는 시나리오가 있을 수 있습니다.

import { Component } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { IState } from '../reducers';

@Component({
    selector: 'example-component',
    templateUrl: './template.html',
})
export class ExampleComponent {
   
    public clicks$: Observable<any>;
    
    // only take the third click
    // then complete
    public listenToClicks() {
        this.clicks$
            .elementAt(2)
            .subscribe((thirdClick: any) => {
                // do something with the third click
            });
    }
    
    public ngOnInit() {
        this.clicks$ = Observable.fromEvent(document.getElementById(button), 'click');
        this.listenToClicks();
    }
}

.takeUntil()

두 개의 Observable 변수가 필요한 takeUntil은 이해하기에 조금 더 어렵습니다. takeUntil은 두 번째 Observable변수에서 무언가 값을 발산(emit)하거나 종료될 때까지 값을 발산합니다.

Imgur

사용 예

특정 페이지에 웹소켓을 열어 리슨(listen)하고 있다고 생각 해 봅시다. 물론 페이지에 머물고 있는 동안, 웹소켓이 열러있어 서브스크립션을 유지하기를 원할것입니다. 이때, takeUntil을 사용하면 라우터가 변할 때(routeChange) 자동으로 서브스크립션이 해지(unsubscribe)됩니다.

역자주: Angular의 router.events는 자체가 Observable 변수 입니다. 여기에 rx.js의 오퍼레이터 filter를 걸어 NavigationStart(URL이 변경될때 발생) 이벤트의 발생을 서브스크립션합니다. 다음은 filter에 대한 rxmarbles의 그림입니다.

Imgur

import { Component } from '@angular/core';
import { NavigationStart, Router } from '@angular/router';
import { Observable } from 'rxjs/Rx';
import { socketService } from '../services/socket';
import { IState } from '../reducers';

@Component({
    selector: 'example-component',
    templateUrl: './template.html',
})
export class ExampleComponent {

    public messages$: Observable<any>;
    public routeChange$: Observable<any>;
    
    constructor(
        public socket: socketService,
        public router: Router,
    ){}
    
    // listen to the socket untill there
    // is an event of NavigationStart
    // then complete
    public listenToSocket() {
      this.messages$
        .takeUntil(this.routeChange$)
        .subscribe((messages: any) => {
          // do something with the messages here
        })
    }
    
    public ngOnInit() {
        this.messages$ = this.socket.messages;
        this.routeChange$ = this.router.events.filter(event => event instanceof NavigationStart);
      
        this.listenToSocket();
    }

.takeWhile()

마지막은 필터처럼 사용될 수 있는 takeWhile입니다. Observable이 발산하는 각 값은 takeWhile이 받습니다. 그리고 받은 값을 조건문과 비교하여 불린값을 반환합니다. 결과가 거짓(false)이면 Observable은 종료됩니다.

Imgur

사용 예

ngrx/store 에서 특정 데이터의 수신이 완료될 수도 있는 상황에 대하여 takeWhile 을 사용할 수 있습니다.

import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { IState } from '../reducers';

@Component({
    selector: 'example-component',
    templateUrl: './template.html',
})
export class ExampleComponent {
    
    constructor(
        public store: Store<IState>,
    ){}

    // get the users from your ngrx/store
    // take values until the length is larger than 0
    // then complete
    public fetchUsersFromStore() {
        this.store.select(getUsers)
            .takeWhile(users => users.length > 0)
            .subscribe((users: IUser[]) {
                // do stuff with your users here
            })
    }
    
    public ngOnInit() {
        this.fetchUsersFromStore();
    }
}

결론

위 예제들은 실제 잘 사용되지는 않겠지만, first, take, elementAt, takeWhile, takeUntil을 전체적으로 unsubscribe()를 사용하지 않고 서브스크립션을 관리하는 방법에 대해 이해하기에는 좋은 예제들입니다.


읽어주셔서 감사합니다. 아래는 번역하지 않겠습니다. 😊 다만 저자에 대한 감사의 표시로 아래 링크들은 그대로 걸어놓습니다.

저자의 다른 글

Follow me on Medium or twitter and let’s connect on LinkedIn

 


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함