티스토리 뷰

Angular.io 에서 제공하는 튜토리얼을 정리한 것입니다.

사진 클릭시 튜토리얼로 이동

!! RC4기준 !!

7. Http 

1. Providing HTTP Services



이번 장에서는 원격 서버의 웹 API에 대응하는 HTTP 호출을 하는 응용 프로그램을 만들것이다.


 HTTP는 core Angular module이 아니다. npm package를 통해서 @angular/http 라는 add-on module로 제공된다. 튜토리얼을 따라왔다면 우리는 systemjs.config에서 필요한 라이브러리들을 이미 다운 받았다.


 HTTP를 어디에서나 엑세스하기 위하여 main.ts 의 bootstrap에 등록한다. HTTP_PROVIDERS를 bootstrap의 2번째 파라미터로 등록하는 것은 @Component의 providers 배열과 같은 효과를 나타낸다.


BootStrap 동작과정 더보기



 현재 요청을 처리할 수 있는 web server가 없다. 그래서 우리는 가짜 서버를 사용할 것이다. mock service에서 가져오고 저장하는 것을 in-memory web API를 이용하여 HTTP 클라이언트를 속일 것이다. mock-heroes.ts 를 삭제하고 임의의 데이터를 가질 in-memory-data.service.ts 생성하자.



< main.ts >


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Import for loading & comfiguring the in-memory web api
import {XHRBackend} from '@angular/http';
import {InMemoryBackendService, SEED_DATA} from 'angular2-in-memory-web-api';
import {InMemoryDataService} from './in-memory-data.service';
 
// The usual bootstrapping imports
import { bootstrap } from '@angular/platform-browser-dynamic';
import {HTTP_PROVIDERS} from '@angular/http';
 
import { AppComponent } from './app.component';
import { appRouterProviders } from './app.routes';
 
bootstrap(AppComponent,[
    appRouterProviders,
    HTTP_PROVIDERS,
    {provide :XHRBackend, useClass:InMemoryBackendService},
    {provide : SEED_DATA, useClass:InMemoryDataService}
]).catch(err => console.error(err));
cs



< in-memory-data.service.ts >


1
2
3
4
5
6
7
8
9
10
11
12
export const HEROES: Hero[] = [
    {id: 11, name: 'Mr. Nice'},
    {id: 12, name: 'Narco'},
    {id: 13, name: 'Bombasto'},
    {id: 14, name: 'Celeritas'},
    {id: 15, name: 'Magneta'},
    {id: 16, name: 'RubberMan'},
    {id: 17, name: 'Dynama'},
    {id: 18, name: 'Dr IQ'},
    {id: 19, name: 'Magma'},
    {id: 20, name: 'Tornado'}
];
cs



< hero.service.ts >


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
import { Injectable } from '@angular/core';
import {Http, Headers} from "@angular/http";
 
import 'rxjs/add/operator/toPromise';
 
import { Hero } from './hero';
 
@Injectable()
export class HeroService {
 
    private heroesUrl='app/heroes';// URL to web api
 
    constructor(private http: Http){    }
 
    getHeroes() {
        return this.http.get(this.heroesUrl).toPromise()
            .then(response=>response.json().data as Hero[])
            .catch(this.handleError);
    }
 
    getHero(id: number){
        return this.getHeroes()
            .then(heroes=>heroes.find(hero=>hero.id===id));
    }
    
    private handleError(error:any){
        console.error('An error occurred', error);
        return Promise.reject(error.message||error);
    }
}
cs

[line 15 - 20] 비동기 작업을하기 위하여 hero.service.ts의 getHeroes()가 HTTP를 사용하도록 변경한다. 우리는 여전히 Promise를 반환하지만 getHeroes()를 호출하였을 때 어떠한 컴포넌트도 업데이트 할 필요가 없다.


 앵귤러의 http.get은 RxJS의 Observable을 반환한다. Observable은 비동기 데이터 흐름을 관리 할 수 있는 강력한 방법이다.


Observable 참고 영상 보기 


RxJS 란?




2. hero.service.ts


HeroService에 히어로를 post,put 그리고 delete하는 메소드 추가.


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import 'rxjs/add/operator/toPromise';
 
import { Hero } from './hero';
 
@Injectable()
export class HeroService {
 
    private heroesUrl='app/heroes';// URL to web api
 
    constructor(private http: Http){    }
 
    getHeroes() {
        return this.http.get(this.heroesUrl).
        toPromise().
        then(response=>
            response.json().data as Hero[])
            .catch(this.handleError);
    }
 
    getHero(id: number){
        return this.getHeroes()
            .then(heroes=>heroes.find(hero=>hero.id===id));
    }
 
    save(hero: Hero): Promise<Hero>{
        if(hero.id){
            return this.put(hero);
        }
        return this.post(hero);
    }
    delete(hero:Hero){
        let headers=new Headers();
        headers.append('Content-Type','application/json');
 
        let url=`${this.heroesUrl}/${hero.id}`;
 
        return this.http
            .delete(url,{headers:headers})
            .toPromise().catch(this.handleError);
    }
 
    // Add new Hero
    private post(hero: Hero): Promise<Hero>{
        let headers = new Headers({
            'Content-Type' : 'application/json'
        });
 
        return this.http.post(this.heroesUrl,JSON.stringify(hero),{headers:headers})
            .toPromise().then(res=> res.json().data).catch(this.handleError);
    }
 
    // Update existing Hero
    private put(hero: Hero){
        let headers = new Headers();
        headers.append('Content-Type','application/json');
 
        let url=`${this.heroesUrl}/${hero.id}`;
       
        return this.http.put(url,JSON.stringify(hero),{headers:headers})
            .toPromise().then(()=>hero).catch(this.handleError);
    }
 
    private handleError(error:any){
        console.error('An error occurred', error);
        return Promise.reject(error.message||error);
    }
}
cs


Post

새로운 히어로를 추가하기 위해 post 사용. header 생성, content type을 application/json으로 셋팅한다. 히어로 객체를 String으로 변환하여 Post하기 전에 JSON.stringify를 호출한다.


Put

hero 각각을 수정하기 위해 사용. Post request와 구조 비슷하다.

유일한 차이점은 우리가 업데이트 할 히어로의 ID를 추가하여 URL을 살짝 변경해야 할 것이다.


Delete

put과 이름만 다른뿐 형식은 같다.


Save

Post, put을 합칠 것이다. public API를 통해서 쉽게 합칠 수 있다.

HeroService는 hero의 상태를 통해 호출할 메소드를 결정한다. 히어로가 이미 ID를 가지고 있다면 수정을 하고 그렇지 않다면 히어로를 추가한다.


 !!주의!! 

delete,sava의 url 작성할때 엔터옆의  ' 가 아니라 탭 위의  ` 임!

   Apostrophe (어퍼스트로피)      Grave (그레이브), back quator(백쿼터)  



3. hero-detail.component.ts


히어로를 더하고 삭제하는 메소드를 사용하기 위해 컴포넌트를 수정


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import {Component, OnInit, OnDestroy, Input, Output, EventEmitter} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
 
import { Hero } from './hero';
import { HeroService } from './hero.service';
 
@Component({
    selector: 'my-hero-detail',
    templateUrl: 'app/hero-detail.component.html',
    styleUrls:['app/hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit, OnDestroy {
    @Input() hero: Hero;
    @Output() close= new EventEmitter();
    error : any;
    sub: any;
    navigated = false;// true if navigated here
 
    constructor(
        private heroService: HeroService,
        private route: ActivatedRoute) {
    }
 
    ngOnInit() {
        this.sub = this.route.params.subscribe(params => {
            if(params['id']!==undefined){
                let id = +params['id'];
                this.navigated=true;
                this.heroService.getHero(id)
                    .then(hero => this.hero = hero);
            }else{
                this.navigated=false;
                this.hero=new Hero();
            }
 
        });
    }
 
    save(){
        this.heroService.save(this.hero).then(hero=>{
            this.hero=hero;
            this.goBack(hero);
        }).catch(error=> this.error=error);
    }
 
    ngOnDestroy() {
        this.sub.unsubscribe();
    }
 
    goBack(saveHero:Hero=null) {
        this.close.emit(saveHero);
        if(this.navigated){
            window.history.back();
        }
    }
}
cs



[line 26 - 34] Add, Delete를 구별하기 위해 URL에 ID가 있는지를 체크하는 부분을 추가하였다. ID가 없으면 빈 히어로 객체에 HeroDetailComponent를 바인딩한다. UI를 통해 이루어진 수정 사항은 동일한 hero property에 다시 바운딩된다.


[line 60 - 65] goBack()은 히어로를 저장한 후 다시 이전 페이지로 redirect 한다.


[line 61] emit : HeroDetailComponent 와 HeroesComponent 사이의 handshake




2. HTML 수정


- hero-detail.component.html의 Back 버튼 옆에 Sava 버튼 만들기

<button (click)="save()">Save</button>


- heroes.component.html의 히어로(</li> 태그 앞에) Delete 버튼 만들기

<button class="delete-button" (click)="deleteHero(hero, $event)">Delete</button>

- heroes.component.html의 히어로 리스트(</ul>태그 뒤에)에 추가하기

- 첫번째 라인은 에러가 있을 경우 에러 메세지 표시, 나머지 부분은 히어로 추가

<div class="error" *ngIf="error">{{error}}</div>
<button (click)="addHero()">Add New Hero</button>
<div *ngIf="addingHero">
  <my-hero-detail (close)="close($event)"></my-hero-detail>
</div>

 !!주의!! 

*ngIf 할때 *ngif로 작성했는데 오류를 잡아주지 않았다. 어디에도 오류가 없는데 브라우저에 띄워지지 않아서 열심히 해맴(사용하던 툴의 문제였는지 / WebStorm 사용중)



[ Angular 2.0에서 $http를 대체하는 것 ]

http://stackoverflow.com/questions/29361027/angularjs-2-alternative-http


Browser에서 Angular를 Loading 하는 과정



댓글
댓글쓰기 폼