CHAPTER 6. Dependency injection

DI yourself

์˜์กด์„ฑ ์ฃผ์ž…์€ ์ž˜ ์•Œ๋ ค์ง„ ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค. ์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ดํŽด ๋ณด๋„๋ก ํ•˜์ž. ์ด ๊ตฌ์„ฑ ์š”์†Œ๋Š” ์•ฑ์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์—์„œ ์ œ๊ณตํ•˜๋Š” ์ผ๋ถ€ ๊ธฐ๋Šฅ์„ ํ•„์š”๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค. (์„œ๋น„์Šค ๋ผ๊ณ  ๋ถ€๋ฅด๋„๋ก ํ•˜์ž). ๊ทธ๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์ข…์†์„ฑ์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ฒƒ์ด๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ข…์†์„ฑ์„ ๋งŒ๋“ค๋„๋กํ•˜๋Š” ๋Œ€์‹ , ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š”์ด๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๊ตฌ์„ฑ ์š”์†Œ์— ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ "์ œ์–ด ๋ฐ˜์ „ (inversion of control)"์ด๋ผ๊ณ  ํ•œ๋‹ค.

์—ฌ๊ธฐ์—๋Š” ๋ช‡ ๊ฐ€์ง€ ํฅ๋ฏธ๋กœ์šด ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค. โ€ข ๊ทธ๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•˜๊ณ  ์›ํ•˜๋Š” ๊ณณ์„ ๋งํ•จ์œผ๋กœ์จ ์‰ฝ๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค. โ€ข ์˜์กด์„ฑ์„ ๋ชจ์˜ ๊ฐ์ฒด๋กœ ๋Œ€์ฒดํ•˜์—ฌ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค. โ€ข ๊ตฌํ˜„์„ ๋ฐ”๊พธ๋ฉด ์‰ฝ๊ฒŒ ๊ตฌ์„ฑ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๊ฒƒ์€ ์„œ๋ฒ„ ์ธก์—์„œ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋…์ด์ง€๋งŒ AngularJS 1.x๋Š” ํ”„๋ก ํŠธ ์—”๋“œ ์ธก์—์„œ ์ฒ˜์Œ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜์ด๋‹ค.

Easy to develop

์˜์กด์„ฑ ์‚ฝ์ž…์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ช‡ ๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•˜๋‹ค. โ€ข ์ข…์†์„ฑ์„ ๋“ฑ๋กํ•˜์—ฌ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ / ์„œ๋น„์Šค์— ์ฃผ์ž… ํ•  ์ˆ˜์žˆ๊ฒŒํ•˜๋Š” ๋ฐฉ๋ฒ• โ€ข ํ˜„์žฌ ๊ตฌ์„ฑ ์š”์†Œ / ์„œ๋น„์Šค์—์„œ ์–ด๋–ค ์ข…์†์„ฑ์ด ํ•„์š”ํ•œ์ง€ ์„ ์–ธํ•˜๋Š” ๋ฐฉ๋ฒ•

ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ๋‚˜๋จธ์ง€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ปดํฌ๋„ŒํŠธ์— ์˜์กด์„ฑ์„ ์„ ์–ธ ํ•  ๋•Œ, ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ์กฐ์‚ฌ ํ•  ๊ฒƒ์ด๊ณ , ์˜์กด์„ฑ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์–ป๊ฑฐ๋‚˜, ์˜์กด์„ฑ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ ํ•  ๊ฒƒ์ด๋ฉฐ ์‹ค์ œ๋กœ ์ปดํฌ๋„ŒํŠธ์— ์˜์กด์„ฑ์ด ์ฃผ์ž… ๋  ๊ฒƒ์ด๋‹ค.

์˜์กด์„ฑ์€ Angular๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์„œ๋น„์Šค์ด๊ฑฐ๋‚˜ ์šฐ๋ฆฌ๊ฐ€ ์Šค์Šค๋กœ ์ž‘์„ฑํ•œ ์„œ๋น„์Šค ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฏธ ๋™๋ฃŒ ์ค‘ ํ•œ ๋ช…์ด ์ž‘์„ฑํ•œ ApiServiceservice๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด ๋ณด์ž. ๊ทธ๋Š” ๊ฒŒ์œผ๋ฅธ ์นœ๊ตฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋นˆ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š” get ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์„ฑ๋งŒ ํ•˜์˜€๋‹ค. ํ•ด๋‹น ์„œ๋น„์Šค๊ฐ€ ๋ฐฑ์—”๋“œ API์™€ ํ†ต์‹ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์œผ๋กœ ์ด๋ฏธ ์ถ”์ธก ํ•  ์ˆ˜ ์žˆ๋‹ค.

export class ApiService {
  get(path) {
   // todo: call the backend API
  }
}

TypeScript๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ตฌ์„ฑ ์š”์†Œ ๋‚˜ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์ข…์†์„ฑ์„ ์„ ์–ธํ•˜๊ธฐ ์‰ฝ๋‹ค. ApiService๋ฅผ ์‚ฌ์šฉํ•˜๋Š” RaceService๋ฅผ ์ž‘์„ฑํ•˜๋ ค๊ณ ํ•œ๋‹ค๊ณ  ๊ฐ€์ • ํ•ด ๋ณด์ž.

import { ApiService } from './api.service';
export class RaceService {
  constructor(private apiService: ApiService) {
  }
}

Angular๋Š” ApiService ์„œ๋น„์Šค๋ฅผ ๊ฐ€์ ธ ์™€์„œ ์ƒ์„ฑ์ž์— ์‚ฝ์ž…๋˜๊ณ  RaceService๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์ƒ์„ฑ์ž๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ApiService ํ•„๋“œ๊ฐ€ ApiService ์„œ๋น„์Šค๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ๋œ๋‹ค. ์ด์ œ ApiService ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฑ์—”๋“œ์— ํ˜ธ์ถœ ํ•  ๋ฉ”์†Œ๋“œ ๋ชฉ๋ก ()์„ ์„œ๋น„์Šค์— ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

import { ApiService } from './api.service';
export class RaceService {
  constructor(private apiService: ApiService) {
  }
  list() {
   return this.apiService.get('/races');
  }
}

Angular 2 ์—์„œ๋Š” ์„œ๋น„์Šค ์ž์ฒด์— ์˜์กด์„ฑ์ด ์žˆ์Œ์„ ์•Œ๋ฆฌ๋ ค๋ฉด ํด๋ž˜์Šค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ('@Injectable()')๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

import { Injectable } from '@angular/core';
import { ApiService } from './api.service';
@Injectable()
export class RaceService {
  constructor(private apiService: ApiService) {
  }
  list() {
   return this.apiService.get('/races');
  }
}

์ด๊ฒƒ์„ ํ•˜๊ธฐ ์œ„ํ•œ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ์•ž์—์„œ ๋ณธ @NgModule ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ providers ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { PonyRacerAppComponent } from './app.component';
import { ApiService } from './services/api.service';
@NgModule({
  imports: [BrowserModule],
  declarations: [PonyRacerAppComponent],
  providers: [
   ApiService
  ],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}

์ด์ œ ReceService ๋ฅผ ๋‹ค๋ฅธ ์„œ๋น„์Šค ๋‚˜ ๊ตฌ์„ฑ ์š”์†Œ์— ์ฃผ์ž… ํ•  ์ˆ˜์žˆ๊ฒŒํ•˜๋ ค๋ฉด ๋“ฑ๋กํ•ด์•ผํ•œ๋‹ค.

import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { PonyRacerAppComponent } from './app.component';
import { RaceService } from './services/race.service';
import { ApiService } from './services/api.service';
@NgModule({
  imports: [BrowserModule],
  declarations: [PonyRacerAppComponent],
  providers: [
   RaceService,
   ApiService
  ],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}

์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์›ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. PonyRacerAppComponent ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ํ…Œ์ŠคํŠธ ํ•ด ๋ณด๋„๋ก ํ•˜์ž.

import { Component } from '@angular/core';
import { RaceService } from './services/race.service';
@Component({
  selector: 'ponyracer-app',
  template: `<h1>PonyRacer</h1>
   <p>{{list()}}</p>`
})
export class PonyRacerAppComponent {
  // add a constructor with RaceService
  constructor(private raceService: RaceService) {
  }
  list() {
   return this.raceService.list();
  }
}

์šฐ๋ฆฌ์˜ ๊ฒŒ์œผ๋ฅธ ๋™๋ฃŒ๊ฐ€ ApiService์˜ get ๋ฉ”์†Œ๋“œ์—์„œ ๋นˆ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ ํ–ˆ์œผ๋ฏ€๋กœ list () ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๊ณ ํ•˜๋ฉด ์•„๋ฌด ๊ฒƒ๋„ ์–ป์„ ์ˆ˜ ์—†๋‹ค.

Easy to Configure

๋‹ค์Œ ์žฅ์—์„œ ์ข…์†์„ฑ ์ฃผ์ž…์œผ๋กœ ์ธํ•œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ ์ด์ ์œผ๋กœ ๋“ค์•„๊ฐ€ ๊ฒ ์ง€๋งŒ ๊ตฌ์„ฑ ๋ฌธ์ œ๋ฅผ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ฐฑ์—”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ์–ด์ฉŒ๋ฉด ๋ฐฑ์—”๋“œ ํŒ€์ด ์•„์ง ์ค€๋น„๊ฐ€๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ๋‚˜์ค‘์—ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ๋‹ค. ์–ด์จŒ๋“  ์šฐ๋ฆฌ๋Š” ๊ฐ€์งœ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค.

import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { PonyRacerAppComponent } from './app.component';
import { RaceService } from './services/race.service';
import { ApiService } from './services/api.service';
@NgModule({
  imports: [BrowserModule],
  declarations: [PonyRacerAppComponent],
  providers: [
   RaceService,
   ApiService
  ],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}
import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { PonyRacerAppComponent } from './app.component';
import { RaceService } from './services/race.service';
import { ApiService } from './services/api.service';
@NgModule({
  imports: [BrowserModule],
  declarations: [PonyRacerAppComponent],
  bootstrap: [PonyRacerAppComponent],
  providers: [
   { provide: RaceService, useClass: RaceService },
   { provide: ApiService, useClass: ApiService }
  ]
})
export class AppModule {
}

์ธ์ ํ„ฐ๋Š” ํ† ํฐ (RaceService ์œ ํ˜•)๊ณผ RaceService ํด๋ž˜์Šค ์‚ฌ์ด์— ์—ฐ๊ฒฐ ํ•ด ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. Injector๋Š” ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ์œ ์ง€ ๊ด€๋ฆฌํ•˜์—ฌ ์ฃผ์ž… ๊ฐ€๋Šฅํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ถ”์ ํ•˜๊ณ  ํ•„์š”ํ•  ๋•Œ ์‹ค์ œ๋กœ ์ฃผ์ž…ํ•˜๋Š” ์„œ๋น„์Šค์ด๋‹ค. ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋Š” ํ† ํฐ์ด๋ผ๊ณ ํ•˜๋Š” ํ‚ค๋ฅผ ํด๋ž˜์Šค์™€ ์—ฐ๊ด€์‹œํ‚ค๋Š” ๋งต์ด๋‹ค. ํ† ํฐ์€ ๋งŽ์€ ์˜์กด์„ฑ ์‚ฝ์ž… ํ”„๋ ˆ์ž„ ์›Œํฌ์™€ ๋‹ฌ๋ฆฌ ๋ฐ˜๋“œ์‹œ ๋ฌธ์ž์—ด์„ ์•Œ ํ•„์š”๋Š” ์—†๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Type ์ฐธ์กฐ์™€ ๊ฐ™์€ ์š”์†Œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ณ  ๊ทธ๊ฒƒ์€ ๋ณดํ†ต ๊ฒฝ์šฐ ์ด๋‹ค.

์ด ์˜ˆ์—์„œ๋Š” ํ† ํฐ๊ณผ ์‚ฝ์ž… ํ•  ํด๋ž˜์Šค๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ ๊ฐ™์€ ์–‘์‹์„ ๋” ์งง์€ ํ˜•์‹์œผ๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { PonyRacerAppComponent } from './app.component';
import { RaceService } from './services/race.service';
import { ApiService } from './services/api.service';
@NgModule({
  imports: [BrowserModule],
  declarations: [PonyRacerAppComponent],
  providers: [
   RaceService,
   ApiService
  ],
  bootstrap: [PonyRacerAppComponent]
})
export class AppModule {
}

ํ† ํฐ์€ ์ข…์†์„ฑ์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•ด์•ผํ•œ๋‹ค. ์ด ์ฃผ์‚ฌ๊ธฐ๋Š” bootstrapModule promise์— ์˜ํ•ด ๋ฐ˜ํ™˜๋˜๋ฏ€๋กœ ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฑธ ๊ฐ€์ง€๊ณ  ๋†€ ์ˆ˜ ์žˆ๋‹ค.

// in our module
providers: [
  ApiService,
  { provide: RaceService, useClass: RaceService },
  // let's add another provider to the same class
  // with another token
  { provide: 'RaceServiceToken', useClass: RaceService }
]
// let's bootstrap the module
platformBrowserDynamic().bootstrapModule(AppModule)
.then(
  // and play with the returned injector
  appRef => playWithInjector(appRef.injector)
);
function playWithInjector(inj) {
  console.log(inj.get(RaceService));
  // logs "RaceService {apiService: ApiService}"
  console.log(inj.get('RaceServiceToken'));
  // logs "RaceService {apiService: ApiService}" again
  console.log(inj.get(RaceService) === inj.get(RaceService));
  // logs "true", as the same instance is returned every time for a token
  console.log(inj.get(RaceService) === inj.get('RaceServiceToken'));
  // logs "false", as the providers are different,
  // so there are two distinct instances
}

๋ณด์‹œ๋‹ค์‹œํ”ผ get ๋ฉ”์†Œ๋“œ์™€ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ ํ„ฐ์— ์˜์กด์„ฑ์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‘ ๊ฐ€์ง€ ๋‹ค๋ฅธ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜์—ฌ RaceService๋ฅผ ๋‘ ๋ฒˆ ์„ ์–ธ ํ–ˆ์œผ๋ฏ€๋กœ ๋‘ ๊ฐ€์ง€ ๊ณต๊ธ‰์ž๊ฐ€ ์žˆ๋‹ค. ์ธ์ ํ„ฐ๋Š” ์ฒ˜์Œ์œผ๋กœ ํŠน์ • ํ† ํฐ์„ ์š”์ฒญ ๋ฐ›์•˜์„ ๋•Œ RaceService์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“  ๋‹ค์Œ ๋งค๋ฒˆ์ด ํ† ํฐ์— ๋Œ€ํ•ด ๋™์ผํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ฐ ๊ณต๊ธ‰์ž๋งˆ๋‹ค ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋Š” ์‹ค์ œ๋กœ ์•ฑ์— ๋‘ ๊ฐœ์˜ RaceService ์ธ์Šคํ„ด์Šค๊ฐ€ ์žˆ๊ณ  ๊ฐ ํ† ํฐ์— ํ•˜๋‚˜์”ฉ ์žˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํ† ํฐ์„ ์ž์ฃผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์ „ํ˜€ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. TypeScript์—์„œ๋Š” ํ˜•์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—…์„ ์™„๋ฃŒํ•˜๋ฏ€๋กœ ํ† ํฐ์€ Type ์ฐธ์กฐ์ด๋ฉฐ ๋Œ€๊ฐœ ํ•ด๋‹น ํด๋ž˜์Šค์— ๋ฐ”์ธ๋”ฉ๋œ๋‹ค. ๋‹ค๋ฅธ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @Inject () ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ์žฅ์˜ ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„์„ ์ฐธ์กฐํ•˜๋ผ.

์ด ์ „์ฒด ์˜ˆ์ œ๋Š” ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ์„ ์ง€์ ํ•˜๊ธฐ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. โ€ข ๊ณต๊ธ‰์ž๊ฐ€ ํ† ํฐ์„ ์„œ๋น„์Šค์— ์—ฐ๊ฒฐํ•œ๋‹ค. โ€ข ์ธ์ ํ„ฐ๋Š” ๋™์ผํ•œ ํ† ํฐ์„ ๋ฌป๋Š” ๋•Œ๋งˆ๋‹ค ๋™์ผํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. โ€ข ํด๋ž˜์Šค ์ด๋ฆ„๊ณผ ๋‹ค๋ฅธ ํ† ํฐ ์ด๋ฆ„์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.

๋ฐ˜ํ™˜ ๋œ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ์—์„œ ์ƒ์„ฑ ๋œ ๋‹ค์Œ ํ•ญ์ƒ ๋™์ผํ•˜๊ฒŒ ์ž˜ ์•Œ๋ ค์ง„ ๋””์ž์ธ ํŒจํ„ด์ด๊ธฐ๋„ ํ•˜๋‹ค. ์ด ์ธ์Šคํ„ด์Šค๋ฅผ ์‹ฑ๊ธ€ ํ†ค์ด๋ผ๊ณ  ํ•œ๋‹ค. ์ด๋Š” ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ„์— ์ •๋ณด๋ฅผ ๊ณต์œ  ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์‹ค์ œ๋กœ ์œ ์šฉํ•˜๊ณ  ๋™์ผํ•œ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ๋œ๋‹ค.

์ด์ œ ๊ฐ€์งœ RaceService ๋ฌธ์ œ๋กœ ๋Œ์•„๊ฐ€์„œ RaceService์™€ ๋™์ผํ•œ ์ž‘์—…์„ ํ•˜์ง€๋งŒ ํ•˜๋“œ ์ฝ”๋“œ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ƒˆ๋กœ์šด ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

class FakeRaceService {
  list() {
   return [{ name: 'London' }];
  }
}

์šฐ๋ฆฌ๋Š” ๊ณต๊ธ‰์ž ์„ ์–ธ์„ ์‚ฌ์šฉํ•˜์—ฌ RaceService๋ฅผ FakeRaceService๋กœ ๋Œ€์ฒด ํ•  ์ˆ˜ ์žˆ๋‹ค.

// in our module
providers: [
  // we provide a fake service
  { provide: RaceService, useClass: FakeRaceService }
]

์•ฑ์„ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋ฉด ์ด๋ฒˆ์— ํ•œ ๋ฒˆ ๊ฒฝ์ฃผ๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์šฐ๋ฆฌ ์•ฑ์ด ์ฒซ ๋ฒˆ์งธ ๊ฒฝํ’ˆ ๋Œ€์‹  ์œ„์กฐ ๋œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋Š” ์•ฑ์„ ์ˆ˜๋™์œผ๋กœ ํ…Œ์ŠคํŠธ ํ•  ๋•Œ ๋˜๋Š” ์ž๋™ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ณง ๋ณด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

Other types of provider

์šฐ๋ฆฌ์˜ ์˜ˆ์ œ ์—์„œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์•ฑ์„ ๊ฐœ๋ฐœํ•  ๋•Œ FakeRaceServic๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ค์ œ RaceServic์„ ์‚ฌ์šฉํ•  ๋•Œ ์ƒ์‚ฐ ์ค‘์— ์žˆ๋‹ค. ๋ฌผ๋ก  ์ˆ˜๋™์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋‹ค๋ฅธ ์œ ํ˜•์˜ ๊ณต๊ธ‰์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.: useFactory

// we just have to change this constant when going to prod
const IS_PROD = false;
  // in our module
  providers: [
   // we provide a factory
   {
   provide: RaceService,
   useFactory: () => IS_PROD ? new RaceService(null) : new FakeRaceService()
   }
  ]

์ด ์˜ˆ์ œ์—์„œ๋Š” useClass ๋Œ€์‹ ์— useFactory๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ํŒฉํ† ๋ฆฌ๋Š” ํ•˜๋‚˜์˜ ์ž‘์—…์œผ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜์ด๋‹ค. ์ด ์˜ˆ์ œ๋Š” ์ƒ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ฐ€์งœ ์„œ๋น„์Šค ๋˜๋Š” ์‹ค์ œ ์„œ๋น„์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ RaceService๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์‹ค์ œ ์„œ๋น„์Šค๋กœ ๋‹ค์‹œ ์ „ํ™˜ํ•˜๋ฉด ApiService ์ข…์†์„ฑ์ด ์ธ์Šคํ„ด์Šคํ™”๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด ์˜ˆ์ œ๋ฅผ ์ž‘๋™ ์‹œํ‚ค๋ ค๋ฉด ApiService ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ์ž ํ˜ธ์ถœ์— ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค. ์ข‹์€ ์†Œ์‹ : useFactory๋Š” deps๋ผ๋Š” ๋‹ค๋ฅธ ์†์„ฑ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. deps์—์„œ๋Š” ์ข…์† ๋ฐฐ์—ด์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// we just have to change this constant when going to prod
const IS_PROD = true;
  // in our module
  providers: [
   ApiService,
   // we provide a factory
   {
   provide: RaceService,
   // the apiService instance will be injected in the factory
   // so we can pass it to RaceService
   useFactory: apiService => IS_PROD ? new RaceService(apiService) : new
FakeRaceService(),
   deps: [ApiService]
   }
  ]

** ๋ช‡ ๊ฐ€์ง€ ์ข…์†์„ฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ ์ˆœ์„œ๊ฐ€ ๋ฐฐ์—ด์˜ ์ˆœ์„œ์™€ ๋™์ผํ•ด์•ผ ํ•œ๋‹ค. ๋ฌผ๋ก ,์ด ์˜ˆ์ œ๋Š” useFactory์™€ ๊ทธ ์˜์กด์„ฑ์˜ ์‚ฌ์šฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ๊ธฐ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ๋„ˆ๋Š” ์“ธ ์ˆ˜ ์žˆ๊ณ ,ํ•ด์•ผํ•œ๋‹ค.

// in our module
providers: [
  ApiService,
  { provide: RaceService, useClass: IS_PROD ? RaceService : FakeRaceService }
]

IS_PROD์— ๋Œ€ํ•œ ์ƒ์ˆ˜ ์„ ์–ธ์€ ๋ฒˆ๊ฑฐ๋กœ์šด ์ž‘์—…์ด๋‹ค. ์•„๋งˆ๋„ ์šฐ๋ฆฌ๋Š” ์˜์กด์„ฑ ์ฃผ์ž…๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ? ๋‚˜๋Š” ๋‹น์‹ ์ด ๋ณผ ์ˆ˜์žˆ๋Š” ๊ฒƒ๋“ค์„ ์กฐ๊ธˆ ์˜๊ฒฌ์„ ์ฃผ๊ณ  ์žˆ๋‹ค :) ๋‹น์‹ ์€ ๋ฐ˜๋“œ์‹œ DI์—์„œ ๋ชจ๋“  ๊ฒƒ์„ ๊ฐ•์ œ ํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ ์ด๊ฒƒ์€ ๋‹ค๋ฅธ ๊ณต๊ธ‰์ž ์œ ํ˜•์„ ๋ณด์—ฌ์ฃผ๊ธฐ์œ„ํ•œ ๊ฒƒ์ด๋‹ค : useValue

// in our module
providers: [
  ApiService,
  // we provide a factory
  { provide: 'IS_PROD', useValue: true },
  {
   provide: RaceService,
   useFactory: (IS_PROD, apiService) => IS_PROD ? new RaceService(apiService) : new
FakeRaceService(),
   deps: ['IS_PROD', ApiService]
  }
]

Hierarchical injectors

Angular 2์—์„œ ๋งˆ์ง€๋ง‰์œผ๋กœ ์•Œ์•„์•ผ ํ•  ์ค‘์š”ํ•œ ์ ์€ ์•ฑ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ธ์ ํ„ฐ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์‹ค์ œ๋กœ ๊ตฌ์„ฑ ์š”์†Œ๋งˆ๋‹ค ํ•˜๋‚˜์˜ ์ธ์ ํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉฐ์ด ์ธ์ ํ„ฐ๋Š” ํ•ด๋‹น ์ธ์ ํ„ฐ๋ฅผ ์ƒ์†๋ฐ›๋Š”๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์•ฑ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ • ํ•ด ๋ณด์ž.

์šฐ๋ฆฌ๋Š” ์ž์‹ ๊ตฌ์„ฑ ์š”์†Œ ์ธ RacesComponent๋ฅผ ๊ฐ€์ง„ ๋ฃจํŠธ ์ปดํฌ๋„ŒํŠธ PonyRacerAppComponent๋ฅผ ๊ฐ€์ง„ ๋ชจ๋“ˆ AppModule์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์•ฑ์„ ๋ถ€ํŠธ ์ŠคํŠธ๋žฉํ•˜๋ฉด ๋ชจ๋“ˆ์˜ ๋ฃจํŠธ ์ธ์ ํ„ฐ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ž์ฒด ์ธ์ ํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ถ€๋ชจ๋ฅผ ์ƒ์†ํ•œ๋‹ค.

์ฆ‰, ๊ตฌ์„ฑ ์š”์†Œ์— ์ข…์†์„ฑ์„ ์„ ์–ธํ•˜๋ฉด Angular 2๋Š” ํ˜„์žฌ ์ธ์ ํ„ฐ์—์„œ ๊ฒ€์ƒ‰์„ ์‹œ์ž‘ํ•œ๋‹ค. ์˜์กด์„ฑ์„ ๋ฐœ๊ฒฌํ•˜๋ฉด ์™„๋ฒฝํ•˜๊ฒŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๋ถ€๋ชจ ์ธ์ ํ„ฐ์—์„œ ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ข…์†์„ฑ์„ ์ฐพ์„ ๋•Œ๊นŒ์ง€ ๋‹ค์‹œ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ทธ๊ฒƒ์€ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค. ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ ๊ฒƒ์ด๋‹ค.

์ฆ‰, ๊ตฌ์„ฑ ์š”์†Œ์— ์ข…์†์„ฑ์„ ์„ ์–ธํ•˜๋ฉด Angular 2๋Š” ํ˜„์žฌ ์ธ์ ํ„ฐ์—์„œ ๊ฒ€์ƒ‰์„ ์‹œ์ž‘ํ•œ๋‹ค. ์˜์กด์„ฑ์„ ๋ฐœ๊ฒฌํ•˜๋ฉด ์™„๋ฒฝํ•˜๊ฒŒ ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๋ถ€๋ชจ ์ธ์ ํ„ฐ์—์„œ ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ข…์†์„ฑ์„ ์ฐพ์„ ๋•Œ๊นŒ์ง€ ์ˆ˜ํ–‰ํ•˜๊ณ  ์˜ˆ์™ธ๋ฅผ ๋˜์งˆ ๊ฒƒ์ด๋‹ค.

์ด๊ฒƒ์—์„œ ์šฐ๋ฆฌ๋Š” ๋‘ ๊ฐ€์ง€๋ฅผ ์ถ”๋ก  ํ•  ์ˆ˜ ์žˆ๋‹ค : โ€ข ๋ฃจํŠธ ์ธ์ ํ„ฐ์—์„œ ์„ ์–ธ ๋œ ์ข…์†์„ฑ์€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ApiService ๋ฐ RaceServicec๋Š” ์–ด๋””์—์„œ๋‚˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. โ€ข ๋ชจ๋“ˆ๊ณผ ๋‹ค๋ฅธ ์ˆ˜์ค€์—์„œ ์ข…์†์„ฑ์„ ์„ ์–ธ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?

@Component ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” provider ๋ผ๊ณ  ํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด provider ์†์„ฑ์€ @NgModule์˜ providers ์†์„ฑ์— ๋Œ€ํ•ด ์ˆ˜ํ–‰ ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ข…์†์„ฑ ๋ชฉ๋ก์ด์žˆ๋Š” ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ž์ฒด RaceService ๊ณต๊ธ‰์ž๋ฅผ ์„ ์–ธ ํ•  RacesComponent๋ฅผ ์ƒ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

@Component({
  selector: 'ns-races',
  providers: [{ provide: RaceService, useClass: FakeRaceService }],
  template: `<strong>Races list: {{list()}}</strong>`
})
export class RacesComponent {
  constructor(private raceService: RaceService) {
  }
  list() {
   return this.raceService.list();
  }
}

์ด ๊ตฌ์„ฑ ์š”์†Œ์—์„œ RaceService ํ† ํฐ์„ ๊ฐ€์ง„ ๊ณต๊ธ‰์ž๋Š” ๋ฃจํŠธ ์ธ์ ํ„ฐ์— ์ •์˜ ๋œ ๊ฒƒ๊ณผ ๊ฐ™์€ FakeRaceService ์ธ์Šคํ„ด์Šค๋ฅผ ํ•ญ์ƒ ์ œ๊ณตํ•œ๋‹ค. ํŠน์ • ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•ด ๋‹ค๋ฅธ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์›ํ•˜๊ฑฐ๋‚˜ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ์„ ์–ธํ•˜๋Š” ์™„๋ฒฝํ•˜๊ฒŒ ์บก์Šํ™” ๋œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์›ํ•  ๊ฒฝ์šฐ์— ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค.

๊ฒฝ๊ณ !! ์•ฑ์˜ ๋ชจ๋“ˆ๊ณผ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ณต๊ธ‰์ž ์†์„ฑ์— ์ข…์†์„ฑ์„ ์„ ์–ธํ•˜๋ฉด ์ด ์ข…์†์„ฑ์˜ ๋‘ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์‚ฌ์šฉ๋œ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ํ•œ ๊ตฌ์„ฑ ์š”์†Œ ๋งŒ ์„œ๋น„์Šค์— ์ ‘๊ทผ ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ provider ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ธ์ ํ„ฐ ์—์„œ ์ด ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ์•ฑ ์ „์ฒด์—์„œ ์ข…์†์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ๋ฃจํŠธ ๋ชจ๋“ˆ์— ์„ ์–ธํ•˜์—ฌ๋ผ.

DI without types

ํ† ํฐ์„ ์‚ฌ์šฉํ•˜๊ณ  TypeScript ์œ ํ˜•์— ์˜์กดํ•˜์ง€ ์•Š์œผ๋ ค๋Š” ๊ฒฝ์šฐ ์‚ฝ์ž… ํ•  ์ข…์†์„ฑ๋งˆ๋‹ค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” @Inject ()์ด๋ฉฐ ์‚ฝ์ž… ํ•  ์ข…์†์„ฑ์˜ ํ† ํฐ์„ ๋ฐ›๋Š”๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด ApiService ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋™์ผํ•œ RaceService๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

import { Injectable, Inject } from '@angular/core';
import { ApiService } from './api.service';
@Injectable()
export class RaceService {
  constructor(@Inject(ApiService) apiService) {
   this.apiService = apiService;
  }
  list() {
   return this.apiService.get('/races');
  }
}

TypeScript ์—†์ด ์˜ˆ์ œ์—์„œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ž‘๋™ ์‹œํ‚ค๋ ค๋ฉด babel์„ decorator์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผํ•˜๊ณ  angle2-annotations ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

Reference URL

Last updated