2. Angular의 의존성 주입
의존성 객체(dependencies)는 클래스가 동작하기 위해 필요한 서비스나 객체를 의미한다. 그리고 의존성 주입(Dependency Injection, DI)은 클래스가 의존성 객체를 외부에 요청하고 외부에서 이 객체의 인스턴스를 생성해서 주입하는 디자인 패턴을 의미한다.
Angular의 의존성 주입 프레임워크는 클래스 단위로 의존성 객체를 생성한다. Angular 의존성 주입 시스템을 활용하면 애플리케이션을 유연하게 구성할 수 있으며 확장성을 높일 수 있다.
1. 의존성으로 주입할 수 있는 서비스 생성하기
src/app/heroes
폴더에 HeroService
를 생성하려면 Angular CLI 명령을 이렇게 실행하면 된다:
그러면 아래와 같은 기본 HeroService
클래스가 생성된다.
import { Injectable } from "@angular/core";
@Injectable({
providedIn: "root",
})
export class HeroService {
constructor() {}
}
@Injectable()
데코레이터는 Angular가 이 클래스를 DI 시스템에 활용하겠다는 것을 의미하는 데코레이터이다. 이 데코레이터의 메타데이터에는 providedIn: 'root'
를 지정했기 때문에 HeroService
가 애플리케이션 전역에서 활용될 수 있다.
이제 히어로의 목(mock) 데이터를 가져오기 위해 getHeroes()
메서드를 추가하고 mock.heroes.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
이다.
3. 서비스에서 다른 서비스 활용하기
어떤 서비스가 다른 서비스를 활용하는 경우에도 컴포넌트에 사용했던 패턴을 그대로 사용하면 된다. HeroService
가 Logger
서비스를 활용해야 한다고 하자.
그러면 먼저 HeroService
에 Logger
서비스를 로드한다. 그리고 HeroService
의 constructor()
에 private logger: Logger
라고 작성해서 Logger
서비스를 의존성으로 주입하면 된다.
constructor()
에 의존성을 주입할 때는 원하는 의존성 객체의 타입을 정확하게 지정해야 Angular가 해당 객체를 주입할 수 있다.
이렇게 작성하면 Logger
서비스의 인스턴스가 프라이빗 필드 logger
로 할당된다.
아래 코드 탭을 보면 HeroService
를 두 가지 방법으로 구현한 코드를 확인할 수 있다. 첫 번째 HeroService
는 Logger
서비스를 활용하지 않는 코드이다. 그리고 두 번째 HeroService
는 Logger
서비스를 의존성으로 주입받는 코드이다.
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
서비스를 활용했다.