Skip to content

2. Angular의 의존성 주입


의존성 객체(dependencies)는 클래스가 동작하기 위해 필요한 서비스나 객체를 의미한다. 그리고 의존성 주입(Dependency Injection, DI)은 클래스가 의존성 객체를 외부에 요청하고 외부에서 이 객체의 인스턴스를 생성해서 주입하는 디자인 패턴을 의미한다.


Angular의 의존성 주입 프레임워크는 클래스 단위로 의존성 객체를 생성한다. Angular 의존성 주입 시스템을 활용하면 애플리케이션을 유연하게 구성할 수 있으며 확장성을 높일 수 있다.


Note


1. 의존성으로 주입할 수 있는 서비스 생성하기

src/app/heroes 폴더에 HeroService를 생성하려면 Angular CLI 명령을 이렇게 실행하면 된다:


ng generate service heroes/hero


그러면 아래와 같은 기본 HeroService 클래스가 생성된다.


src/app/heroes/hero.service.ts (CLI가 생성한 서비스)
import { Injectable } from "@angular/core";

@Injectable({
  providedIn: "root",
})
export class HeroService {
  constructor() {}
}


@Injectable() 데코레이터는 Angular가 이 클래스를 DI 시스템에 활용하겠다는 것을 의미하는 데코레이터이다. 이 데코레이터의 메타데이터에는 providedIn: 'root'를 지정했기 때문에 HeroService가 애플리케이션 전역에서 활용될 수 있다.


이제 히어로의 목(mock) 데이터를 가져오기 위해 getHeroes() 메서드를 추가하고 mock.heroes.ts 파일의 내용을 반환해 보자:


src/app/heroes/hero.service.ts
import { Injectable } from "@angular/core";
import { HEROES } from "./mock-heroes";

@Injectable({
  // 이 서비스를 애플리케이션 최상위 인젝터에 등록합니다.
  providedIn: "root",
})
export class HeroService {
  getHeroes() {
    return HEROES;
  }
}


코드를 간결하게 작성해서 유지보수성을 높이기 위해 컴포넌트와 서비스는 별도 파일로 분리해서 작성하는 것을 권장한다.


의도적으로 컴포넌트와 서비스를 한 파일에 작성하는 상황이라면, 서비스를 먼저 정의하고 컴포넌트를 정의해야 한다. 컴포넌트를 서비스보다 먼저 정의하면 실행 시점에 null 참조 에러가 발생한다.


2. 서비스 주입하기

서비스를 컴포넌트에 의존성으로 주입하면 컴포넌트에서 이 서비스를 활용할 수 있다.


서비스를 컴포넌트에 주입하려면 컴포넌트의 constructor()에 의존성 객체의 타입을 지정하면 된다. 아래 코드는 HeroListComponent의 생성자로 HeroService를 주입하는 예제 코드이다. heroService의 타입은 HeroService이다.


src/app/heroes/hero-list.component (생성자)
constructor(heroService: HeroService)


3. 서비스에서 다른 서비스 활용하기

어떤 서비스가 다른 서비스를 활용하는 경우에도 컴포넌트에 사용했던 패턴을 그대로 사용하면 된다. HeroServiceLogger 서비스를 활용해야 한다고 하자.


그러면 먼저 HeroServiceLogger 서비스를 로드한다. 그리고 HeroServiceconstructor()private logger: Logger라고 작성해서 Logger 서비스를 의존성으로 주입하면 된다.


constructor()에 의존성을 주입할 때는 원하는 의존성 객체의 타입을 정확하게 지정해야 Angular가 해당 객체를 주입할 수 있다.


이렇게 작성하면 Logger 서비스의 인스턴스가 프라이빗 필드 logger로 할당된다.


아래 코드 탭을 보면 HeroService를 두 가지 방법으로 구현한 코드를 확인할 수 있다. 첫 번째 HeroServiceLogger 서비스를 활용하지 않는 코드이다. 그리고 두 번째 HeroServiceLogger 서비스를 의존성으로 주입받는 코드이다.


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

@Injectable({
  providedIn: "root",
})
export class Logger {
  logs: string[] = []; // 테스트하기 위해 로그를 저장합니다.

  log(message: string) {
    this.logs.push(message);
    console.log(message);
  }
}
import { Injectable } from "@angular/core";
import { HEROES } from "./mock-heroes";

@Injectable({
  providedIn: "root",
})
export class HeroService {
  getHeroes() {
    return HEROES;
  }
}
import { Injectable } from "@angular/core";
import { HEROES } from "./mock-heroes";
import { Logger } from "../logger.service";

@Injectable({
  providedIn: "root",
})
export class HeroService {
  constructor(private logger: Logger) {}

  getHeroes() {
    this.logger.log("Getting heroes ...");
    return HEROES;
  }
}


이 예제에서는 히어로 목록을 가져온다는 것을 로그로 남기기 위해 getHeroes() 메서드가 Logger 서비스를 활용했다.


References