의존성 주입(DI)은 객체 간의 의존성을 외부에서 주입해주는 디자인 패턴이다.
이를 통해 객체 간 결합도를 낮추고 유연성을 높일 수 있다. DI는 주로 제어의 역전 개념과 함께 사용되며, 객체의 생성 및 관리를 담당한다.
DI는 객체가 직접 의존성을 생성하지 않고, 외부에서 주입받도록 설계하는 방식이다.
즉, 클래스 내부에서 다른 객체를 직접 생성하지 않고, 외부에서 필요한 객체를 제공해준다.
예제: 직접 의존성 생성(DI 미사용)
class Engine {
start() {
console.log('Engine started');
}
}
class Car {
private engine: Engine;
constructor() {
this.engine = new Engine(); // 직접 객체 생성
}
drive() {
this.engine.start();
console.log('Car is moving');
}
}
위 코드에서는 Car 가 Engine 을 직접 생성하고 있기 때문에 두 객체는 강하게 결합되어있다.
이 경우 Engine을 다른 엔진으로 교체하려면 Car의 코드도 수정해야 하므로 확장성이 떨어진다.
DI를 적용하면 Car 클래스가 Engine을 직접 생성하는 것이 아니라, 외부에서 주입받도록 변경할 수 있다.
예제: 생성자를 통한 의존성 주입
class Engine{
start(){
console.log('Engine started');
}
}
class Car{
private engine: Engine;
constructor(engine: Engine){
this.engine = engine; //외부에서 주입
}
drive(){
this.engine.start();
console.log('Car is moving');
}
}
const engine = new Engine();
const car = new Car(engine);
car.drive();
Engine 을 다른 종류의 엔진으로 쉽게 교체 가능하다.
Nestjs는 DI 컨테이너를 통해 의존성을 자동으로 관리한다.
Nestjs에서는 클래스 기반의 프로바이더를 사용하여 DI를 구현한다.
Nestjs에서 DI 사용 예제
1) Service 클래스 생성
import {Injectable} from '@nestjs/common';
@Injectable()
export class EngineService{
start(){
console.log('Engine started');
}
}
@Injectable() 데코레이터를 사용하여 이 클래스가 의존성 주입 가능한 프로바이더 임을 선언한다.
2) Service를 주입받는 클래스
import {Injectable} from '@nestjs/common';
import {EngineService} from './engine.service';
@Injectable()
export class CarService{
constructor(private readonly engineService: EngineService){}
drive(){
this.engineService.start();
console.log('Car is moving');
}
}
CarService는 EngineService에 의존하고 있으며, 생성자를 통해 주입받는다.
3) Module에서 등록
import {Module} from '@nestjs/common';
import {EngineService} from './engine.service';
import {CarService} from './car.service';
@Module({
providers: [EngineService, CarService],
export:[CarService],
})
export class CarModule{}
Nestjs에서는 @Module을 사용하여 서비스들을 등록하고, 이를 자동으로 주입해준다.
4) 사용 예시
import {Injectable} from '@nestjs/common';
import {CarService} from './car.service';
@Injectable()
export class AppService{
constructor(private readonly carService:CarService){}
run(){
this.carService.drive();
}
}
Nestjs가 CarService와 EngineService를 자동으로 주입하여 사용할 수 있도록 해준다.
✅ 장점
객체 간 결합도 감소 -> 유지보수 용이
코드의 재사용성 증가 -> 동일한 의존성을 여러 곳에서 사용 가능
테스트 용이 -> Mock을 활용한 유닛 테스트 가능
확장성과 유연성 증가 -> 새로운 기능 추가가 쉬움
✅ 활용 예시
REST API 서비스 간의 분리
데이터베이스 연결을 관리하는 Repository 패턴
메시지 브로커(MQTT, Kafka)와의 인터페이스 모듈
OAuth 또는 JWT 기반 인증 모듈