DI with Nest.js

코드는 여기에서 확인할 수 있다.


Nest.js는 기본적으로 DI를 지원하기 때문에 별도의 library를 설치하지 않아도 DI를 적극적으로 사용할 수 있게 도와준다.

Hexagonal architecture와 같이 특정 인프라 스트럭쳐에 의존하지 않는 코드를 작성한다면
Repository와 같이 특정 인프라 스트럭쳐를 사용하는 기능은 interface로 추상화하고 이에 대한 구현은 클래스에 할 것이다.

사용자 기능을 구현하는 Nest.js application을 만든다면 아래와 같이 시작할 수 있을 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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);
}
}
1
2
3
4
5
// src/user/UserRepository.ts

export interface UserRepository {
findById(id: number): User | null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 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를 할 수 있도록 설정하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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에 지정하면 어떨까?

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/user/user.module.ts

import { Module } from '@nestjs/common';

@Module({
imports: [],
controllers: [],
providers: [
UserService,
InMemoryUserRepository
],
})
export class UserModule {}

AppModuleUserModule을 추가하고 실행해보자.

1
2
3
4
5
6
7
8
9
10
11
12
// src/app.module.ts

import { Module } from '@nestjs/common';

@Module({
imports: [
UserModule
],
controllers: [],
providers: [],
})
export class AppModule {}

애플리케이션을 실행하면 아래와 같이 UserServiceUserRepository dependency를 injection 받아야하는데 해당 dependency를 찾을 수 없다는 에러메시지를 볼 수 있을 것이다.

1
2
3
4
5
6
7
$ 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를 파일을 확인하면 다음과 같이 비어있는 것을 확인할 수 있다.

1
2
3
4
5
// 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를 사용한 약간의 스킬로 구현할 수는 있다.

1
2
3
4
5
6
7
8
9
10
11
12
import { Module } from '@nestjs/common';

@Module({
imports: [],
controllers: [],
providers: [
UserService,
// provide: interface, useClass: DI 하려는 interface 구현체
{ provide: 'UserRepository', useClass: InMemoryUserRepository }
],
})
export class UserModule {}
1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
}

위와 같이 설정한 후 애플리케이션을 실행하면 정상적으로 실행되는 것을 볼 수 있을 것이다.

1
2
3
4
5
6
7
8
9
$ 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
Share