CHAPTER 14. Send and receive data with Http

Intro

๋†€๋ผ์šด ์ผ์€ ์•„๋‹ˆ์ง€๋งŒ ๋งŽ์€ ์—…๋ฌด๋Š” ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์— ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๋ณด๋‚ด๋„๋ก ์š”์ฒญํ•œ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ WebSocket๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ๋Œ€์•ˆ์ด ์žˆ๋”๋ผ๋„ HTTP๋ฅผ ํ†ตํ•ด ์ˆ˜ํ–‰๋œ๋‹ค. Angular 2๋Š” http ๋ชจ๋“ˆ์„ ์ œ๊ณตํ•˜์ง€๋งŒ ๊ฐ•์ œ๋กœ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ์›ํ•˜๋Š” ๊ฒฝ์šฐ ์„ ํ˜ธํ•˜๋Š” HTTP ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

์‹ ์ž… ํšŒ์› ์ค‘ ํ•˜๋‚˜๋Š” ํ˜„์žฌ polyfill()๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ‘œ์ค€์ด ๋˜์–ด์•ผํ•˜๋Š” fetchAPI์ด๋‹ค. ๊ฐ€์ ธ ์˜ค๊ธฐ ๋˜๋Š” ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์„ ์™„๋ฒฝํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์‚ฌ์‹ค ๊ทธ๊ฒƒ์€ Http ๋ถ€๋ถ„์ด Angular2์—์„œ ์ˆ˜ํ–‰๋˜๊ธฐ ์ „์— ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒƒ์ด๋‹ค. ํ”„๋ ˆ์ž„ ์›Œํฌ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ–ˆ์Œ์„ ์ธ์‹ํ•˜๊ณ  ๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ์‹คํ–‰ํ•ด์•ผํ•œ๋‹ค๋Š” ํŠน๋ณ„ํ•œ ํ˜ธ์ถœ์ด ํ•„์š”์—†์ด ํ›Œ๋ฅญํ•˜๊ฒŒ ์ž‘๋™ํ•œ๋‹ค. (AngularJS์™€๋Š” ๋‹ฌ๋ฆฌ 1.x, ์—ฌ๊ธฐ์„œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด $ scope.apply ()๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค : ์ด๊ฒƒ์€ Angular 2์™€ ๊ทธ ์˜์—ญ์˜ ๋งˆ์ˆ ์ด๋‹ค!)

๊ทธ๋Ÿฌ๋‚˜ ํ”„๋ ˆ์ž„ ์›Œํฌ์— ์ต์ˆ™ํ•˜๋‹ค๋ฉด ํ•ต์‹ฌ ํŒ€์—์„œ ์ œ๊ณตํ•˜๋Š” HttpModule์ด๋ผ๋Š” ์ž‘์€ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๊ฒƒ์€ ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ •๋ง๋กœ, ๋‹น์‹ ์ด ์›ํ•˜๋Š” ๋Œ€๋กœ ํ•˜์‹œ์˜ค. Fetch API ์ œ์•ˆ์„ ์ถฉ๋ถ„ํžˆ ๋ฐ€์ ‘ํ•˜๊ฒŒ ๋ฐ˜์˜ํ•œ๋‹ค.

๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @ angular / httppackage์˜ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์™œ ์ด ๋ชจ๋“ˆ์„ fetch๋ณด๋‹ค ๋” ์„ ํ˜ธ ํ•˜๋Š”๊ฐ€? ๋Œ€๋‹ต์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์•ž์œผ๋กœ ์‚ดํŽด ๋ณด๊ฒ ์ง€๋งŒ Http ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋ฅผ ๋ชจ๋ฐฉํ•˜๊ณ  ๊ฐ€์งœ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ •๋ง, ์ •๋ง ์œ ์šฉํ•˜๋‹ค!! API๋กœ ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— ๋งˆ์ง€๋ง‰์œผ๋กœ, Http ๋ชจ๋“ˆ์€ ๋ฐ˜์‘ ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ๋”ฐ๋ผ์„œ Reactive Programming ์žฅ์„ ๊ฑด๋„ˆ ๋›ฐ๋ฉด ์ง€๊ธˆ ๋Œ์•„๊ฐ€์„œ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ์‹œ๊ฐ„์ด ๋  ๊ฒƒ์ด๋‹ค.)

Getting data

Http ๋ชจ๋“ˆ์€ Http๋ผ๋Š” ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ด ์„œ๋น„์Šค๋Š” ๋ชจ๋“  ์ƒ์„ฑ์ž์— ์‚ฝ์ž… ํ•  ์ˆ˜ ์žˆ๋‹ค. ์„œ๋น„์Šค๊ฐ€ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ ์˜ค๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ๊ตฌ์„ฑ ์š”์†Œ ๋‚˜ ์„œ๋น„์Šค์—์„œ ์ˆ˜๋™์œผ๋กœ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด HttpModule์„ ๋ฃจํŠธ ๋ชจ๋“ˆ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
@NgModule({
  imports: [BrowserModule, HttpModule],
  declarations: [PonyRacerAppComponent],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}

์ด ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด Httpservice๋ฅผ ํ•„์š”ํ•œ ๊ณณ ์–ด๋”” ์—๋‚˜ ์‚ฝ์ž… ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Component({
  selector: 'ponyracer-app',
  template: `<h1>PonyRacer</h1>`
})
export class PonyRacerAppComponent {
  constructor(private http: Http) {
  }
}

๊ธฐ๋ณธ์ ์œผ๋กœ Http ์„œ๋น„์Šค๋Š” XMLHttpRequest๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ AJAX ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ HTTP ๋™์‚ฌ์™€ ์ผ์น˜ํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.

โ€ข get โ€ข post โ€ข put โ€ข delete โ€ข patch โ€ข head

AngularJS 1.x์—์„œ $ http ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด ์•ฝ์†์— ํฌ๊ฒŒ ์˜์กดํ•˜๊ณ  ์žˆ์Œ์„ ๊ธฐ์–ตํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Angular 2์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๋ชจ๋“  ๋ฉ”์„œ๋“œ๊ฐ€ Observable ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Observables๋ฅผ Http๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์„ ์ทจ์†Œํ•˜๊ณ , ๋‹ค์‹œ ์‹œ๋„ํ•˜๊ณ , ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ๋“ฑ์˜ ๋ช‡ ๊ฐ€์ง€ ์ด์ ์ด ์žˆ๋‹ค.

PonyRacer์— ๋“ฑ๋ก ๋œ ๋ ˆ์ด์Šค๋ฅผ ๊ฐ€์ ธ์™€ ๋ณด์ž. ๋ฐฑ์—”๋“œ๊ฐ€ ์ด๋ฏธ ์‹คํ–‰๋˜์–ด RESTful API๋ฅผ ์ œ๊ณตํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž. ๊ฒฝ์ฃผ๋ฅผ ๊ฐ€์ ธ ์˜ค๋ ค๋ฉด 'http : //backend.url/api/races'์™€ ๊ฐ™์€ URL์— GET ์š”์ฒญ์„ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ HTTP ํ˜ธ์ถœ์˜ ๊ธฐ๋ณธ URL์€ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ์‰ฝ๊ฒŒ ๊ตฌ์„ฑ ํ•  ์ˆ˜์žˆ๋Š” ๋ณ€์ˆ˜ ๋˜๋Š” ์„œ๋น„์Šค์— ์ €์žฅ๋œ๋‹ค. ๋˜๋Š” REST API๊ฐ€ Angular ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋™์ผํ•œ ์„œ๋ฒ„์—์„œ ์ œ๊ณต๋˜๋Š” ๊ฒฝ์šฐ ์ƒ๋Œ€ URL ์ธ '/ api / races'๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

์ด๋Ÿฌํ•œ ์š”์ฒญ์€ Http ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๋‹ค.

http.get(`${baseUrl}/api/races`)

์ด๊ฒƒ์€ Observable์„ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, Observable์„ ์‚ฌ์šฉํ•˜์—ฌ ์‘๋‹ต ์ˆ˜์‹ ์„ ์‹ ์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ์‘๋‹ต์€ ๋ช‡ ๊ฐ€์ง€ ํ•„๋“œ์™€ ํŽธ๋ฆฌํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋Š” Response ๊ฐ์ฒด์ด๋‹ค. ์ƒํƒœ ์ฝ”๋“œ, ํ—ค๋” ๋“ฑ์„ ์‰ฝ๊ฒŒ ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๋‹ค.

http.get(`${baseUrl}/api/races`)
  .subscribe(response => {
   console.log(response.status); // logs 200
   console.log(response.headers); // logs []
  });

๋ฌผ๋ก  ์‘๋‹ต ๋ณธ๋ฌธ์ด ๊ฐ€์žฅ ํฅ๋ฏธ๋กœ์šด ๋ถ€๋ถ„์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์•ก์„ธ์Šคํ•˜๋ ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. โ€ข text () ์ผ๋ถ€ ํ…์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ถ„์„์ด ์ˆ˜ํ–‰๋œ๋‹ค. โ€ข json () JSON ๊ฐ์ฒด๋ฅผ ์˜ˆ์ƒํ•˜๋ฉด ๊ตฌ๋ฌธ ๋ถ„์„์ด ์ˆ˜ํ–‰๋œ๋‹ค.

http.get(`${baseUrl}/api/races`)
  .subscribe(response => {
   console.log(response.json());
   // logs the array of races
  });

๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ๋„ ๋งค์šฐ ์‰ฝ๋‹ค. ๊ฒŒ์‹œ ํ•  URL๊ณผ ๊ฐ์ฒด๊ฐ€์žˆ๋Š” post() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ๋งŒํ•˜๋ฉด ๋œ๋‹ค.

// you currently need to stringify the object you send
http.post(`${baseUrl}/api/races`, newRace)

Transforming data

Observable ๊ฐ์ฒด๋ฅผ ์‘๋‹ต์œผ๋กœ ๋ฐ›๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๋ณ€ํ˜• ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์žŠ์ง€ ๋งˆ์‹œ์˜ค. ๊ฒฝ์ฃผ ์ด๋ฆ„์˜ ๋ชฉ๋ก์„ ์›ํ•˜๋Š”๊ฐ€? ์ธ์ข…์„ ์š”์ฒญํ•˜๊ณ  ์ด๋ฆ„์„ ๋งคํ•‘ํ•˜๋ฉด ๋œ๋‹ค! ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด RxJS์— ๋Œ€ํ•œ mapoperator๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค. Angular ํŒ€์€ ์•ฑ์— ์ „์ฒด RxJS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํฌํ•จํ•˜๊ธฐ๋ฅผ ์›ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ช…์‹œ ์ ์œผ๋กœ ํ•„์š”ํ•œ ์—ฐ์‚ฐ์ž๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค.

import 'rxjs/add/operator/map';

์ด์ œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

http.get(`${baseUrl}/api/races`)
// extract json body
  .map(res => res.json())
  .subscribe(races => {
   // store the array of the races in the component
   this.races = races;
  });

์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์ž‘์—…์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ „์šฉ ์„œ๋น„์Šค๋กœ ์ˆ˜ํ–‰๋œ๋‹ค. RaceService์™€ ๊ฐ™์€ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒฝํ–ฅ์ด ์žˆ๋‹ค. RaceService๋Š” ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ๋œ ๊ณณ์ด๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‚ด ๊ตฌ์„ฑ ์š”์†Œ๋Š” ์„œ๋น„์Šค ๋ฐฉ๋ฒ•์„ ๊ตฌ๋… ํ•ด์•ผํ•˜๋ฉฐ ํ›„๋“œ ๋‚ด๋ถ€์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ์ง€ ๋ชปํ•œ๋‹ค.

raceService.list()
  .subscribe(races => {
   // store the array of the races in the component
   this.races = races;
  });

๋˜ํ•œ RxJS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹คํŒจํ•œ ์š”์ฒญ์„ ๋ช‡ ๋ฒˆ ๋‹ค์‹œ ์‹œ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

raceService.list()
  // if the request fails, retry 3 times
  .retry(3)
  .subscribe(races => {
   // store the array of the races in the component
   this.races = races;
  });

Advanced options

๋ฌผ๋ก  ๋ณด๋‹ค ์„ธ๋ฐ€ํ•˜๊ฒŒ ์š”์ฒญ์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ชจ๋“  ๋ฉ”์†Œ๋“œ๋Š” RequestOption ๊ฐ์ฒด๋ฅผ ์„ ํƒ์  ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์„ ๊ตฌ์„ฑ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ช‡ ๊ฐ€์ง€ ์˜ต์…˜์ด ์ •๋ง ์œ ์šฉํ•˜๋ฉฐ ์š”์ฒญ์˜ ๋ชจ๋“  ๊ฒƒ์„ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ์˜ต์…˜ ์ค‘ ์ผ๋ถ€๋Š” Fetch API์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๊ฐ’์„ ๊ฐ–๋Š”๋‹ค. url ์˜ต์…˜์€ ๋งค์šฐ ๋ช…ํ™•ํ•˜๋ฉฐ ์š”์ฒญ์˜ URL์„ ๋ฎ๋Š”๋‹ค. method ์˜ต์…˜์€ Request Method.Get๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  HTTP ๋™์‚ฌ์ด๋‹ค. ์ˆ˜๋™์œผ๋กœ ์š”์ฒญ์„ ์ž‘์„ฑํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

const options = new RequestOptions({ method: RequestMethod.Get });
http.request(`${baseUrl}/api/races/3`, options)
  .subscribe(response => {
   // will get the race with id 3
  });

search๋Š” URL์— ์ถ”๊ฐ€ ํ•  URL ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. URLSearchParams ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์ •ํ•˜๋ฉด ์ „์ฒด URL์ด ์ƒ์„ฑ๋œ๋‹ค.

const searchParams = new URLSearchParams();
searchParams.set('sort', 'ascending');
const options = new RequestOptions({ search: searchParams });
http.get(`${baseUrl}/api/races`, options)
  .subscribe(response => {
   // will return the races sorted
   this.races = response.json();
  });

ํ—ค๋” ์˜ต์…˜์€ ์š”์ฒญ์— ๋ช‡ ๊ฐ€์ง€ ๋งž์ถค ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, JSON Web Token๊ณผ ๊ฐ™์€ ์ผ๋ถ€ ์ธ์ฆ ๊ธฐ์ˆ ์— ํ•„์š”ํ•˜๋‹ค.

const headers = new Headers();
headers.append('Authorization', `Bearer ${token}`);
http.get(`${baseUrl}/api/races`, new RequestOptions({ headers }))
  .subscribe(response => {
   // will return the races visible for the authenticated user
   this.races = response.json();
  });

Jsonp

์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹œํ–‰๋˜๋Š” ๋™์ผ ์›์  ์ •์ฑ…์— ์˜ํ•ด ์ฐจ๋‹จ๋˜์ง€ ์•Š๊ณ  API์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๋„๋ก CORS๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  JSONP (Padding with JSON)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์›น ์„œ๋น„์Šค๊ฐ€ ์žˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ์ฝœ๋ฐฑ์œผ๋กœ ์ „๋‹ฌ ๋œ ํ•จ์ˆ˜๋กœ ๋ž˜ํ•‘ ํ•œ๋‹ค.

์‘๋‹ต์€ ์Šคํฌ๋ฆฝํŠธ๋กœ ๋Œ์•„์˜ค๊ณ  ์Šคํฌ๋ฆฝํŠธ๋Š” ๋™์ผํ•œ ์ถœ์ฒ˜ ์ •์ฑ…์˜ ์ ์šฉ์„๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ผ๋‹จ๋กœ๋“œ๋˜๋ฉด ์‘๋‹ต์— ํฌํ•จ ๋œ JSON ๊ฐ’์— ์•ก์„ธ์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

HttpModule ์™ธ์—๋„ JsonpService๋ฅผ ์ œ๊ณตํ•˜๋Š” JsonpModule์ด ์žˆ๋‹ค. JsonpModule์€ ์ด๋Ÿฌํ•œ API์™€ ์‰ฝ๊ฒŒ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜์žˆ๊ฒŒ ํ•ด์ฃผ๋ฉฐ, ์šฐ๋ฆฌ ๋ชจ๋‘์—๊ฒŒ ํ•„์š”ํ•œ ์ผ์„ ํ•œ๋‹ค. ํ˜ธ์ถœ ํ•  ์„œ๋น„์Šค์˜ URL์„ ์ง€์ •ํ•˜๊ณ  JSONP_CALLBACK์„ ์ฝœ๋ฐฑ ๋งค๊ฐœ ๋ณ€์ˆ˜ ๊ฐ’์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” JSONP๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Github ์กฐ์ง์—์„œ ๋ชจ๋“  ๊ณต๊ฐœ Repos๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

jsonp.get('https://api.github.com/orgs/Ninja-Squad/repos?callback=JSONP_CALLBACK')
// extract json
  .map(res => res.json())
  // extract data
  .map(res => res.data)
  .subscribe(response => {
   // will return the public repos of Ninja-Squad
   this.repos = response;
  });

Tests

์ด์ œ ์šฐ๋ฆฌ๋Š” ์ข…์กฑ์„ ๊ฐ€์ ธ ์˜ค๊ธฐ ์œ„ํ•ด HTTP ์ข…์ ์„ ํ˜ธ์ถœํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ๊ฐ–๊ฒŒ ๋œ๋‹ค. ์–ด๋–ป๊ฒŒ ํ…Œ์ŠคํŠธ ํ•˜๋Š”๊ฐ€?

@Injectable()
export class RaceService {
  constructor(private http: Http) {
  }
  list() {
   return this.http.get('/api/races').map(res => res.json());
  }
}

๋‹จ์œ„ ํ…Œ์ŠคํŠธ์—์„œ๋Š” HTTP ์„œ๋ฒ„๋ฅผ ์‹ค์ œ๋กœ ํ˜ธ์ถœํ•˜๊ณ  ์‹ถ์ง€ ์•Š๊ณ  ๊ฐ€์งœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด HTTP ํ˜ธ์ถœ์„ "๊ฐ€์งœ"๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด MockBackend๋ผ๋Š” ํ”„๋ ˆ์ž„ ์›Œํฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์งœ ๊ตฌํ˜„์œผ๋กœ Http ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ๋Œ€์ฒด ํ•  ์ˆ˜ ์žˆ๋‹ค.

import { async, TestBed } from '@angular/core/testing';
import { BaseRequestOptions, Response, ResponseOptions, RequestMethod } from '@angular/http';
import { MockBackend, MockConnection } from '@angular/http/testing';
import 'rxjs/add/operator/map';

describe('RaceService', () => {
  let raceService;
  let mockBackend;
  beforeEach(() => TestBed.configureTestingModule({
   providers: [
   MockBackend,
   BaseRequestOptions,
   {
   provide: Http,
   useFactory: (backend, defaultOptions) => new Http(backend, defaultOptions),
   deps: [MockBackend, BaseRequestOptions]
   },
   RaceService
   ]
  }));
  beforeEach(() => {
   raceService = TestBed.get(RaceService);
   mockBackend = TestBed.get(MockBackend);
  });
  it('should return an Observable of 2 races', async(() => {
   // fake response
   const hardcodedRaces = [new Race('London'), new Race('Lyon')];
   const response = new Response(new ResponseOptions({ body: hardcodedRaces }));
   // on a the connection
   mockBackend.connections.subscribe((connection: MockConnection) => {
   // return the fake response when we receive a request
   connection.mockRespond(response);
   });
   // call the service
   raceService.list().subscribe(races => {
   // check that the returned array is deserialized as expected
   expect(races.length).toBe(2);
   });
  }));
});

๋˜ํ•œ ๊ธฐ๋ณธ HTTP ์š”์ฒญ์— ๋ช‡ ๊ฐ€์ง€ ๋‹จ์ •๋ฌธ์„ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

mockBackend.connections.subscribe((connection: MockConnection) => {
  // return the fake response when we receive a request
  connection.mockRespond(response);
  // check that the underlying HTTP request was correct
  expect(connection.request.method).toBe(RequestMethod.Get);
  expect(connection.request.url).toBe('/api/races');
});

Reference URL

Last updated