1. Nest.js 코드를 통한 개념
1) 진입점 파일
main.ts : Nest.js에서 진입점으로 사용하겠다고 사전에 약속된 파일로, 임의로 파일이름을 변경해서는 안 된다.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
위 코드는 기본적인 Nest.js의 main.ts 파일인데, NestFactory 클래스의 create라는 정적 함수를 통해 Nest.js 어플리케이션 인스턴스를 새롭게 생성한다. 괄호 안의 AppModule이라는 부분은 "AppModule이라는 이름의 모듈을 루트모듈로 사용하는 Nest.js 어플리케이션 인스턴스를 생성해줘." 라는 의미이다.
2) 모듈
Nest.js에서 모듈은 애플리케이션의 특정부분을 캡슐화하며, 관련된 컴포넌트, 서비스, 그리고 다른 모듈들을 그룹화한다. 이를 통해 레이어드 아키텍처 패턴과 같은 구조적인 아키텍처 패턴을 효과적으로 구현할 수 있다.
- app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
위에서 언급한 AppModule은 app.module.ts에서 import 해온 것인데, Nest.js는 이런 모듈 기반으로 구성되어 있다. 모듈을 생성하는 명령어를 입력하면 모듈이름이라는 디렉토리가 src 디렉토리 아래에 생성되며 그 디렉토리 안에 모듈이름.module.ts이란 파일이 생성된다.
- 데코레이터
위 코드에서 @Module({...})이란 문법을 볼 수 있는데 이것을 데코레이터라고 한다. 해당 클래스나 함수가 어떤 역할을 수행하는지에 대해 Nest.js에 알려주는 역할을 한다. 데코레이터는 각각 필요한 속성이 다른데, @Module은 imports, controller, providers, exports라는 속성을 가질 수 있다.
- imports : 현재 모듈에서 사용하려는 다른 모듈들의 목록을 정의하고, 주로 필요한 프로바이더(서비스)를 제공한다. 예를 들어 API 호출을 위해 자주 사용되는 HttpModule이나 db 작업을 위한 TypeOrmModule 등이 여기 포함된다.
- controllers : 현재 모듈과 관련된 컨트롤러의 목록을 정의한다. 이 컨트롤러들은 해당 모듈의 요청 처리 로직을 담당
- providers : 현재 모듈에서 사용하거나 제공하는 서비스, 리포지토리, 팩토리 등을 정의하며, 주로 비즈니스 로직이나 데이터 엑세스 같은 작업을 수행한다.
-exports : 현재 모듈에서 다른 모듈로 제공하려는 서비스 목록을 정읳나다. 여기 명시되면 현재 모듈 외부에서도 사용할 수 있게 된다.
3. 컨트롤러
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
위 코드는 컨트롤러의 예시코드인데, 모듈과 마찬가지로 @Controller 라는 데코레이터를 통해 AppController 클래스가 컨트롤러 역할을 하는 것을 Nest.js에 알리고 있다.
- 의존성 주입
constructor(private readonly appService: AppService) {}
위의 생성자에서 인자로 AppService 객체를 넘기면 this.appService라는 멤버 변수에 AppService 객체가 주입된다. 이를 DI(의존성 주입)이라고 하며 생성자를 통한 Di를 Nest.js에서 지원하는 것. 물론 컨트롤러는 서비스를 반드시 의존해야 하며, 이는 생성자를 통한 DI로 해결되는 것이다.
- 컨트롤러의 테코레이터 : @Get 라는 부분은 HTTP GET으로 요청이 들어올 시 아래의 함수를 실행하라는 뜻이다. 마찬가지로 @Post, @Put, @Patch, @Delete 데코레이터도 있어서 각 참수는 어떤 요청을 담당하는지 쉽게 알수 있다.
4. 서비스
import { Injectable } from '@nestjs/common';
@Injectable() // <- 난 Inject(주입)될 수 있어! 라고 선언하는 것이에요.
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
위는 서비스 코드의 예시이다. @Injectable 데코레이터는 'AppService가 나를 필요로 하면 언제든지 나를 주입시켜서 사용해.' 라는 의미이다. AppController에서 생성자로 AppService를 주입할 수 있는 이유가 바로 이것 덕분.
- 서비스의 역할은 컨트롤러에세 서비스를 제공하는 것이다.AppService같은 서비스 객체는 실제로 리포지토리를 의존하며 비즈니스 로직 실행을 담당한다. 마찬가지로 서비스는 리포지토리를 반드시 의존해야하며 이는 생성자를 통한 DI로 해결한다.
5. IoC와 DI
1) IoC(제어역전)
Inversion of Control의 준말로, 개발자가 사용하고 싶은 객체를 직접 생성하는 것이다. 객체의 생명주미 관리 자체를 외부(여기서는 Nest.js 컨테이너)에 위임한다. 즉, 객체의 관리를 컨테이너에 맡겨서 제어권이 넘어간 것. 전통적인 방법으로 객체를 직접 선언하면 AppController는 AppService의 구체적인 구현에 강하게 결합되어서 의존하는 서비스가 변경되면 개발자도 그에 맞춰 코드를 수정해야한다.
IoC 원칙을 사용하면 모듈 간 결합도를 낮춰 다른 모듈에 끼치는 영향을 최소화하고, AppController는 AppService의 구체적인 구현보다는 인터페이스나 추상 클래스에 의존하게 된다. 서비스 자체가 변경되어도 관계없이 사용될 수 있는 것이다.
constructor(private readonly appService: AppService) {} // IoC 방식
this.appService = new AppService(); // 전통적인 방식
'Sparta > TIL' 카테고리의 다른 글
24.03.12 TIL - TypeOrm (0) | 2024.03.13 |
---|---|
24.03.11 TIL - nest.js 패키지 (0) | 2024.03.12 |
24.03.07 TIL - Typescript(3) (0) | 2024.03.08 |
24.03.06 TIL - Typescript(2) (0) | 2024.03.07 |
24.03.05 TIL - Typescript (0) | 2024.03.06 |