- @Module() 데코레이터가 달린 클래스이다.
- Nest가 애플리케이션 구조를 만들 때 사용할 수 있는 메타데이터(데이터에 대한 데이터)를 제공해주는 역할을 한다.
- 각 응용 프로그램에는 적어도 하나 이상의 루트모듈이 있다. -> 애플리케이션 그래프를 구성하기 위해 사용하는 시작점!
@Module()
- providers
- Nest 인젝터(의존성을 주입하는 Nest 내부 모듈)가 인스턴스화 시키고 이 모듈 안에서 공유하는 프로바이더
- controllers
- imports
- 해당 모듈에서 필요한 모듈의 집합(프로바이더를 노출하는 모듈)
- exports
- 프로바이더의 부분집합이며, 다른 모듈에서 사용할 수 있도록 할 프로바이더
Feature Modules
- UserController와 UserService는 둘다 유저를 다룬다. 즉, 같은 도메인에 속해 밀접하게 관련되어 있어 기능 모듈로 이동하는것이 좋다.
- 기능 모듈은 특정 기능과 관련된 코드를 구성하여 코드를 체계적으로 유지하고 명확한 설계를 설정한다. 이는 특히나 애플리케이션이나 팀의 규모가 커짐에 따라 커지는 복잡성을 관리하는데 도움을 주며, SOLID 원칙으로 개발하는데 도움이 된다.
// user/user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user/service';
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
// $ nest g mo | module user
- UserModule을 정의한 것인데 User와 관련된 것들을 다 위 모듈에 넣어준다.
- 그 뒤에는 이 모듈을 루트 모듈 즉, app.module.ts에 정의된 AppModule로 가져온다.
// app.module.ts
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
@Module({
imports: [UserModule],
})
export class AppModule {}
Shared Modules
- Nest에서 모듈은 기본적으로 Singleton이다. 이는 Nest 고유의 특성이 아닌, Node의 특성이다. 이러한 특성으로 Nest에서는 여러 모듈 간에 쉽게 공급자의 동일한 인스턴스를 공유할 수 있다.
- 모든 모듈은 자동으로 공유 모듈이다. 즉, 공유가 가능한 모듈이라는 뜻이며, 일단 생성되면 모든 모듈에서 재사용할 수 있다.
- 다른 여러 모듈간에 인스턴스를 공유하려면 exports 배열에 해당 프로바이더를 추가하여 프로바이더를 노출시켜줘야한다.
// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user/service';
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
- 이렇게해서 UserModule을 가져오는 모듈에서는 UserService에 접근할 수 있으며, 이 모듈을 가져오는 다른 모듈과 동일한 인스턴스를 공유한다.
DI
- 모듈 클래스 또한 프로바이더를 주입할 수 있다.
- 하지만 모듈 클래스 자체는 순환 종속성으로 인해 프로바이더로 주입할 수 없다.
Global Modules
- 모든 곳에서 동일한 모듈을 가져오는 좋지않다. 별도의 절차 없이 어플리케이션 어디에서나 사용할 수 있게 하기 위해선 전역적으로 적용할 수 있는 수단이 필요하다.
- Nest는 전역적으로 프로바이더를 등록할 수 없다.Nest에서의 프로바이더는 모듈을 벗어날 수가 없기 때문이다. 해당 프로바이더를 다른 곳에서 사용하려면 해당 프로바이더가 속한 모듈을 먼저 가져오지 않으면 안된다.
- 어디에서나 사용할 수 있어야 하는 프로바이더를 제공하려면 @Global() 데코레이터를 사용해 모듈을 전역 모듈로 만들어주자.
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user/service';
@Global()
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
- @Global() 데코레이터는 모듈을 전역적으로 사용할 수 있도록 만든다. 전역 모듈은 일반적으로 루트 또는 코어모듈에 의해 단 한번만 등록되어야 한다.
- UserService는 어디에서나 사용할 수 있으며 UserService를 주입하려는 모듈은 UserModule에 있는 imports 배열에 추가하지 않아도 된다.
- 그치만 모듈의 imports 배열에 넣는게 일반적으로 선호하는 방법이다. 진짜 보일러플레이트 코드가 많을때 사용하자.
Dynamic modules
- Nest 모듈 시스템에는 동적 모듈이라 불리는 강력한 기능이 있다.
- 이 기능을 사용하면 프로바이더를 동적으로 등록하고 구성할 수 있는 커스터마이징 모듈을 쉽게 만들 수 있다.
import { Module, DynamicModule } from '@nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';
@Module({
providers: [Connection],
})
export class DatabaseModule {
static forRoot(entities = [], options?): DynamicModule {
const providers = createDatabaseProviders(options, entities);
return {
module: DatabaseModule,
providers: providers,
exports: providers,
};
}
}
- 이 모듈은 기본적으로 Connection 프로바이더를 정의하지만, forRoot() 메서드에 전달된 entities 및 options 객체에 따라 Repository와 같은 여러 프로바이더를 노출한다.
- 동적 모듈이 반환하는 속성은 @Module() 데코레이터에 정의된 모듈의 기본적인 메타데이터를 Override하지않고 확장한다. 이렇게하면 정적으로 선언된 연결 제공자와 동적으로 생성된 저장소 제공자가 모두 모듈에서 내보내진다.
- 동적모듈은 지금까지 본 정적 바인딩을 사용하는 것과는 다르게 한 모듈을 다른 모듈로 가져오고 해당 모듈을 가져올 때 해당 모듈의 속성과 동작을 사용자가 지정하기 위한 API를 제공한다.