**Dependency Injection(DI)**은 클래스가 필요로 하는 의존성을 직접 생성하는 대신 프레임워크가 자동으로 생성하고 제공하는 NestJS의 핵심 패턴입니다. 필요한 것을 생성자에 선언하면 NestJS가 공급합니다 — 느슨하게 결합되고 테스트 가능한 코드로 이어집니다.
동작 방식: 생성자에 선언
()
{
() {}
}
Nest는 생성자의 파라미터 타입을 읽고, 대응하는 등록된 provider를 찾고, (아직 없다면) 생성하여 전달합니다 — 전체 의존성 그래프를 자동으로 해석합니다.
1. 클래스가 @Injectable()로 표시됨 → provider가 됨
2. provider가 module의 `providers` 배열에 등록됨
3. 다른 클래스가 그것을 생성자 파라미터로 선언함
4. NestJS의 IoC 컨테이너가 인스턴스를 해석하고 자동으로 주입함
@Module({
providers: [UsersService, DatabaseService, LoggerService], // 모든 provider 등록
controllers: [UsersController],
})
export class UsersModule {}
// 이제 Nest의 컨테이너는 각각을 어떻게 생성하고 필요한 곳에 주입할지 압니다
✓ 느슨한 결합 — 클래스가 구체적인 의존성 생성이 아닌 추상화에 의존
✓ 테스트 가능성 — 테스트에서 MOCK 의존성을 쉽게 주입 (실제 DB 불필요)
✓ 재사용성 — 앱 전체에서 하나의 공유 인스턴스(싱글톤) 사용
✓ 유지보수성 — 한 곳(module 등록)에서 구현을 변경
// 테스트에서 가짜 DatabaseService 제공 — 실제 데이터베이스 불필요
const module = await Test.createTestingModule({
providers: [
UsersService,
{ provide: DatabaseService, useValue: mockDatabase }, // mock 주입
],
}).compile();
클래스가 의존성을 직접 생성하지 않기 때문에 테스트를 위해 mock으로 교체할 수 있습니다 — DI가 NestJS 앱을 매우 테스트하기 쉽게 만듭니다.
// 클래스뿐 아니라 config/값에는 injection token을 사용
constructor(@Inject("CONFIG") private config: AppConfig) {}
Dependency injection은 NestJS 전체 아키텍처를 떠받치는 기초 패턴입니다 — controller가 service를 얻고, service가 repository와 다른 service를 얻으며, 전체 애플리케이션이 함께 연결되는 방식입니다.
거의 모든 NestJS 클래스가 DI에 참여하기 때문에 이를 이해하는 것이 필수적입니다: 의존성을 생성자에 선언하면 프레임워크의 IoC 컨테이너가 자동으로 해석하고 제공하여 수동 인스턴스화와 강한 결합을 제거합니다.
이점은 상당하며 NestJS가 가치 있는 이유의 핵심입니다: 느슨한 결합(추상화에 의존), 테스트 가능성(실제 의존성 대신 mock 주입 — 철저한 단위 테스트를 쉽게 만듦), 재사용성(공유 싱글톤), 유지보수성.
DI는 Angular나 Spring 같은 프레임워크에서 차용한 것이며, 이를 숙달하는 것 — 의존성을 선언, 등록, 주입, mock하는 방법 — 은 잘 구조화되고 테스트 가능한 NestJS 애플리케이션을 구축하는 데 필요하며 프레임워크에서 깊이 이해해야 할 가장 중요한 개념 중 하나입니다.