(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 |
댓글