출시·고도화 중
NestJS · Express 안내서 · 2/6
Express는 폴더 구조를 정해 두지 않지만, NestJS는 모듈 단위로 코드를 나누는 분명한 관례가 있습니다. 이 장에서는 nest new가 만든 프로젝트의 파일, 주요 설정, 기능별 모듈 구성, 그리고 Express 프로젝트를 정리하는 흔한 방법을 살펴봅니다.
nest new로 만든 프로젝트는 대략 다음과 같습니다(ESLint · Prettier 설정 파일은 생략).
hello-nest/
├── src/
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test/
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── nest-cli.json
├── package.json
├── tsconfig.build.json
└── tsconfig.json| 파일 | 역할 |
|---|---|
src/main.ts | 시작점. NestFactory로 애플리케이션을 만들고 전역 설정을 한 뒤 포트를 엽니다 |
src/app.module.ts | 루트 모듈. 다른 모듈을 모두 이 모듈에서 가져옵니다 |
src/app.controller.ts | 경로 하나를 처리하는 예제 컨트롤러 |
src/app.service.ts | 컨트롤러가 주입받아 쓰는 예제 서비스(프로바이더) |
src/*.spec.ts | 소스 옆에 두는 단위 테스트 |
test/ | 애플리케이션 전체를 띄워 HTTP로 검사하는 e2e 테스트 |
nest-cli.json은 Nest CLI의 동작을 정합니다. sourceRoot는 소스 폴더, deleteOutDir는 빌드 전에 dist를 비울지 여부입니다.
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}tsconfig.json에서 Nest에 꼭 필요한 옵션은 experimentalDecorators와 emitDecoratorMetadata입니다. 앞의 것은 @Controller() 같은 데코레이터 문법을 켜고, 뒤의 것은 생성자 매개변수의 타입 정보를 실행 시점에 남겨서 의존성 주입이 어떤 클래스를 넣어야 할지 알 수 있게 합니다. tsconfig.build.json은 빌드할 때 테스트 파일을 제외합니다.
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"outDir": "./dist",
"baseUrl": "./"
}
}위는 Nest와 관련된 옵션만 추린 예입니다. 실제 생성 파일에는 module, target, strictNullChecks 같은 옵션이 더 있으며, 기본값은 Nest 버전에 따라 조금씩 다릅니다.
애플리케이션이 커지면 기능마다 모듈을 하나씩 둡니다. nest g resource는 컨트롤러, 서비스, 모듈, DTO, 엔티티를 한 번에 만들고, REST API를 고르면 CRUD 엔드포인트의 뼈대까지 채워 줍니다.
nest g resource userssrc/users/
├── dto/
│ ├── create-user.dto.ts
│ └── update-user.dto.ts
├── entities/
│ └── user.entity.ts
├── users.controller.spec.ts
├── users.controller.ts
├── users.module.ts
├── users.service.spec.ts
└── users.service.ts파일 이름은 <이름>.<종류>.ts(케밥 표기)로 짓고, 클래스 이름은 UsersController, UsersService처럼 파스칼 표기로 짓는 것이 관례입니다. 모듈은 자신이 가진 컨트롤러와 프로바이더를 선언하고, 다른 모듈이 써야 하는 프로바이더만 exports로 내보냅니다.
// src/users/users.module.ts
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}CLI가 루트 모듈의 imports에 UsersModule을 자동으로 추가합니다. 여러 모듈이 함께 쓰는 가드, 인터셉터, 유틸리티는 src/common/ 같은 폴더에 모아 두는 경우가 많습니다.
Express는 구조를 강제하지 않으므로 팀이 직접 정합니다. 흔히 쓰는 방식은 라우터, 미들웨어, 비즈니스 로직을 폴더로 나누고, 애플리케이션 생성과 포트 열기를 서로 다른 파일에 두는 것입니다.
my-express-api/
├── src/
│ ├── routes/
│ │ └── users.js
│ ├── middleware/
│ │ └── error-handler.js
│ ├── services/
│ │ └── users-service.js
│ ├── app.js
│ └── server.js
└── package.jsonapp.js는 앱을 만들어 내보내기만 하고, server.js가 포트를 엽니다. 이렇게 나누면 테스트에서 포트를 열지 않고 app만 가져다 쓸 수 있습니다.
// src/app.js
const express = require("express");
const usersRouter = require("./routes/users");
const app = express();
app.use(express.json());
app.use("/users", usersRouter);
module.exports = app;// src/server.js
const app = require("./app");
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on ${port}`));src/main.ts(시작점)와 src/app.module.ts(루트 모듈)를 중심으로 구성됩니다.experimentalDecorators와 emitDecoratorMetadata는 데코레이터와 의존성 주입에 꼭 필요합니다.nest g resource로 컨트롤러 · 서비스 · DTO를 한 번에 만들 수 있습니다.app과 listen을 분리해 두면 테스트하기 쉽습니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.