Search
Duplicate

[오버뷰] 04.Modules

Created
2021/07/02 02:52
Tags
module 이란, @Module() 데코레이터가 달린 클레스 이다.
@Module() 데코레이터는 Nest가 어플리케이션 구조를 구성하는데 사용하는 메타데이터를 제공하니다. (make use of to-v : v 하는데 활용하다)
각각의 어플리케이션은 최소한 root module 이라는 하나의 모듈을 가지고 있어야 합니다.
root module 은 네스트가 application graph 를 만드는데 사용하는 스타팅 포인트 입니다.
application graph 란, 네스트가 모듈과 프로바이더의 관계와 디펜던시를 해결하는데 사용하는 내장 데이터 스트럭쳐 이니다.
아주작은 어플리케이션의 경우 이론적으로 단지 루트모듈만 필요할 수 있습니다. (하지만 전형적인 케이스는 아닙니다)
우리는 당신의 컴포넌트를 효과적으로 구성하는 방법으로 모듈이 강력하게 추천된다는 것을 강조하고 싶습니다.
결과적으로 어플리케이션은 여러개의 모듈들로 구성됩니다. (각각 밀접하게 관련된 기능 set을 캡슐랑화 합니다.)
@Module() 데코레이터는 하나의 오브젝트(아래의 프로퍼티들을 포함하는)를 받습니다. :

providers : 프로바이더는 네스트 인젝터에 의해 인스턴스화 되고, 최소한 이 모듈을 건너 공유될 수 있습니다.

controllers : 인스턴스화 되야 하는 이 모듈 안에 정의된 컨트롤러들의 집합

imports : 이 모듈에서 요청된 프로바이더들을 익스포트하는 모듈의 임포트 리스트 (말 겁나 어렵게하네)

exports : 이 모듈에서 제공하고 이 모듈을 가져오는 다른 모듈에서 사용할 수 있어야하는 프로바이더들의 하위집합

모듈은 프로바이더들을 기본적으로 캡슐화 합니다.
현재 모듈의 직접적인 부분이 아니거나, 임포트된 모듈이 익스포트하는 하지 않는 프로바이더들은 인젝트 하는 것이 불가능하니다.
당신은 모듈로부터 익스포트된 프로바이더들을 모듈의 public interface 나 API 라고 생각할 수 있습니다.

Feature modules (기능 모듈)

CatsControllerCatsService 는 같은 어플리케잇녀 도메인에 속해있습니다.
이것들은 가깝게 연관되어 있기에, 이것을 feature module 로 옮기는 것은 메잌 센스 하다.
feature module 은 간단하게 특정 기능에 연관된 코드를 구성하고, 코드가 확실한 바운드 안에 있는 것을 유지한다.
이것은 우리가 복잡성과, SOLID 원칙의 개발 (특히 어플리케이션 이나 팀의 규모가 커짐에 따라) 을 매니지 하도록 도와준다.
To demonstrate this, we'll create the CatsModule
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule {}
Plain Text
복사

HINT : nest g module cats

위에서 우리는 CatsModulecats.module.ts 안에 선언하고, 관련된 모든 것들을 이 모듈에 넣었다
마지막 남은 일은 이 CatsModule 을 루트모듈에 임포트 하는 것이다.
import { Module } from '@nestjs/common'; import { CatsModule } from './cats/cats.module'; @Module({ imports: [CatsModule], }) export class AppModule {}
Plain Text
복사

Shared modules

네스트에서 모듈은 기본적으로 싱글톤이다.
그리고 이것은 여러 모듈들이 서로 같은 프로바이더의 인스턴스를 쉽게 공유 할 수 있게 한다.
모든 모듈은 기본적으로 shared module 입니다.
한번 만들어지면 모든 모듈에서 재사용 될수 있습니다.
우리가 CatsService 인스턴스를 몇몇 다른 모듈들에서 쉐어 하기를 원한다고 상상해봅시다. (말을 왜케 어렵게하냐 진짜)
그러기 위해선, 우리는 첫째로 CatsService 프로바이더를 export 할 필요가 있습니다. 어떻게? 모듈의 exports array 에 더하는 것으로. 아래를 보세요
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], exports: [CatsService] }) export class CatsModule {}
Plain Text
복사
이제 CatsModule 을 임포트한 모든 모듈이 CatsService 를 엑세스 할 수 있습니다.
또한 모든 모듈과 같은 인스턴스를 공유합니다. (네스트의 거의 모든게 싱글턴 이니까)

Module re-exporting

위에서 본대로 모듈은 그들의 내부 프로바이더들을 내보낼 수 있습니다.
여기에 더불어 모듈들은 자신이 가져온 모듈을 다시 내보낼 수 있습니다.
아래의 예에서 CommonModuleCoreModule 이 가져오기도 하고, 내보내기도 합니다
@Module({ imports: [CommonModule], exports: [CommonModule], }) export class CoreModule {}
Plain Text
복사

Dependency Injection

모듈 클레스는 프로바이더들을 주입 할 수 있습니다. (예를들어, configuration 목적으로)
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule { constructor(private catsService: CatsService) {} }
Plain Text
복사
그러나 모듈클레스 그 자체는 프로바이더로 인젝트 될 수 없습니다. (왜냐면 circular dependency 때문에)

Global Modules

만약 당신이 같은 모듈들의 집합을 모든 곳에 임포트 해야 한다면, it can get tedious (지루 할 수 있다)
네스트와 다르게, Angular providers 는 글로벌 스코프로 등록된다.
일단 정의되면, 모든 곳에서 사용 될 수 있다.
하지만 네스트는 이와 다르게, 모듈 스코프내에서 프로바이더들을 캡슐화 합니다.
당신은 캐슐화된 모듈을 임포트하지 않고는, 모듈의 프로바이더들을 사용 할 수 없습니다.
만약 당신이 set of providers 를 모든 곳에 제공하기를 원한다면, (예를들어 helpers, database connections, etc.), @Global() 데코레이터를 이용하여 모듈을 global로 만들 수 있습니다.
import { Module, Global } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Global() @Module({ controllers: [CatsController], providers: [CatsService], exports: [CatsService], }) export class CatsModule {}
Plain Text
복사
@Global() 데코레이터는 모듈을 글로벌스코프에 존재하게 만듭니다.
글로벌 모듈은 일반적으로 루트나 코어 모듈에서, 오직 한번만 등록 되어야 합니다.
위의 예제에서 CatsService 프로바이더 는 어디에나 있으며, 이 서비스를 주입하고자 하는 모듈들은 CatsModule 을 그들의 imports array 에 임포트 할 필요가 없습니다.

HINT 모든 것을 글로벌로 만드는 것은 좋은 디자인이 아닙니다. 글로벌 모듈은 필요한 보일러플레이트의 양을 줄이는데 사용 할 수 있습니다. 일반적으로 imports array 를 통해 가져오는 것이 선호됩니다.

Dynamic modules

네스트의 모듈 시스템은 dynamic modules 라고 불리는 강력한 기능을 포함합니다.
이 기능ㅇ르 사용하면 프로바이더들을 동적으로 등록하고, 구성 할 수 있는 사용자 지정 가능한 모듈을 쉽게 만들 수 있습니다.
여기 서 다이나믹모듈 을 광범위하게 다룹니다.
이 쳅터에서는, 간단하게 브리핑 하겠습니다.
아래 예는 DatabaseModule 이라는 다이나믹 모듈의 정의 입니다. :
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, }; } }
Plain Text
복사

HINT : forRoot() 메소드는 동기화되거나, 비동기화 되는 다이나믹모듈을 리턴 할 수있습니다.

이 모듈은 @Module() 데코레이터 메타데이터 에서, Connection 프로바이더를 기본적으로 정의했다.
그러나 추가적으로 (forRoot() 메소드를 통해 전달된 entities 와 options objects 에 따라) 프로바이더의 컬렉션을 노출한다 (이 프로바이더의 컬렉션을 예를들어 repositories 라 하자)
동적모듈에 의해 반환된 프로퍼티들은 ovrride 되는 것이 아니라, @Module() 데코레이터에 정의된 기본 module 메타데이터에서 extend 된다.
이 방법으로 정적으로 선언된 *Connection 프로바이더와 동적으로 생성된 repository 프로바이더가 모듈에서 exported 된다.
만약 동적 모듈을 글로벌하게 등록하고 싶으면, 반환값에 global 프로퍼티를 true 로 설정하면 된다.
{ global: true, module: DatabaseModule, providers: providers, exports: providers, }
Plain Text
복사
DatabaseModule 은 아래와 같은 방법으로 임포트되고, 구성될 수 있다.
import { Module } from '@nestjs/common'; import { DatabaseModule } from './database/database.module'; import { User } from './users/entities/user.entity'; @Module({ imports: [DatabaseModule.forRoot([User])], }) export class AppModule {}
Plain Text
복사
만약 다이나믹 모듈을 re-export 하고싶다면, forRoot() 메소드콜을 생략하고 exports array 에 넣으면된다.
import { Module } from '@nestjs/common'; import { DatabaseModule } from './database/database.module'; import { User } from './users/entities/user.entity'; @Module({ imports: [DatabaseModule.forRoot([User])], exports: [DatabaseModule], }) export class AppModule {}
Plain Text
복사
Dynamic modules 쳅터에서 이 토픽을 더 크게 커버하고, working example 을 포함한다.