[Nest.js][TIL] IoC, DI

Trippy·2024년 1월 29일
0

Nest.js

목록 보기
15/15
post-thumbnail

Provider

  • provider는 Nest의 기본 개념
  • 대부분의 Nest 클래스는 service, repository, factory, helper 등 provider로 취급될 수 있다.
  • provider의 주요 아이디어는 dependency로 주입할 수 있다.
  • dependency로 주입할 수 있다는 의미는 object가 다른 object와 다양한 관계를 만들 수 있고, 객체의 instance를 'wiring up'기능은 NestJs runtime system에 위임될 수 있다.

IoC

Dependency Injection(DI)는 IoC기술로, 사용자 자신의 코드로 종속성을 인스턴스화하는 대신 IoC 컨테이너(NestJs 런타임 시스템)로 위임한다.

  • 개발자가 제어할 영역을 NestJs에게 넘겨줌
  • NestJs는 provider 사이의 관계를 해결해 주는 내장된 inversion of control(IoC) container를 가지고 있다.
  • DI를 구현하기 위해서는 IoC컨테이너 기술이 필요하다. IoC는 provider를 다른 컴포넌트에 주입할 때 사용하는 기술, Nest는 프레임워크에 IoC를 구현하고 있다.
expor class UserController{
    constructor(private readonly usersService : UsersService){}
}
  • UserController는 UserService에 의존하고 있다.
  • 하지만 UserService는 객체의 라이프 사이클에는 전혀 관여하지 않는다.
  • 단지 controller의 생성자에 주어진 객체를 가져다 쓰고 있다.
  • 이 역할을 하는 것이 IoC(Inversion of control)
  • IoC의 도움으로 객체의 라이프 사이클에 신경 쓰지 않아도 된다.

DI는 IoC컨테이너가 직접 객체의 생명주기를 관리하는 방식이다.
A객체에서 B객체가 필요하다고 할 때 (A는 B에 의존) A클래스에는 B클래스를 생성해서 사용가능 -> 문제는 B의 구현체가 변경되었을 때 발생 -> A는 B를 직접 참조하기에 B가 변경될 떄마다 complier는 a를 다시 컴파일 해야함 -> A와 B가 class가 아니라 module이라고 하면 변경의 크기는 더 커지게 되고 complie 시간은 더 오래 걸린다. -> 이를 해결하기 위해 B에 대한 interface를 정의하고, A에는 IB타입을 이용한다 -> 하지만 interface B의 구현체를 직접 생성해야 되는 건 여전함 -> IoC의 강력함이 발휘.

IoC 사용 X

export interface Person {
  getName: () => string;
}

@Injectable()
export class Dexter implement Person {
  getName() {
    return 'Dexter';
  }
}

@Injectable()
export class Jane implement Person {
  getName() {
    return 'Jane'
  }
}

class MyApp {
  private person: Person;
  constructor() {
    this.person = new Dexter();
  }
}

IoC 사용 O

class MyApp2 {
  constructor (@Inject('Perso') private p: Person) {}
}

Person 객체의 관리는 IoC가 담당한다.
Person은 인터페이스이기에 Person을 실제 구현한 클래스를 module에 선언해야 객체를 생성할 수 있다.

@Module({
  controllers: [UserController],
  providers: [
    UserService,
    {
      provide: 'Person',
      useClass: Dexter
    }
  ]
})

DI(dependency Injection)

  • dependencies는 클래스가 동작하기 위해 필요한 서비스나 객체를 의미

  • Dependency injection, or DI, is a design pattern in which a class requests dependencies from external sources rather than creating them.

  • dependency InJection(DI)는 class가 의존성 객체를 외부에 요청하고 외부에서 인스턴스를 생성해서 주입하는 디자인 패턴

  • @Injectable() decorator는 이 class를 DI system에 활용하겠다는 것을 의미한다.(Angular 공식 홈페이지)

  • @Injectable() decorator는 UserService가 Nest IoC 컨테이너에서 관리할 수 있는 클래스임을 선언하는 메타데이터를 첨부..

  • @Injectable() 만일 userService에 이 데코레이터를 사용하면 다른 어떤 Nest component에서도 주입할 수 있는 provider가 된다. 별도의 scope를 지정하지 않으면 singleton 인스턴스가 됨

constructor(private userRepository: UserRepository)

NestJS는 일반적으로 Dependency Injection으로 알려진 디자인 패턴을 기반으로 구축되었다.

  • NestJS에서는 TypeScript 기능 덕분에 dependency이 유형별로 해결되기에 매우 쉽게 관리할 수 있다.

  • UserRepository를 UserService에 Dependency Injection을 하라면 contructor에 접근 제어 지시자를 활용해서 초기화를 해주면 된다.

  • private를 사용하면 동일한 위치에서 즉시 userRepository 멤버를 선언하고 초기화할 수 있다.

  • 위와 같은 코드를 생성자 기반 주입 -> provider는 생성자 메서드를 통해 주입되기 때문이다.

  • 특정한 경우 속성 기반 주입(Property-based-injection)이 유용 -> 최상위 클래스가 하나 또는 여러 프로바이더에 의존하는 경우 생성자의 하위 클래스에서 super()를 호출하여 모든 프로바이더를 전달하는 것은 매우 쉽지 않다. 이를 방지하기 위해 property 수준에서 @Inject() decorator를 사용한다.

import { UserRepository } from './../repository/user.repository';
import { PrismaService } from './../prisma/prisma.service';
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  controllers: [UserController],
  providers: [UserService, PrismaService, UserRepository],
})
export class UserModule {}

provider로 UserService, PrismaService, UserRepository를 정의하고 controller가 있으니 injection을 수행할 수 있게 service를 Nest에 등록해야 됨. @module() 데코레이터의 providers 배열에 service들을 추가해서 이를 수행한다. 이렇게 해서 NestJS는 userController class의 dependency를 해결할 수 있다.

profile
감금 당하고 개발만 하고 싶어요

0개의 댓글