01 [NestJS] CLI를 이용한 프로젝트 설치 및 초기 구조 이해
NestJS는 Node.js 기반의 서버사이드 애플리케이션을 구축하기 위한 프레임워크다. TypeScript를 기본으로 하며, Angular의 아키텍처에서 영감을 받아 데코레이터와 모듈 시스템을 활용한다.
NestJS CLI 설치
NestJS 프로젝트를 생성하고 관리하기 위해서는 먼저 NestJS CLI를 전역으로 설치해야 한다.
npm install -g @nestjs/cli
설치가 완료되면 nest 명령어를 사용할 수 있다. 버전을 확인해보자.
nest --version
새 프로젝트 생성
NestJS CLI를 사용해서 새로운 프로젝트를 생성하는 방법은 매우 간단하다.
nest new project-name
이 명령어를 실행하면 몇 가지 옵션을 선택하라고 나온다:
- 패키지 매니저 선택: npm, yarn, pnpm 중에서 선택할 수 있다
- Git 저장소 초기화: 기본적으로 Git 저장소가 자동으로 초기화된다
프로젝트 생성이 완료되면 해당 디렉토리로 이동해서 개발 서버를 실행할 수 있다.
cd project-name
npm run start:dev
http://localhost:3000으로 접속하면 "Hello World!" 메시지가 출력되는 것을 확인할 수 있다.
초기 프로젝트 구조 분석
생성된 NestJS 프로젝트의 초기 구조를 살펴보면 다음과 같다:
src/
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts
main.ts - 애플리케이션 진입점
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
main.ts는 애플리케이션의 진입점이다. NestFactory를 사용해서 NestJS 애플리케이션 인스턴스를 생성하고, AppModule을 루트 모듈로 지정한다. 기본적으로 3000번 포트에서 서버가 실행된다.
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은 애플리케이션의 루트 모듈이다. @Module 데코레이터를 사용해서 모듈을 정의하며, 다음과 같은 속성들을 가진다:
- imports: 다른 모듈들을 가져올 때 사용
- controllers: 이 모듈에서 사용할 컨트롤러들
- providers: 이 모듈에서 사용할 서비스들 (의존성 주입 대상)
- exports: 다른 모듈에서 사용할 수 있도록 내보낼 프로바이더들
app.controller.ts - 컨트롤러
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();
}
}
컨트롤러는 들어오는 HTTP 요청을 처리하고 응답을 반환하는 역할을 한다. @Controller 데코레이터로 클래스를 컨트롤러로 정의하고, @Get, @Post 등의 HTTP 메소드 데코레이터를 사용해서 라우트를 정의한다.
여기서 중요한 점은 생성자에서 AppService를 주입받고 있다는 것이다. 이는 NestJS의 의존성 주입(Dependency Injection) 시스템을 보여주는 예시다.
app.service.ts - 서비스
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
서비스는 비즈니스 로직을 담당하는 클래스다. @Injectable 데코레이터를 사용해서 다른 클래스에 주입될 수 있도록 만든다. 컨트롤러에서는 HTTP 요청 처리에만 집중하고, 실제 비즈니스 로직은 서비스에서 처리하는 것이 좋은 아키텍처 패턴이다.
app.controller.spec.ts - 테스트 파일
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
NestJS는 테스트를 위한 강력한 도구들을 제공한다. @nestjs/testing 패키지를 사용해서 테스트 모듈을 생성하고, 의존성 주입을 통해 테스트 대상을 가져올 수 있다.
추가 파일들
프로젝트 루트에는 다음과 같은 설정 파일들도 있다:
- nest-cli.json: NestJS CLI 설정 파일
- tsconfig.json: TypeScript 컴파일러 설정
- package.json: 프로젝트 의존성 및 스크립트 정의
NestJS의 핵심 개념
초기 구조를 통해 NestJS의 핵심 개념들을 이해할 수 있다:
1. 모듈 시스템
NestJS는 모듈 기반 아키텍처를 사용한다. 각 모듈은 관련된 기능들을 그룹화하고, 애플리케이션을 체계적으로 구성할 수 있게 해준다.
2. 의존성 주입
컨트롤러에서 서비스를 주입받는 것처럼, NestJS는 강력한 의존성 주입 시스템을 제공한다. 이를 통해 코드의 결합도를 낮추고 테스트하기 쉬운 구조를 만들 수 있다.
3. 데코레이터 기반
@Module, @Controller, @Injectable, @Get 등 데코레이터를 통해 메타데이터를 정의하고, 프레임워크가 이를 해석해서 동작한다.
개발 서버 실행 모드
package.json을 보면 다양한 실행 모드가 정의되어 있다:
npm run start # 프로덕션 모드
npm run start:dev # 개발 모드 (파일 변경 시 자동 재시작)
npm run start:debug # 디버그 모드
개발할 때는 start:dev 명령어를 사용하는 것이 좋다. 파일이 변경될 때마다 자동으로 서버가 재시작되기 때문이다.
마무리
NestJS의 초기 구조는 단순해 보이지만, 확장 가능한 아키텍처의 기반을 제공한다. 모듈, 컨트롤러, 서비스의 분리를 통해 관심사의 분리(Separation of Concerns) 원칙을 잘 보여주고 있으며, 의존성 주입을 통해 테스트하기 쉽고 유지보수하기 좋은 코드를 작성할 수 있다.