ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2-15 Observable사용하기
    앵귤러/01 퀵스타트 & 튜토리얼 2017. 8. 9. 09:10

    Observable사용하기

    Http서비스 메소드는 HTTP Response객채에서 Observable을 리턴한다.

    Observable은 배열과 같은 연산자로 처리 할 수 있는 이벤트 스트림이다.

     

    영웅조회 기능 추가

    영웅조회용 서비스를 생성한다.

    ng g service hero-service –m app

    src/app/hero-search.service.ts

    import { Injectable } from '@angular/core';

    import { HttpClient } from '@angular/common/http';

     

    import { Observable } from 'rxjs/Observable';

    import 'rxjs/add/operator/map';

     

    import { Hero } from './hero';

     

    @Injectable()

    export class HeroSearchService {

     

      constructor(private http:HttpClient) { }

     

      search(term:string): Observable<Hero[]>{

        return this.http

        .get(`http://localhost:3000/heroes/?name=${term}`)

        .map(response => response as Hero[]);

      }

     

    }

    http.get()메소드는 Observable을 리턴한다.

    map()메소드는 Obaervable 연산자로 response 데이터에서 Hero배열을 추출한다.

     

    영웅조회 컴포넌트를 생성한다.

    ng g component hero-search –-flat

    만들어진 컴포넌트 html수정

    src/app/hero-search.component.html

    <div id="search-component">

      <h4>영웅 조회</h4>

      <input #searchBox id="search-box" (keyup)="search(searchBox.value)"/>

      <div>

        <div *ngFor="let hero of heroes | async"

          (click)="gotoDetail(hero)" class="search-result">

          {{hero.name}}

        </div>

      </div>

    </div>

     

    만들어진 컴포넌트 css 수정

    src/app/hero-search.component.css

    .search-result{

        border-bottom: 1px solid gray;

        border-left: 1px solid gray;

        border-right: 1px solid gray;

        width: 195px;

        height: 16px;

        padding: 5px;

        background-color: white;

        cursor: pointer;

    }

     

    .search-result:hover{

        color: #eee;

        background-color: #607D8B;

    }

     

    #search-box{

        width: 200px;

        height: 20px;

    }

     

    이제 컴포넌트를 수정한다.

    src/app/hero-search.component.ts

    import { Component, OnInit } from '@angular/core';

    import { Router }            from '@angular/router';

     

    import { Observable }        from 'rxjs/Observable';

    import { Subject }           from 'rxjs/Subject';

     

    import 'rxjs/add/observable/of';

     

    import 'rxjs/add/operator/catch';

    import 'rxjs/add/operator/debounceTime';

    import 'rxjs/add/operator/distinctUntilChanged';

     

    import { HeroSearchService } from './hero-search.service';

    import { Hero } from './hero';

     

    @Component({

      selector: 'hero-search',

      templateUrl: './hero-search.component.html',

      styleUrls: ['./hero-search.component.css']

    })

    export class HeroSearchComponent implements OnInit {

      heroes: Observable<Hero[]>;

      private searchTerms = new Subject<string>();

     

      constructor(

        private heroSearchServie: HeroSearchService,

        private router:Router

      ) { }

     

      ngOnInit() {

        this.heroes = this.searchTerms.debounceTime(300)

        .distinctUntilChanged()

        .switchMap(term => term ? this.heroSearchServie.search(term) : Observable.of<Hero[]>([]))

        .catch(error => {

          console.log(error);

          return Observable.of<Hero[]>([]);

        });

      }

     

      search(term:string):void{

        this.searchTerms.next(term);

      }

     

      gotoDetail(hero:Hero): void {

        let link = ['/detail', hero.id];

        this.router.navigate(link);

      }

    }

    Subject Observable 이벤트 스트림을 생산한다. searchTerms는 문자열 Observable을 생성한다

    search ()를 호출 할 때마다, next ()를 호출하여 문자열의 관측 가능한 스트림에 새로운 문자열이 추가된다.

    private searchTerms = new Subject<string>();

     

    // Push a search term into the observable stream.

    search(term: string): void {

      this.searchTerms.next(term);

    }

     

    ngOnInit메소드에서는 검색어의 스트림을 Hero배열 스트림으로 변환하고 그 결과를 heroes 속성에 할당 할 수 있다.

    heroes: Observable<Hero[]>;

     

    ngOnInit(): void {

      this.heroes = this.searchTerms

        .debounceTime(300)        // wait 300ms after each keystroke before considering the term

        .distinctUntilChanged()   // ignore if next search term is same as previous

        .switchMap(term => term   // switch to new observable each time the term changes

          // return the http search observable

          ? this.heroSearchService.search(term)

          // or the observable of empty heroes if there was no search term

          : Observable.of<Hero[]>([]))

        .catch(error => {

          // TODO: add real error handling

          console.log(error);

          return Observable.of<Hero[]>([]);

        });

    }

    키입력이 발생할 때마다 HeroSearchService에 전달하면 과도한 HTTP요청이 만들어져 서버 리소스 및 네트워크 리소스에 부담이 된다.

    이를 해결하기 위해 Observable연산자를 연결하여 Observable 문자열에 대한 요청 흐름을 줄인다. HeroSearchService호출 횟수를 줄이기 위해 다음 방법을 사용하였다.

    l  debounceTime (300) : 새로운 문자열 이벤트의 흐름이 300 밀리 초 동안 일시 중지 될 때까지 기다린 후 최신 문자열을 전달한다. 300ms보다 자주 요청을하지 않는다.

    l  distinctUntilChanged : 필터 텍스트가 변경된 경우에만 요청이 전송되도록한다.

    l  switchMap() : debounce distinctUntilChanged를 통해 검색하는 각 검색어에 대해 검색 서비스를 호출한다. 이전 검색 Observable을 취소하고 파기하며 최신 검색 서비스 만 반환한다.

    l  catch : 실패한 Observable을 차단한다. 간단한 예로 콘솔에 오류를 인쇄한다. 그런 다음 검색 결과를 지우기 위해 빈 배열을 포함하는 Observable 객체를 리턴한다.

     

    RxJS 오퍼레이터 및 Observable 확장을 사용하기 위해서는 해당 오퍼페이터 및 Observable확장을 임포트해야 한다.

    import { Observable }        from 'rxjs/Observable';

    import { Subject }           from 'rxjs/Subject';

     

    // Observable class extensions

    import 'rxjs/add/observable/of';

     

    // Observable operators

    import 'rxjs/add/operator/catch';

    import 'rxjs/add/operator/debounceTime';

    import 'rxjs/add/operator/distinctUntilChanged';

     

    대시보드HTML에 조회컴포넌트를 추가한다.

    src/app/dashboard.component.html

    <h3>최고 영웅들</h3>

    <div class="grid grid-pad">

        <a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">

            <div class="module hero">

                <h4>{{hero.name}}</h4>

            </div>

        </a>

    </div>

    <hero-search></hero-search>

     

    댓글

Designed by Tistory.