# Nest js Provider

By [Primrose](https://paragraph.com/@primrose) · 2022-11-27

---

Nest JS 를 배우며
=============

Nest JS에 대해 공부를 하며 정리하는 글.

### Sources …

[https://www.wisewiredbooks.com/nestjs/overview/04-provider.html](https://www.wisewiredbooks.com/nestjs/overview/04-provider.html)

계층형 구조(Layered Architecture)
----------------------------

소프트웨어 업계에서는 계층형 구조라는 기법을 사용한다.

복잡해 보이는 작업도 그 작업을 나누고 각 작업마다 역량을 집중하면 쉽게 해결할 수 있다.

MVC에서 관심사를 분리하여 유지보수 및 개발을 쉽게 만드는 원리와 같다고 본다.

몇 개의 계층으로 구분하는냐에 따라서 다르지만 보통 3계층 구조를 많이 사용한다.

#### Presentation Tier

사용자 인터페이스 혹은 외부와의 통신을 담당.

#### Application Tier

Logic Tier라고 하기도 하고 Middle Tier라고 하기도 한다. 주로 비즈니스 로직을 여기서 구현을 하며, Presentation Tier와 Data Tier사이를 연결해준다.

#### Data Tier

데이터베이스에 데이터를 읽고 쓰는 역할을 담당한다.

Nest에서의 Presentation Tier는 외부의 입력을 받아들이는 컨트롤러이며 서비스는 Application Tier에 해당한다.

서비스에는 주로 비즈니스 로직이 들어간다.

Nest는 이렇게 컨트롤러와 그 하위 계층을 프로바이더라는 이름으로 구분하며, 이는 응집도는 높이고 결합도는 낮추는 소프트웨어 설계이다.

* * *

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

> 스포 : 개발자가 제어하던 것을 프레임워크가 제어하게 된다고 하여 제어의 역전이라고 부름.

`제어 역전`을 한 마디로 표현한다면 “**나 대신 프레임워크가 제어한다”** 라고 표현할 수 있다.

제어 역전을 설명하기 위해서는 `의존성`이라는 개념을 알아야한다.

타입스크립트를 비롯한 많은 언어에서는 클래스를 사용하려면 `new` 같은 키워드로 인스턴스화를 시켜야 한다.

붕어빵(인스턴스화시킨 클래스)을 먹지, 붕어빵틀(클래스)을 먹지는 않는 것과 같다.

    const sword = new Sword();
    

`Warrior`클래스에서 `Sword`클래스를 인스턴스화 했다.

기획팀에서 다음 업데이트때 이제 전사는 칼 뿐만 아니라 몽둥이도 사용할 수 있다고 지령이 내려왔다.

좋은 객체지향 설계는 구체적인 개념에 의존하지 말고 추상적 개념에 의존해야 한다.

위 코드에서 `new`를 사용하면 `Sword`와 `Sword`를 생성하는 `Warrior` 사이에 의존성이 생긴다.

정확히는 `Warrior`가 `Sword`에 의존하게 된다.

이렇게 직접적이고 구체적으로 클래스를 인스턴스화하는 행동은 바람직하지 않다.

이럴때 `인터페이스`가 사용된다. 프로그래밍에서 인터페이스는 규약이다.

인터페이스를 구현하려면 인터페이스가 원하는 규약을 따라야 한다.

반대 급부로 프로그래머는 내가 호출하는 클래스가 무엇인지 정확하게 알 필요가 없다.

다만 특정 기능이 동작 가능하다는 사실만 알고 개발하면 된다.

    interface Weaponable {
      swing(): void;
    }
    
    interface Playable {
      attack(): void;
    }
    
    class Warrior implements Playable {
      private Weaponable weapon;
    
      constructor Warrior(private readonly Weaponable _weapon) {
        weapon = _weapon;
      }
    
      public void attack() {
        weapon.swing();
      }
    }
    
    class Mongdungee implements Weaponable {
      public void swing() {
        console.log('Mongdungee Swing!');
      }
    }
    

몽둥이 쥔 전사 클래스를 인스턴스화 시켜보자.

    Warrior warrior = new Warrior(new Mongdungee());
    

하지만 클래스 계층 구조가 복잡한 프로그램에서 직접 저 몽둥이를 넘겨야 한다거나, 여러 전사에게 같은 몽둥이를 넘기거나 한다는 상황에서는 그닥 좋지 않다.

이 경우 `제어 역전`을 사용한다.

Nest는 `제어 역전`을 추상화해서 그 동작이 잘 보이지 않기 때문에 원 글에서는 [typedi](https://github.com/typestack/typedi)를 사용해서 이를 구현했다.

    import "reflect-metadata";
    import { Container, Service } from "typedi";
    
    // Weaponable, Playble 은 위와 같음
    
    @Service()
    class Mongdungee implements Weaponable {
      public void swing() {
        console.log('Mongdungee Swing!');
      }
    }
    
    @Service()
    class Warrior implements Playable {
      // 아래 코드 중요!
      constructor(private readonly weapon: Weaponable) {}
    
      public void attack() {
        this.weapon.swing();
      }
    }
    
    
    const playerInstance = Container.get<Warrior>(Warrior);
    playerInstance.attack();  // "Mongdungee Swing!"
    

코드 어디에서도 `new`가 없다. 하지만 잘 동작함을 확인할 수 있다.

이는 `typedi`의 `Container`라는 친구가 알아서 클래스의 인스턴스를 생성했기 때문이다.

이처럼 제어권을 내가 아닌 프레임워크에게 넘기는 것이 `제어 역전`이다.

여기서 라이브러리와 프레임워크의 결정적인 차이가 발생한다.

라이브러리는 **내가 짠 코드에서** 필요할 때 라이브러리를 실행시킨다.

즉 라이브러리는 내 코드의 소비자가 될 수 없다. 반면 프레임워크는 내 코드의 소비자가 될 수 있다.

프레임워크는 **내가 짠 코드가 필요할 때** 알아서 실행시키게 된다.

### 의존성 주입

`제어 역전`(이하 IoC)은 **나 대신 프레임워크가 제어한다**라면 `의존성 주입`(이하 DI)은 프레임워크가 주체가 되어 **네가 필요한 클래스 등을** 너 대신 내가 관리해준다는 개념이라고 생각하면 된다.

멀리서 보면 DI보다 IoC가 더 크고 추상적인 개념이다.

IoC는 추상적이기 때문에 이를 구현한게 바로 DI이며 이는 제어 역전의 구현체 중 하나이다.

그래서 DI를 통해 IoC를 구현했다는 말이 나온다. Nest는 DI를 통해 IoC를 구현한 프레임워크이다.

![Nest js Document](https://storage.googleapis.com/papyrus_images/3a629535d69f61f78e0b6ea8e314d8d90e046ce0dc96e80163727f52f69d50fa.png)

Nest js Document

#### Nest js 공식 Document

… `프로바이더`는 Nest의 기본 개념입니다. 많은 기본 Nest 클래스는 서비스(Service), 레파지토리, 팩토리, 헬퍼 등등의 프로바이더로 취급될 수 있습니다. 프로바이더의 주요 아이디어는 **의존성을 주입**할 수 있다는 점입니다. 이 뜻은 객체가 서로 다양한 관계를 만들 수 있다는 것을 의미합니다. 그리고 객체의 인스턴스를 연결해주는 기능은 Nest 런타입 시스템에 위임될 수 있습니다.

> `Warrior`의 `Mongdungee`가 바로 `프로바이더`이다.

어떤 컴포넌트가 필요하며 의존성을 주입당하는 객체를 `프로바이더`라고 생각하면 된다.

그리고 Nest 프레임워크 내부에서 알아서 컨테이너를 만들어서 관리해준다는 말이다.

의존성을 주입하기 위한 방법은 크게 3가지가 있다.

*   생성자를 이용한 의존성 주입(Constructor Injection)
    
*   수정자를 이용한 의존성 주입(Setter Injection)
    
*   필드를 이용한 의존성 주입(Field Injection)
    

Nest에서는 주로 생성자를 이용한 의존성 주입을 권장한다.

필드를 이용한 의존성 주입은 한 두 스크롤 아래 `속성 기반 주입(Property-based injection)`이라는 항목으로 소개한다.

* * *

Scope
-----

프로바이더는 일반적으로 Nest 프로그램의 수명 주기와 동기화 된 수명(범위)을 갖는다.

Nest 프로그램이 [부트 스트랩](https://www.wisewiredbooks.com/term-dict/common/bootstrap.html) 될 때 모든 종속성을 해결해야 하기 때문에 모든 프로바이더가 인스턴스화 된다.

마찬가지로 Nest 프로그램이 종료되면 각 프로바이더가 메모리에서 삭제된다.

그러나 프로바이더의 수명을 요청 단위로 제한하는 방법도 있다.

다만 성능에 문제가 될 수 있기 때문에 특수한 상황이 아니라면 기본 설정된 수명 주기를 사용하는게 좋다.

* * *

선택적 프로바이더 (Optional Provider)
-----------------------------

때때로, 반드시 해결될 필요가 없는 종속성이 있을 수 있다.

예를 들어 클래스는 **configuration** 객체에 의존할 수 있지만 해당 인스턴스가 없는 경우 기본값을 사용하는 경우이다.

이러한 경우 에러가 발생하지 않으므로 종속성이 선택사항이 된다.

    import { Injectable, Optional, Inject } from '@nestjs/common';
    
    @Injectable()
    export class HttpService<T> {
      constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
    }
    

프로바이더가 선택적임을 나타내려면 `constructor` 서명에 `@Optional()` 데코레이터를 사용하면 된다.

* * *

속성 기반 주입(Property-based Injection)
----------------------------------

앞서 설명한 의존성 주입의 3가지 방법 중 3번째 방법인 "필드를 이용한 의존성 주입"이다.

지금까지 예제는 의존성이 생성자 방법을 통해 주입되기 때문에 `생성자를 이용한 의존성 주입`이라고 불린다.

매우 구체적인 경우 `속성 기반 주입`이 유용할 수 있다.

예를 들어 최상위 클래스가 하나 또는 여러 프로바이더에 종속되어 있는 경우, 생성자에서 하위 클래스의 `super()`를 호출하여 해당 클래스를 끝까지 전달하는 것은 매우 지루할 수 있다.

이 문제를 방지하려면 속성 수준에서 `@Inject()` 데코레이터를 사용할 수 있다.

    import { Injectable, Inject } from '@nestjs/common';
    
    @Injectable()
    export class HttpService<T> {
      @Inject('HTTP_OPTIONS')
      private readonly httpClient: T;
    }
    

만약 클래스가 다른 프로바이더를 확장(extend)하지 않는 이상 반드시 생성자를 이용한 의존성 주입을 사용하는 것이 좋다고 한다.

---

*Originally published on [Primrose](https://paragraph.com/@primrose/nest-js-provider)*
