3. Backend/NestJS / / 2024. 3. 12. 19:19

[코드팩토리 NestJS 강의] Module, Provider and Inversion of Control (제어의 역전)

728x90

컨트롤러에서 어떻게 주입을 받고 있고, 어떻게 서비스에 대한 권한과 접근이 가능한지에 관한 궁금증이 생길 수 있다

이 부분은 NestJS의 가장 핵심이 되는 요소 중 하나라고 생각하면 됨

클래스 B를 클래스A에서 생성을 했었는데

 

 

Dependency Injection은 어디선가 클래스B를 생성해서, 클래스A에서 생성하지않고 constructor에다가 입력을 해줌(*이것을 주입이라고 부름) 외부에서 클래스A가 생성될때 무조건 클래스B에 해당되는 인스턴스를 넣어주도록 정의를 함 그렇지않으면 constructor를 실행할수없으니까

Dependency의 클래스A를 사용할때, 클래스B의 인스턴스가 필요하기에 클래스A는 클래스B에 의존하고 있음 즉, 의존하고 있는 값을 주입해준다 라는 것이 Dependency Injection의 가장 큰 중요한 포인트임

 

Inversion of control 의 경우 Dependency Injection의 일종이라고 볼 수 있다

역제어는 원래는 클래스A,C 둘 다 클래스B의 인스턴스에 의존성이 있다고 하면, 클래스B를 직접 생성을 해서 주입을 시켜줌

다른 클래스들이 의존하는 어느 하나의 인스턴스를 생성,삭제,주입해주는 과정을 프레임워크가 직접 담당을 함

(* 예를들어 컨트롤러에서도 서비스를 생성한 적이 없는데 자동으로 주입이 된것을 확인할 수 있다) (* constructor에 정의만 하면 사용할 수 있었다)

 

조금 더 깊게 들어가면

Inversion of Control 과 Dependency Injection을 사용하는 NestJS 프레임워크라고 생각을 해보자

NestJS는 실행과 동시에 IoC(Inversion of Control)라 컨테이너 라는 것이 생성됨 Inversion of Control 안에서 클래스A와 클래스C를 사용해야한다고 가정을 하면, 그 A와C안에서는 B라는 서비스를 사용해야한다

그러면 NestJS의 IoC컨테이너가 클래스B를 사용하여 new B라고 한 후 클래스B 인스턴스를 생성을 한다. 그리고 이 IoC컨테이너 안에서 클래스B의 인스턴스를 그대로 들고 있음 이 인스턴스B의 라이프사이클을 알아서 제어한다

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

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

그 외에도 직접 인스턴스를 생성하는 상황이 없다면 테스트 코드 작성하는데도 편리해진다

인스턴스를 생성하는 과정이 없기에 그 부분을 생략하고서 테스트해도되기때문이다.

현재 배운 개념내에서 클래스 A와 C가 컨트롤러라고 가정한다면, 클래스B인스턴스를 주입받은 경우는 서비스라고 볼 수 있다 (* 코드에 보면 private read-only 하고서 서비스를 주입 받았었음)

constructor(private readonly postsService: PostsService) {}

주입을 받을 수 있는 클래스들을 NestJS에서 프로바이더라고 부름

constructor(instance:B) 프로바이더를 컨스트럭터 안에다가 주입받겠다고 정의 해주면, IoC컨테이너가 알아서 그 프로바이더들을 찾아서 인스턴스화를 한 후, 인스턴스들이 필요한 곳에다가 자동으로 주입을 해줌

이것이 NestJS가 하는 큰 역할 중 하나다


Q. 앞서 만들은 posts 컨트롤러에서 postsService라는 것을 한번도 주입한적이 없는데, postsService관련 기능들을 전부 다 잘 사용하고 있음 = 인스턴스화해서 직접 넣어주지 않았는데 도대체 어디서 이 postsService가 생겨난것인가!!

A. NestJS IoC컨테이너에서 자동으로 생성이 되고 있음. 실행을 하면 자동으로 IoC컨테이너가 이 주입되어야 하는 이 서비스들을 생성해주는것

Q. 그러면, 이 서비스를 어디에 등록하면 IoC컨테이너가 인지할 수 있는것인가?

A. post.module.ts에 가면 확인할 수 있다

import { Module } from '@nestjs/common';
import { PostsService } from './posts.service';
import { PostsController } from './posts.controller';

@Module({
  controllers: [PostsController],
  providers: [PostsService],
})
export class PostsModule {}

CLI를 사용하여 생성하는 이 하나의 세트마다 컨트롤러, 모듈, 서비스 3개의 세트마다 모듈파일이 꼭 존재하는 것을 볼 수 있다.

이름도 지정한 폴더의 이름+모듈로 생성됨

모듈로 애노테이션이 되어있는데(@Module) PostModule클래스가 모듈이다 라는 것을 정의해준 역할

그 안에 모듈에서 두개의 파라미터를 넣고있다.

  • controllers : 컨트롤러들을 등록할 수 있는 위치 (위의 경우, PostsController를 사용하기에 PostsController를 등록하게 됨 : 특정된 패스로 요청이 들어가면 PostsController로 라우팅이 되는것)
  • providers : list로 클래스들을 넣을 수 있는 것을 볼 수 있다.
    • 주의해야할 점은 instance(PostsController(), PostsService())를 넣은것이 아니라 클래스(PostsController, PostsService )를 그대로 넣음
    • 이유 : 모듈이 생성되는 순간 클래스를 인스턴스화하고 싶은게 아니고 IoC컨테이너가 자동으로 인스턴스화하고 관리하는것을 원하기때문 그래서 인스턴스화해서 관리할 클래스만 그대로 클래스로 입력을 함
    • PostsController에서 주입하는 값들을 전부 다 providers안에다 넣어주면
    • PostsService(데이터를 다루는 로직이 들음) 에 대한 정의는 어떤 역할을 하는지에 대한 정의
      • 데이터 다루는 로직을 작성하는 클래스를 서비스라고 부름
      • (* 나중에 DB와 쉽게 통신할 수 있는 typeORM같은 거라던가 인증과 검증에 필요한 기능들 같은것들도 컨트롤러 안에서 또는 서비스 안에서 주입을 받아야되는 경우들이 생김 → 그럴때 서비스가 꼭 아니더라도 특정 클래스에 컨트롤러에서 주입을 시켜야하는 클래스가 있다면 그 클래스들은 전부 providers안에다가 넣어주면 됨)
      • 그러면 모듈 안에 등록되어있는 controllers와 providers안에서는 providers 안에 등록된 모든 클래스들을 인스턴스화 없이 IoC컨테이너에 의존하면서 사용할 수 있게됨

정리

NestJS가 실행되었을때, 포스트 컨트롤러가 실행되어야하는데, 인스턴스화가 되어서 함수들을 실행시킬 준비가 되어있어야하는데, 이 포스트 컨트롤러가 잘 실행되기 위해서는 포스트서비스에 대한 의존성을 IoC컨테이너가 생성을 해서 포스트컨트롤러 안에다 주입을 해줌

주입이되려면 모듈에다가 provider로 등록을 해줘야 한다.


service에 가보면 @Injectable() 을 볼 수 있다.

즉, 모듈 안 providers안에 원하는 클래스를 등록해놓으면 Dependency Injection의 용도로 클래스를 사용할 수 있는데, 그거 외에도 실제로 프로바이더에다가 @Injectable() 이라고 태그를 해줘야지만 프로바이더로 사용할 수 있다.

두가지 다 해줘야 한다

프로바이더로 사용하고 싶은 클래스에는 모듈에다 providers안에 등록해주는것 + @Injectable() 로 애노테이션 해주는것

그러면 인스턴스를 직접 생성할 필요 없이 IoC컨테이너가 등록한 클래스를 모듈안에 어떤곳에서든 사용할 수 있게 된다

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유