Dependency Injection / Inversion of Control [의존성 주입 / 제어의 역전] [NestJS]

2023. 12. 13. 09:31프레임워크/Nest.js

인스턴스를 생성하지 않았는데 컨트롤러에서 어떻게 주입을 받고 어떻게 서비스에 대한 권한과 접근이 가능할까?

 

이는 NestJS의 가장 핵심이 되는 요소중 하나이다. 


일반 인스턴스화

 

클래스 B 를 클래스 A에 사용해야 한다면?

 

class A {
	const b = B();
}

class B {

}

A라는 클래스를 인스턴스로 만들때마다 A 안에 클래스 B의 인스턴스를 매번 새로 생성하게 된다.


DI (Dependency Injection)

 

class A {
	constructor(instance: B)
}

class B {

}

B라는 클래스를 생성을 해서 constructor에 입력해준다. 이를 주입이라고 한다.

외부에서 클래스 A가 생성될 때 무조건 클래스 B에 해당되는 인스턴스를 넣어주도록 정의를 하는 것이다.

 

그럼 의존성이 무엇일까? 

 

클래스 A를 사용할 때, 이 클래스 A는 지금 클래스 B의 인스턴스가 생성할 때 필요하기 때문에 클래스 A는 클래스 B에 의존하고 있다.

이를 풀어서 예기하면 의존하고 있는 값을 우리가 주입해준다라는게 DI의 가장 중요한 포인트이다.


IoC (Inversion of Control)

 

class B {

}

class A {
	constructor(instance: B)
}

class C {
	constructor(instance: B)
}

 

IoC 는 DI의 일종이라고도 볼 수 있다.

Control : 제어하다, Inversion : 반대

반대로 제어한다는 뜻이다.

 

역제어가 된다는 것인데 무엇을 역제어 하냐면 위의 예처럼 클래스 A와 클래스 C 가 둘 다 클래스 B에 의존성이 있다고 하면 클래스 B를 직접 생성을 해서 주입을 시켜준다.

클래스 B가 아니더라도 다른 클래스들이 의존하고 있는 인스턴스를 생성하고 삭제하고 주입을 해주는 이 과정을 프레임워크가 직접 담당하는 것이다.

constructor 에 정의만 하면 사용할 수 있다.


IoC 와 DI 를 사용하는 NestJS 프레임 워크라면?

 

NestJS는 실행과 동시에 IoC 컨테이너가 생성된다.

우리는 A 컨트롤러와 B 컨트롤러를 사용하는데, 그 안에서 B 라는 서비스를 사용해야 한다. 

그럼 NestJS 의 IoC 컨테이너가 클래스 B를 사용해서 new B() 라고 한 다음에 B클래스의 인스턴스를 생성한다.

그리고 IoC 컨테이너 안에서 이 B클래스의 인스턴스를 그대로 가지고 있고 인스턴스 B의 라이프 사이클을 알아서 제어한다.

이 클래스 A 가 필요한 상황 그러니까 이 안에서 인스턴스 B가 필요한 상황이 있을 때마다  IoC 컨테이너가 자동으로 생성된 인스턴스를 주입해준다.

 

그래서 개발자는 이 의존성이 있는 것들의 생성과 폐기를 전혀 신경 쓸 필요가 없이 기능에만 집중할 수 있도록 NestJS가 설계 되었다.

그 외에도 직접 인스턴스를 생성하는 상황이 없다면 테스트 코드 작성하는데도 굉장히 편리해진다. 인스턴스를 생성하는 과정이 없기 때문에 그 부분을 생략하고서 테스트해도 되기 때문

 

그리고 이렇게 주입을 받을 수 있는 클래스들을 NestJS 에서는 Provider 라고 부른다.

 

provider 를 constructor 안에다가 주입 받겠다고 정의를 해주면 IoC 컨테이너가 알아서 그 프로바이더들을 찾아서 인스턴스화 한 다음에 인스턴스들이 필요한 곳에다가 자동으로 주입을 해준다.

이것이 NestJS가 하는 굉장히 큰 역할 중 하나이다.