티스토리 뷰

nodejs

express에서 typedi로 의존성 주입하기

안양사람 2022. 2. 18. 21:09
728x90
SMALL

배경

예전부터 의존성 주입에 대해서 알아보고 싶었는데 귀찮아서 미루고 있었다. 이번 기회에 좀 제대로 알아보려고 한다.

의존성 주입(Dependency Injection)이란

의존성 주입이란 하나의 객체가 다른 객체의 의존성을 제공하는 테크닉이다. 조금 감이 안올수도 있다. 코드로 예를 들어보자.

class Animal {
  public hi() {
    console.log('hi');
  }
}

class Player {
  private readonly animal: Animal;

  constructor() {
    this.animal = new Animal();
  }

  public hi() {
    this.animal.hi();
  }
}

const player = new Player();
player.hi();

위 코드의 문제점이 뭘까?? 한번 생각해보자

class Dog extends Animal {
  public hi() {
    console.log('월월');
  }
}

class Player {
  private readonly animal: Animal;

  constructor() {
    this.animal = new Dog();
  }

  public hi() {
    this.animal.hi();
  }
}

const player = new Player();
player.hi();

위와 같이 player에서 this.animal을 변경하는 경우가 있다. 그런데 이런 경우에 new Animal을 new Dog로 모든 코드에서 바꿔줘야 한다. 파일이 몇개 안되면 별로 상관이 없지만 파일이 많아질수록 바꿔야 할 코드가 많아진다. 이럴때 의존성 주입을 사용한다.

의존성 주입 적용하기

class Player {
  private readonly animal: Animal;

  constructor(animal: Animal) {
    this.animal = animal;
  }

  public hi() {
    this.animal.hi();
  }
}

const player = new Player(new Dog());
player.hi();

이런식으로 의존성을 주입하면 의존성이 변경되어도 모든 파일을 수정할 필요가 없어진다.

제어권의 역전 IoC(Inversion of control)

위의 방법으로 의존성을 주입하면 모든 문제점이 해결된걸까?? 그렇지 않다. new A(new B(new C())) 몇 개의 계층만 만들어도 종속성이 많아지고 여러개의 클래스 인스턴스를 넣어줘야 한다. 이렇게 의존성을 우리가 관리하는 것은 좋은 방법은 아니다. 이때 사용하는 것이 typedi다. typedi는 인스턴스를 생성하고 관리하는 제어권을 갖는 주체가 되고 이를 제어권의 역전이라고 한다.

typedi 세팅

먼저 타입스크립트를 사용해야 한다. 그리고 데코레이터 문법을 사용하기 때문에 조금 설정해야 될 것들이 있다.(typeorm과 똑같다)
yarn add typedi reflect-metadata

tsconfig.json

    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,

app.ts 혹은 lodaer.ts(데코레이터 실행 이전에 import하면 아무데서나 가능)

import 'reflect-metadata';

typedi 적용하기

어렵지 않아서 공식문서의 코드를 그대로 가져왔다. 클래스 위에 Service 데코레이터를 실행하면 constructor에서 의존성 주입을 할 수 있다. 그리고 클래스를 불러올 때 Container.get으로 불러오면 끝이다.

import { Container, Service } from 'typedi';

@Service()
class ExampleInjectedService {
  printMessage() {
    console.log('I am alive!');
  }
}

@Service()
class ExampleService {
  constructor(
    // because we annotated ExampleInjectedService with the @Service()
    // decorator TypeDI will automatically inject an instance of
    // ExampleInjectedService here when the ExampleService class is requested
    // from TypeDI.
    private injectedService: ExampleInjectedService
  ) {}
}

const serviceInstance = Container.get(ExampleService);
// we request an instance of ExampleService from TypeDI

serviceInstance.injectedService.printMessage();
// logs "I am alive!" to the console

참고 블로그
https://darrengwon.tistory.com/1363

728x90
LIST
댓글
공지사항