첫 번째 NestJS 애플리케이션 만들기
축하드립니다! 이제 여러분의 컴퓨터에는 NestJS 개발을 위한 모든 준비가 끝났습니다. 앞서 생성하고 실행해 본 "Hello World!" 애플리케이션은 NestJS의 가장 기본적인 형태를 보여주었는데요. 이번 절에서는 이 애플리케이션의 기본 구조를 자세히 살펴보고, 어떻게 "Hello World!" 메시지가 화면에 나타났는지, 즉 NestJS가 어떻게 요청을 처리하는지 그 과정을 함께 파헤쳐 보겠습니다.
프로젝트 기본 구조 이해하기
NestJS CLI를 통해 nest new
명령어로 프로젝트를 생성하면 다음과 같은 디렉토리 및 파일 구조를 갖게 됩니다. 모든 파일을 다 살펴보기보다는 지금 당장 우리가 이해해야 할 핵심적인 파일들을 중심으로 살펴보겠습니다.
-
src/
디렉토리: NestJS 애플리케이션의 모든 소스 코드가 위치하는 핵심 디렉토리입니다. 우리가 작성할 대부분의 코드는 이 안에 들어있습니다.main.ts
: 애플리케이션의 진입점(Entry Point) 파일입니다. Node.js 애플리케이션이 실행될 때 가장 먼저 이 파일이 호출됩니다. 마치 건물의 출입구와 같다고 생각하시면 됩니다. 여기서 NestJS 애플리케이션 인스턴스를 생성하고 시작합니다.app.module.ts
: 애플리케이션의 루트 모듈(Root Module) 입니다. NestJS 애플리케이션의 최상위 모듈이며, 다른 모든 모듈의 기반이 됩니다. 앞서 1.1절에서 설명했듯이, 모듈은 컨트롤러와 프로바이더를 묶어주는 역할을 합니다.app.controller.ts
:AppController
를 정의하는 파일입니다./
경로로 들어오는 HTTP GET 요청을 처리하고 "Hello World!" 문자열을 반환하는 로직을 담고 있습니다. 클라이언트의 요청을 받아들이는 역할을 합니다.app.service.ts
:AppService
를 정의하는 파일입니다. 컨트롤러에서 호출하여 실제 비즈니스 로직(여기서는 "Hello World!" 문자열을 반환하는 간단한 로직)을 처리하는 프로바이더의 한 종류입니다.
-
package.json
: 프로젝트의 메타데이터와 의존성 패키지 목록, 실행 스크립트 등을 정의하는 파일입니다.npm install
명령어를 실행하면 여기에 명시된 패키지들이 설치됩니다. -
tsconfig.json
: TypeScript 컴파일러 옵션을 설정하는 파일입니다. NestJS는 TypeScript 기반이므로, 이 파일이 중요하게 사용됩니다.
NestJS 애플리케이션의 실행 흐름 추적하기
이제 http://localhost:3000
으로 접속했을 때 "Hello World!"가 어떻게 출력되는지, NestJS 내부의 동작 흐름을 따라가 보겠습니다.
main.ts
실행:
애플리케이션을 시작하면 npm run start:dev
명령어가 main.ts
파일을 실행합니다. 이 파일의 핵심 코드는 다음과 같습니다.
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
// NestJS 애플리케이션 인스턴스를 생성합니다.
// 여기서 AppModule은 애플리케이션의 루트 모듈입니다.
const app = await NestFactory.create(AppModule);
// 애플리케이션이 3000번 포트에서 수신 대기하도록 합니다.
await app.listen(3000);
}
bootstrap(); // bootstrap 함수를 호출하여 애플리케이션을 시작합니다.
NestFactory.create(AppModule)
를 통해 AppModule
을 기반으로 NestJS 애플리케이션이 초기화되고, app.listen(3000)
을 통해 3000번 포트에서 들어오는 HTTP 요청을 수신 대기하게 됩니다.
AppModule
초기화:
main.ts
에서 AppModule
을 전달했으므로, NestJS는 app.module.ts
파일을 읽고 초기화합니다.
// src/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 {}
@Module()
데코레이터 안의 controllers
배열에 AppController
가 등록되어 있고, providers
배열에 AppService
가 등록되어 있는 것을 볼 수 있습니다. NestJS는 이 정보를 바탕으로 AppController
와 AppService
를 인스턴스화하고, 의존성 주입을 준비합니다.
요청 처리: AppController
동작:
이제 http://localhost:3000
으로 웹 브라우저가 요청을 보냅니다. 이 요청은 NestJS 애플리케이션에 도달하고, 등록된 컨트롤러 중 해당 요청을 처리할 컨트롤러를 찾습니다.
// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller() // 이 클래스가 컨트롤러임을 나타냅니다. 기본 경로는 '/' 입니다.
export class AppController {
// AppService를 주입받습니다. NestJS가 자동으로 AppService 인스턴스를 제공합니다.
constructor(private readonly appService: AppService) {}
@Get() // HTTP GET 요청, 경로가 '/'일 때 이 메서드가 실행됩니다.
getHello(): string {
// AppService의 getHello() 메서드를 호출하여 결과값을 반환합니다.
return this.appService.getHello();
}
}
@Controller()
데코레이터는 이 클래스가 컨트롤러임을 선언하며, 기본 경로가 /
임을 의미합니다. @Get()
데코레이터는 이 메서드가 HTTP GET 요청을 처리하며, 경로가 컨트롤러의 기본 경로와 같을 때(즉, /
) 호출됨을 나타냅니다.
AppController
의 생성자를 보시면 private readonly appService: AppService
라는 코드가 있습니다. 이것이 바로 NestJS의 핵심 기능 중 하나인 의존성 주입(Dependency Injection) 입니다. NestJS는 자동으로 AppService
의 인스턴스를 생성하여 AppController
에 주입해주므로, 우리는 직접 new AppService()
와 같이 인스턴스를 생성할 필요가 없습니다.
getHello()
메서드는 주입받은 appService
의 getHello()
메서드를 호출합니다.
비즈니스 로직 처리: AppService
동작:
AppController
의 요청을 받은 AppService
가 실행됩니다.
// src/app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable() // 이 클래스가 프로바이더임을 나타냅니다. (다른 곳에 주입될 수 있음)
export class AppService {
getHello(): string {
return 'Hello World!'; // 간단하게 "Hello World!" 문자열을 반환합니다.
}
}
@Injectable()
데코레이터는 이 클래스가 NestJS의 의존성 주입 시스템에 의해 관리되는 프로바이더임을 나타냅니다. getHello()
메서드는 단순히 'Hello World!'
문자열을 반환합니다.
응답 반환:
AppService.getHello()
가 반환한 'Hello World!'
문자열은 AppController.getHello()
로 돌아오고, 최종적으로 클라이언트(웹 브라우저)에게 HTTP 응답으로 전달되어 화면에 표시됩니다.
이러한 과정을 통해 NestJS가 어떻게 모듈, 컨트롤러, 프로바이더를 활용하여 클라이언트의 요청을 받아들이고 처리한 후 응답을 반환하는지 이해하셨기를 바랍니다. 이는 NestJS 애플리케이션의 가장 기본적인 동작 방식이며, 앞으로 우리가 만들 모든 복잡한 기능들도 결국 이 기본 틀 안에서 동작하게 됩니다.
다음 절에서는 NestJS의 핵심 개념인 의존성 주입(Dependency Injection) 에 대해 좀 더 자세히 알아보겠습니다.