My Profile Photo

DongChanS's blog


수학과 학생의 개발일지


Angular (7) Tutorial - Services(2) - Observable

위의 튜토리얼은 https://angular.io/tutorial 을 참고하였습니다.

1) Observable data

그런데 위에서 짠 로직은 실제 현업에서 적용하기에는 심각한 문제가 있습니다.

바로 동기화적인 로직이라는 건데요. 이에 대해서 알아봅시다.

  • HeroService.getHeroes() : synchronous signature

    HeroService 객체는 hero들을 동기적으로 가져옵니다.

    => 즉, HeroesComponent가 동기적으로 가져온 데이터들을 렌더링합니다.

  • 주의점!

    this.heroes = this.heroService.getHeroes();
    

    만약 우리 데이터를 외부 서버에서 받아오는 방식이라면,

    getHeroes() 메서드는 틀림없이 비동기적으로 작동하기 때문에 this.heroes에 리턴값이 바로 반영이 안됩니다.

이런 결점을 극복하기 위해서 Observable이라는 녀석이 있습니다.

얘는 Angular에서만 지원하는건 아니고, 자바스크립트 ES7에서 추가된 비동기적인 데이터를 관리하는 기능입니다.

1-1. Observable HeroService

angular에서 지원하는 HttpClient 메서드 : RxJS observable들을 리턴함.

  • Observable?

    쉽게 말하면 데이터의 변화상태를 감지하는 녀석이라고 생각하면 됨

    => 이전에 받아온 데이터와 비교해서 변화한 부분만 바꿔주는게 프론트엔드 라이브러리의 역할

    => virtual DOM(가상으로 DOM을 만들어둬서 바뀐 부분에 대해서만 다시 렌더링을 해줘서 상당히 효율적임, createDocumentFragment)과 비슷한 느낌임.

사용법)

  1. Observable을 임포트함.

    // hero.service.ts
    import { Observable, of } from 'rxjs';
    
  2. Observable[]을 리턴하게끔 메서드를 바꾼다.

    // hero.service.ts
    getHeroes(): Observable<Hero[]> {
      return of(HEROES);
    }
    

    of(HEROES) : Observable<Hero[]>를 반환하는 함수.

  3. 이렇게 서비스단에서 Observable을 반환하도록 함수가 바꼇으니까

    Component에서도 얘를 사용할 수 있게 설정을 해야한다.

    getHeroes(): void {
        this.heroService.getHeroes()
        	.subscribe( heroes => this.heroes = heroes);
    }
    

    observable을 안쓰고 동기적으로 메서드를 작동하게 되면 이 데이터를 다 받아올때까지 브라우저 UI가 멈춘다.

    observable을 쓴다면 observable이 array를 받아올 때 까지 기다리고, subscribe가 callback으로 이 array를 실제 heroes 프로퍼티에 반영한다. (비동기적으로 처리)

그런데, 비동기적인 데이터를 받아올 때는 보통 Promise라는 것을 쓰고, subscribe 대신에 then을 사용했었습니다.

사실 두 객체는 약간 차이가 있는데요.

Observable은 시간에 따라서 여러 값을 가질 수 있고, 이렇게 변하고 있는 값들을 주기적으로 받아올 수 있습니다.

  • 생산자는 데이터를 지속적으로 바꾸고 제공한다.
  • 소비자는 데이터를 가져올 시간을 정함 => 그래서 생산자 입장에서는 어떻게 가져갔는지 알 수 없음.

반면에 Promise는 시간에 따라서 값이 변하지 않으나, 소비자 입장에서 데이터를 어떻게 가져올 지를 제어할 수 있다는 장점이 있습니다.

1-2. Show Messages

이제는 다른 Service를 만들어 봅시다.

우리가 HEROES 데이터를 성공적으로 받아올 때 마다 메세지를 뿌려주고 싶습니다.

그러기 위해서는 두개의 기능이 필요한데요.

메세지를 가져오는 Service와 메세지를 보여주는 Component입니다.

  1. MessageComponent : app message를 밑에 보여주는 역할.
  2. MessageService : injectable service, 1번이 보여줄 메세지를 준비하기 위한 서비스
  3. HeroService : MessageService를 가짐. (inject 된다.)
  4. HeroService가 hero들을 성공적으로 컴포넌트에 제공하면(fetch), MessageService에서 받은 메세지를 뿌려준다.
  • Component 만들기

    ng generate component messages
    
  • component를 루트 html파일에 추가

    <!-- app.component.html -->
    <app-messages></app-messages>
    
  • MessageService 뼈대 만들기

    ng generate service message
    
    1. messages : 메세지들의 array
    2. add 메서드 : messages 배열에 메세지 추가
    3. clear : 메세지 array를 비움.

    => heroservice에서 데이터를 성공적으로 받을때마다 messages에 메세지를 추가하고, 이걸 message component에 뿌려주는 역할입니다.

    // message.service.ts
    export class MessageService {
      messages: string[] = [];
      
      add(message: string): void {
        this.messages.push(message);
      }
      clear(): void {
        this.messages = [];
      }
      constructor() { }
    }
    
  • HeroService에 MessageService를 inject하기

    1. 임포트

    2. construcor에서 MessageService 객체 생성

      => private 식별자를 반드시 선언해야함!!! (그래야 객체화되는 듯 합니다.)

    3. fecth 성공할 때 마다 MessageService 객체에 메세지를 추가하기.

    // hero.service.ts
    export class HeroService {
      getHeroes(): Observable<Hero[]> {
        this.messageService.add('fetch 성공적');
        return of(HEROES);
      }
      constructor(private messageService: MessageService) { }
    }
    
  • MessageComponent에서 MessageService의 메세지를 받기.

    1. HeroService의 객체를 MessageComponent가 가지고있기

      // messages/messages.component.ts
      construct(private messageService: MessageService){
      }
      
    2. 그 객체를 html파일에 렌더하기

      • clear 버튼
      • 메세지들의 리스트
           
      <!-- messages/messages.component.html -->
      <div *ngIf="messageService.messages.length">
        <h2>Messages</h2>
        <button (click)="messageService.clear()">Clear</button>
        <ul>
          <li *ngFor="let message of messageService.messages">
            <span>{{message}}</span>
          </li>
        </ul>
      </div>
           
      

10

comments powered by Disqus