(2020-05-23에 작성한 글입니다.)
코드는 여기에서 확인할 수 있다.
Nest.js는 기본적으로 DI를 지원하기 때문에 별도의 library를 설치하지 않아도 DI를 적극적으로 사용할 수 있게 도와준다.
Hexagonal architecture와 같이 특정 인프라 스트럭쳐에 의존하지 않는 코드를 작성한다면
Repository와 같이 특정 인프라 스트럭쳐를 사용하는 기능은 interface로 추상화하고 이에 대한 구현은 클래스에 할 것이다.
사용자 기능을 구현하는 Nest.js application을 만든다면 아래와 같이 시작할 수 있을 것이다.
// src/user/UserService.ts import { Injectable } from '@nestjs/common'; @Injectable() export class UserService { constructor( private readonly userRepository: UserRepository ) {} getUser(id: number): User { return this.userRepository.findById(id); } }
// src/user/UserRepository.ts export interface UserRepository { findById(id: number): User | null; }
// src/user/InMemoryUserRepository.ts import { Injectable } from '@nestjs/common'; @Injectable() export class InMemoryUserRepository implements UserRepository { private readonly users: { [userId: string]: User } = {}; findById(id: number): User | null { const user = this.users[id]; return !!user ? user : null; } }
그리고 UserModule을 만들어서 위 component들을 Nest.js가 DI를 할 수 있도록 설정하자.
// src/user/user.module.ts import { Module } from '@nestjs/common'; @Module({ imports: [], controllers: [], providers: [ UserService, UserRepository ], }) export class UserModule {}
하지만 위와 같이 UserRepository interface를 providers
에 지정하면 다음과 같이 compile 에러가 난다.

그러면 구현 클래스를 provider로 UserModule의 providers에 지정하면 어떨까?
// src/user/user.module.ts import { Module } from '@nestjs/common'; @Module({ imports: [], controllers: [], providers: [ UserService, InMemoryUserRepository ], }) export class UserModule {}
AppModule에 UserModule을 추가하고 실행해보자.
// src/app.module.ts import { Module } from '@nestjs/common'; @Module({ imports: [ UserModule ], controllers: [], providers: [], }) export class AppModule {}
애플리케이션을 실행하면 아래와 같이 UserService가 UserRepository dependency를 injection 받아야하는데 해당 dependency를 찾을 수 없다는 에러메시지를 볼 수 있을 것이다.
$ npm run start > interface-di@0.0.1 start /Users/son/dev/study/what_i_study/JS/NestJS/interface-di > nest start [Nest] 46016 - 05/23/2020, 3:53:46 PM [NestFactory] Starting Nest application... [Nest] 46016 - 05/23/2020, 3:53:46 PM [ExceptionHandler] Nest can't resolve dependencies of the UserService (?). Please make sure that the argument Object at index [0] is available in the UserModule context.
Nest.js는 interface DI를 지원하지 않는 것일까?
이에 대한 해답은 Nest.js issue: Typescript - Interface injecting에서 확인할 수 있다.

일단 interface를 providers에 지정할 수 없는 이유는 interface는 개발 시에만 존재하기 때문에 값으로 사용할 수가 없다는 것이다.
실제로 컴파일된 interface를 파일을 확인하면 다음과 같이 비어있는 것을 확인할 수 있다.
// dist/user/UserRepository.js "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); //# sourceMappingURL=UserRepository.js.map

Nest.js의 창시자인 kamilmysliwiec 역시 ts types는 컴파일이 되면 사라지기 때문에 인해서 interface로 DI를 할 수 없다고 못박았다.
interface DI를 하고자 하면 다음과 같이 Nest.js의 provider를 사용한 약간의 스킬로 구현할 수는 있다.
import { Module } from '@nestjs/common'; @Module({ imports: [], controllers: [], providers: [ UserService, // provide: interface, useClass: DI 하려는 interface 구현체 { provide: 'UserRepository', useClass: InMemoryUserRepository } ], }) export class UserModule {}
import { Injectable, Inject } from '@nestjs/common'; @Injectable() export class UserService { constructor( // provide에서 설정한 string 값을 @Inject에 적어준다 @Inject('UserRepository') private readonly userRepository: UserRepository ) {} getUser(id: number): User { return this.userRepository.findById(id); } }
위와 같이 설정한 후 애플리케이션을 실행하면 정상적으로 실행되는 것을 볼 수 있을 것이다.
$ npm run start > interface-di@0.0.1 start /Users/son/dev/study/what_i_study/JS/NestJS/interface-di > nest start [Nest] 46301 - 05/23/2020, 3:58:35 PM [NestFactory] Starting Nest application... [Nest] 46301 - 05/23/2020, 3:58:35 PM [InstanceLoader] AppModule dependencies initialized +17ms [Nest] 46301 - 05/23/2020, 3:58:35 PM [InstanceLoader] UserModule dependencies initialized +0ms [Nest] 46301 - 05/23/2020, 3:58:35 PM [NestApplication] Nest application successfully started +8ms
'JS & TS > Nest.js' 카테고리의 다른 글
AdminBro로 Nest.js admin 쉽게 만들기 (8) | 2021.05.29 |
---|---|
Nest.js Docker image 만들기 (0) | 2021.01.02 |
댓글