formulous

주니어 개발자의 개발 지식 공유 블로그입니다.

NestJS

[nestjs] module에서 선언하는 provider, 대체 뭘까요?

formulous 2022. 12. 13. 13:36

 

 

안녕하세요.

 

오늘은 저 포함 많은 분들이 정확한 개념을 가지고 있지 않은 채로 사용하고 있는 provider에 대해서 알아봅시다.

 

 

업무 중에 있었던 일입니다.

 

어떤 스크립트에서든 사용할 수 있도록 만든 utils에 포함된 sqliteUtil 클래스를 provider에 선언해주고 의존성 주입을 통해 사용한 적이 있었는데요.

 

그때 당시 코드 리뷰에서 온  "util을 왜 provider에 썼어요?"라는 질문에 전혀 답변하지 못했던 아쉬운 기억이 있습니다.

 

provider에 대한 기본 개념이 부족했기 때문에 그렇게 작성했을테고, 그 이유 따윈 없었으니까요..

 

(이렇게 개발하시면 안 됩니다..)

 

 

모든 코드에는 나름의 이유가 있어야 되는 법입니다.

 

provider [공급자]에 대해서 몰랐기 때문에 한 잘못을 provider에 대해 학습하며 만회해봅시다.

 

먼저 provider에 대해 쉽게 이해하려면 3가지 개념이 우선적으로 이해가 돼야 합니다.

 

  • 계층형 구조 (Layered Architecture)
  • 제어 역전 (IoC, Inversion of Control)
  • 의존성 주입 (Dependency Injection)

 

선행 학습 목록들 이름이 살벌하죠?

 

 

저도 처음 볼 땐 그랬어요. 하지만 막상 접하고 나면 별거 아닌 개념들이니 잘 따라와 주세요!


첫 번째로, 계층형 구조 (Layered Architecture)에 대해 알아봅시다.

 

 

* 계층형 구조 (Layered Architecture)

 

Nest는 기본적으로 3 계층 구조를 사용합니다. 3계층 구조는 아래와 같은데요.

 

-  Presentation 계층 : 사용자 인터페이스 혹은 외부와의 통신을 담당합니다.

-  Application 계층 : 주로 비즈니스 로직을 구현하고 Presentation 계층과 Data 계층을 연결하는 역할을 담당합니다.

-  Data 계층 : 데이터베이스에 데이터를 읽고 쓰는 역할을 담당합니다.

 

Nest의 Controller, Service는 각 계층과 아래와 같이 연결될 수 있습니다.

 

Controller - Presentation 계층 / Service - Application 계층

 

Nest에서는 주로 Application 계층의 로직들을 provider라는 이름으로 구분하여 구성하게 됩니다.


두 번째로, 제어 역전 (IoC, Inversion of Control)입니다.

 

 

* 제어 역전 (IoC, Inversion of Control)

 

제어 역전을 간단히 말하자면 이렇게 설명할 수 있습니다.

 

제어권을 내가 아닌 프레임 워크로 넘긴다.

 

 

보통 클래스를 사용하려면 new 키워드로 인스턴스화를 시켜야 합니다.

 

클래스와 인스턴스를 붕어빵 틀과 붕어빵으로 많이들 비유하는데요.

 

export class Concept() {
	...
}

----------------------------------------------------------------

const concept = new Concept();

 

위에선 Concept라는 붕어빵 틀(클래스)에서 new 키워드를 통해 concept라는 붕어빵(인스턴스)을 찍어냈다고 생각할 수 있겠죠.

 

제어 역전이란, 위의 인스턴스 화와 동일한 행위를 의존성 주입을 통해 프레임워크에서 할 수 있도록 제어권을 프레임워크에게 넘겨줌을 말합니다.

 

Nest 예시 코드를 한번 볼까요?

 

* login.service.ts

@Injectable()
export class LoginService {
 ...비즈니스 로직
}

 

* login.controller.ts

@Controller()
export class LoginController {
  constructor(private loginService: LoginService) {}
  
  @Post('login')
  async login(@Query() query: LoginDto) {
    return await this.loginService.login(query);
  }
}

 

위처럼 service와 같은 provider 로직에 의존성 주입을 위한 @Injectable() 데코레이터를 달아줍니다.

 

그리고 Controller의 constructor에서 loginService를 선언해 인스턴스화를 할 수 있도록 제어권을 맡겨두는 것이죠.

 

이렇게 프레임워크에게 인스턴스화를 맡겨두면 new 키워드 없이 loginService의 로직을 사용할 수 있게 됩니다.

 

제어 역전의 개념이 이해가 가시나요??


그럼 세 번째로, 의존성 주입 (Dependency Injection)에 대해 알아봅시다.

 

 

* 의존성 주입 (Dependency Injection)

 

이미 위에서 @Injectable() 데코레이터를 얘기하며 의존성 주입을 언급했었죠?

 

의존성 주입은 추상적인 제어 역전을 구체적으로 구현하는 구현체 역할을 합니다.

 

간단하게 말해 "의존성 주입을 이용해 제어 역전(Inversion of Control)을 구현한 것이다" 정도로만 이해하시면 될 것 같아요.

 

그리고 저희가 사용하는 Nest가 의존성 주입을 통해 제어 역전을 구현한 프레임워크의 대표적 예시입니다.


조금 어렵죠..? 

 

 

그래도 이렇게 세 가지 개념을 이해한 채로 Nest 공식문서 내용을 참고해보면 읽는 느낌이 달라집니다.

 

한번 보실까요? Nest 공식 문서에서 정리한 provider에 대한 내용을 인용해보자면

 

Provider는 Nest의 기본 개념이에요. Nest의 기본 클래스인 service, repository, factory, helper 등등은 Provider로 취급될 수 있어요. Provider의 주요 아이디어는 "의존성을 주입할 수 있다"입니다. 이 뜻은 객체가 서로 다양한 관계를 만들 수 있다는 것을 의미해요. 그리고 "객체의 인스턴스를 연결해주는 기능은 Nest 런타임 시스템에 위임"할 수 있어요.

 

인용구를 읽으며 앞서 배운 의존성 주입과 제어 역전의 의미가 머릿속에 떠오르시나요? 그렇다면 성공입니다!

 

그럼 정확하지만 간단하게 Provider의 의미는 무엇일까요?

 

Controller와 같은 Presentation 계층에서 비즈니스 로직을 행하지 않고 상세한 기능들을 담당하는 Service 파일을 따로 만들어 두게 되는데요.

 

이런 비즈니스 로직들을 Presentation 계층으로 공급한다는 의미에서 공급자(Provider)로 분류하게 됩니다.

 

그리고 저희는 그 Provider를 module에서 선언해준 상태에서 생성자를 이용한 의존성 주입으로 provide의 로직을 사용할 수 있게 되는 것이죠.

 

결국 Provider는 보다 상세한 기능을 구현하여 presentation 계층으로 그 기능을 공급해주는 Application 계층을 일컫는 말이 되는 것입니다.

 

확실한 건 언제 어디서든 사용할 수 있어야 하는 util 파일이 들어갈 항목은 아니란 거겠죠..?

 

 

이렇게 오늘은 provider에 대해 알아보았어요.

 

꽤나 복잡한 개념이었지만 처음 말했다시피 모든 코드에는 존재 이유가 있고 그걸 정확히 알아야 활용할 수 있는 법인 것 같아요.

 

잘 이해하셔서 도움이 되었으면 좋겠네요!

 

감사합니다.

 

'NestJS' 카테고리의 다른 글

[nestjs] URL 유효성 검사를 Middleware 에서 하는 방법  (0) 2022.12.06