본문 바로가기

개발새발 개발자/JavaScript

[NestJS] DI 처리 후 Module에 등록하기

NestJS의 DI 방법은 스프링과 비슷하면서도 사뭇 다르다. 자바스크립트의 module 개념도 친숙하지 않은 상황에서 DI를 적용시키다 많이 헤맸다. 그동안 이리저리 부딪히면서 깨달은 내용을 정리해본다.

 

1. DI 처리하기

constructor(private readonly catsService: CatsService) {}

이렇게 constructor에서 해당 데이터, 그러니까 여기선 CatsService의 인스턴스를 생성하고 리턴하면 NestJS가 알아서 resolve 해준다. 

 

앞에서 말했듯 NestJS는 스프링처럼 annotation을 이용해 DI를 한다. 

[auth.service.ts]

@Injectable()
export class AuthService {
    constructor(
        @Inject('JwtService') private readonly jwtService: JwtService,
        @Inject('UserService') private readonly userService: UserServiceImpl,
        @Inject('ValidationService') private readonly validationService: ValidationServiceImpl,
    ) {}
}
[user.service.impl.ts]

@Injectable()
export class UserServiceImpl implements UserService {
    constructor(@InjectRepository(User) private userRepository: IUserRepository) {}
}

만약 interface처럼 implement하는 클래스가 많을 경우엔, 자동으로 어떤 클래스인지 찾아줄 수가 없으므로 위처럼 직접 지정해준다. 스트링으로 별칭을 지어주거나 User처럼 객체를 넣어줄 수 있다.

 

2. DI하려는 클래스를 Module에 Provider로 등록하기

위처럼 선언했다고 끝이 아니다. injection을 수행하려면 module에 등록해주어야 한다.

 

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class ApplicationModule {}

기본적으로는 providers 부분에 해당 클래스를 넣어주면 된다.

 

[auth.module.ts]

@Module({
    imports: [
        PassportModule.register({ defaultStrategy: 'jwt' }),
        JwtModule.register({
            secretOrKeyProvider: (
                requestType: JwtSecretRequestType,
            ) => {
                switch (requestType) {
                    case JwtSecretRequestType.SIGN:
                        return 'privateKey';
                    case JwtSecretRequestType.VERIFY:
                        return 'publicKey';
                    default:
                        return 'hard!to-guess_secret';
                }
            },
            signOptions: {
                expiresIn: 3600,
            },
        }),
        UserModule],
    providers: [AuthService,
        {provide: 'UserService', useClass: UserServiceImpl},
        {provide: 'ValidationService', useClass: ValidationServiceImpl},
        {provide: 'IUserRepository', useClass: UserRepository},
        {provide: 'BridgeService', useClass: Web3BridgeService},
        {provide: 'WEB3', useClass: Web3},
    ],
    controllers: [AuthController],
    exports: [PassportModule],
})
export class AuthModule {
}

위에서는 provide와 useClass를 이용해 바인딩해주고 있다. 자세한 내용은 공식 docs의 Custom Provider를 참고하면 된다. 간단히 말하자면, 의존성 주입 시 특정한 클래스를 지정해주고 싶을 때 사용한다.

 

이때, provide는 @Inject가 있던 클래스에서 @inject('JwtService')의 괄호 안의 내용 즉, JwtService 부분을 넣어준다. 이때 주의할 점은,

 

  1. @inject('JwtService')처럼 string으로 별칭을 지정해줬다면 그대로 provide: 'JwtService'로 쓴다.
  2. @inject(User)처럼 객체 그대로를 넣었다면 그대로 provide: User 라고 넣어야 한다.

 

내가 여기서 잘못한 것

 

  1. repository는 위와 같이 사용하지 않고 그냥 클래스 자체로 선언해야 했다.
  2. 맨 위에 defaultStrategy로 jwt를 지정해놓고 그에 대한 내용을 import 하지 않았다.

 

[auth.module.ts]

@Module({
    imports: [
        PassportModule.register({ defaultStrategy: 'jwt' }),
        JwtModule.register({
            secretOrKeyProvider: (
                requestType: JwtSecretRequestType,
            ) => {
                switch (requestType) {
                    case JwtSecretRequestType.SIGN:
                        return 'privateKey';
                    case JwtSecretRequestType.VERIFY:
                        return 'publicKey';
                    default:
                        return 'hard!to-guess_secret';
                }
            },
            signOptions: {
                expiresIn: 3600,
            },
        }),
        UserModule],
    providers: [AuthService, UserRepository, JwtStrategy,
        {provide: 'UserService', useClass: UserServiceImpl},
        {provide: 'ValidationService', useClass: ValidationServiceImpl},
        {provide: 'BridgeService', useClass: Web3BridgeService},
        {provide: 'WEB3', useClass: Web3},
    ],
    controllers: [AuthController],
    exports: [PassportModule],
})
export class AuthModule {
}

이렇게 변경하면 정상적으로 작동한다.

 

 

맨 처음 NestJS를 접하고 같은 내용의 공식 문서를 읽을 땐 정말 무슨 말인지 하나도 와닿지가 않았는데 실제 프로젝트에서 부딪혀보고 다시 읽으니 '그게 그런 뜻이었구나' 하고 이해하게 되었다. 조금이지만 여전히 발전하고 있다!