Article#1 : NestJS - Provider

roach·2021년 5월 30일
2

Article

목록 보기
1/3

서론

  • 요즘 프로그래밍 라이브러리나 프레임워크에 관심을 기울이지 않아, 매주 하루는 여러가지 아티클을 정리해 보는 날을 가져보려고 한다. 그중 첫번째로 NestJS 의 Provider 에 대한 Article 을 적용해보려고 한다. 꼭 NestJS 를 쓰려는 사람이 읽으라는게 아니라, 다른 프레임워크 사람들도 이 프레임워크는 DI 를 이렇게 해석했네 ? 라고 읽어도 좋은 공부가 될 수 있을 것이다.

NestJS Provider

  • Provider 는 Nest 의 기본적인 컨셉이다. 대다수의 Nest Class 들은 Provider 처럼 대해진다. Provider 를 사용하는 주된 생각은 Dependency Injection 을 하기 위함이다. Spring 의 IoC Container 와 흡사하다고 생각하면 된다. 거의 비슷하다. 이렇게 Provider 를 이용함으로 인해 Nest 는 Runtime 안에서 많은 관계를 가지도록 설계할 수 있다.

Injectable()

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}
  • Service 단에 주입가능하다는 표시를 해주자. 저 Injectable() 이라는 데코레이터는 Nest IoC Container 에 객체의 생성과 관리를 위임하겠다는 뜻을 말한다.

Dependency Injection

  • NestJS 는 DI 를 이용해 강력한 디자인 패턴을 구축할 수 있었다. NestJS 가 이렇게 강한 구축을 할 수 있었던 이유는 TypeScript 의 도입이 상당히 컸던것 같다. Type 비교를 통해 DI 를 쉽게 진행할 수 있기 때문이다. IoC 는 해당 Instance 를 SingleTon 형태로 관리한다. 아마 스프링처럼 Map 자료구조를 사용하기에 유사 싱글톤으로 관리된다고 생각하는 편할 것 같다. 실제로도 Description 만 봐도 비슷하게 유지하고 있을 것 같다.
  • 아래는 Nest IoC Container 에 등록한 후 Constructor Dependency Injection 을 받는 방법이다. Nest 에서도 Constructor Injection 을 권장하니 이렇게 사용하도록 하자!
constructor(private catsService: CatsService) {}

Scopes

  • 프로바이더는 일반적으로 어플리케이션 시스템과 생명주기를 동일하게 가져간다. 어플리케이션이 실행될때, 모든 의존성이 Resolve 된다. 그래서 주의해야 할 점은 BootStrap 에 IoC Container 에 관리를 받고 있는 MiddleWare 를 등록한다면 정상적으로 동작하지 않을 수 있다. IoC Container 의 관리를 받는 미들웨어라면 Module 을 통해 MiddleWare Chain(PipeLine) 을 구성하도록 하자!

Custom Provider

  • Nest 는 Inversion of Controll 컨테이너를 통해 Provider 들의 관계를 풀어나가는 형태이다. 재밌게도 Provider 의 형태는 사람들이 축약되서 착각하는되 원래는 아래와 같은 형태이다.
@Module({})
@Global()
export class JwtModule {
  static forRoot(options: JwtModuleOptions): DynamicModule {
    return {
      module: JwtModule,
      imports: [UsersModule],
      providers: [
        {
          provide: Roach,
          useValue: "내 이름은 로치입니다.",
        },
				{
					provide: Jobs,
					useValue: "개발자로 일하고 있습니다."
				}
        JwtService,
      ],
      exports: [JwtService, CONFIG_OPTIONS],
    };
  }
}
  • 스프링을 접해본 사람도 익숙한 사람이 있고, 익숙하지 않은 사람이 있을텐데 간단히 말하면 providekey useValue 혹은 use... 으로 쓴것이 value 가 되어 IoC Container 에 등록된뒤 DI 를 진행할 수 있다. 필자도 그렇게 하여 공통적으로 쓰이는 Value 들은 DI 를 받으려고 하고 있다. 간단히 얘기하면, 저기 providers 배열안의 JwtService 를 풀어서 써보면 아래와 같이 변한다.
 providers: [
        {
          provide: Roach,
          useValue: "내 이름은 로치입니다.",
        },
				{
					provide: Jobs,
					useValue: "개발자로 일하고 있습니다."
				}
        {
					provide: JwtService,
					useClass: JwtService
				},
      ],
  • 약간의 고민을 하는 사람이라면 알겠지만, 위에서 설명했듯 BootStrap 시 모든 Dependency 를 Resolve 하므로 Nest 는 켜지는데 약간 느릴 수 밖에 없다.

useFactory

  • 생각보다 상당히 유용한 기능이라고 여겨져 아티클에 적어보려고 한다.
const connectionFactory = {
  provide: 'CONNECTION',
  useFactory: (optionsProvider: OptionsProvider) => {
    const options = optionsProvider.get();
    return new DatabaseConnection(options);
  },
  inject: [OptionsProvider],
};

@Module({
  providers: [connectionFactory],
})
export class AppModule {}
  • 간단하게 공식문서를 정리해보면, 우리의 팩토리 메소드는 Nest IoC Container 와 관련된 Instance 들과 연관을 가질 수 있는데, 그렇게 될시 inject: [OptionsProvider] 이런식으로 Injection 을 받을 수 있게 된다. 근데 이렇게 되면 BootStrap 시 resolve 되는게 아니라, Factory Function 이 Call 될때 Dependency 가 Resolve 된다고 설명해주는 것 같다. 여튼 이런 패턴이 필요하게 되기에 알아두면 좋을 것 같다!

정리하며 느낀점

  • TypeScript 가 도입되고 계속해서 TS 를 Support 하는 서버 프레임워크가 많아지고 있는데, 이는 좋은 행보라고 생각된다. 사실 요즘들어 느끼는 것이 정적언어 프레임워크의 빠른 발전으로 개발속도도 충분히 좋아졌다고 본다. 그 단적인 예가 Spring Boot 를 이용해 개발하면, Dependency 를 개발자가 설정해 줄 필요도 없이 진행이 가능하다. Config 는 해줘야 겠지만
  • 회사에서 TS 를 이용해서 작은 프로젝트들을 진행하고 있는데, NestJS 도 충분히 좋아보인다. 아직까지 서버 쪽은 Ruby On Rails 를 사용하고 있긴한데, 이렇게 따로 공부해봐도 좋은 것같다. 이번 사이드 프로젝트에 채팅 기능을 넣게 되면, Go 언어를 통해서 마이크로 서비스로 포팅해보고 싶다!
profile
모든 기술에는 고민을

0개의 댓글