Framework/NestJS

NestJS 완전정복: CLI 활용하기

Joonfluence 2025. 5. 26. 09:54

NestJS CLI는 NestJS 애플리케이션을 개발할 때 필수적인 도구입니다. 이 가이드에서는 NestJS CLI의 주요 기능과 사용법을 자세히 알아보겠습니다.

1. NestJS CLI 설치

먼저 NestJS CLI를 전역으로 설치하는 방법을 알아보겠습니다:

npm install -g @nestjs/cli

설치가 완료되면 다음 명령어로 버전을 확인할 수 있습니다:

nest --version

2. 새 프로젝트 생성

새로운 NestJS 프로젝트를 생성하는 방법입니다:

nest new my-nest-project

이 명령어를 실행하면 다음과 같은 구조의 프로젝트가 생성됩니다:

my-nest-project/
├── src/
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test/
├── package.json
├── tsconfig.json
└── nest-cli.json

3. 주요 CLI 명령어

3.1 모듈 생성

nest generate module users
# 또는 간단히
nest g mo users

생성된 모듈 파일의 예시:

// users/users.module.ts
import { Module } from '@nestjs/common';

@Module({
  imports: [],
  controllers: [],
  providers: [],
  exports: []
})
export class UsersModule {}

3.2 컨트롤러 생성

nest generate controller users
# 또는 간단히
nest g co users

생성된 컨트롤러 파일의 예시:

// users/users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(id);
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }
}

3.3 서비스 생성

nest generate service users
# 또는 간단히
nest g s users

생성된 서비스 파일의 예시:

// users/users.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  private users = [];

  findAll() {
    return this.users;
  }

  findOne(id: string) {
    return this.users.find(user => user.id === id);
  }

  create(createUserDto: CreateUserDto) {
    const user = {
      id: Date.now().toString(),
      ...createUserDto,
    };
    this.users.push(user);
    return user;
  }
}

3.4 미들웨어 생성

nest generate middleware common/logger
# 또는 간단히
nest g mi common/logger

생성된 미들웨어 파일의 예시:

// common/logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`Request... ${req.method} ${req.url}`);
    next();
  }
}

3.5 파이프 생성

nest generate pipe common/validation
# 또는 간단히
nest g pi common/validation

생성된 파이프 파일의 예시:

// common/validation.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    // 여기에 유효성 검사 로직 구현
    return value;
  }
}

3.6 가드 생성

nest generate guard common/auth
# 또는 간단히
nest g gu common/auth

생성된 가드 파일의 예시:

// common/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    // 인증 로직 구현
    return true;
  }
}

4. 프로젝트 빌드 및 실행

4.1 개발 모드 실행

nest start --watch

4.2 프로덕션 빌드

nest build

4.3 프로덕션 모드 실행

node dist/main

5. 테스트 관련 명령어

5.1 단위 테스트 실행

npm run test

5.2 E2E 테스트 실행

npm run test:e2e

5.3 테스트 커버리지 확인

npm run test:cov

6. 모범 사례

6.1 프로젝트 구조

src/
├── common/           # 공통 모듈, 미들웨어, 가드 등
├── config/           # 설정 파일
├── modules/          # 기능별 모듈
│   ├── users/
│   │   ├── dto/
│   │   ├── entities/
│   │   ├── users.controller.ts
│   │   ├── users.module.ts
│   │   └── users.service.ts
│   └── auth/
├── main.ts
└── app.module.ts

6.2 환경 설정

// config/configuration.ts
export default () => ({
  port: parseInt(process.env.PORT, 10) || 3000,
  database: {
    host: process.env.DATABASE_HOST,
    port: parseInt(process.env.DATABASE_PORT, 10) || 5432
  }
});

6.3 모듈 구성 예시

// modules/users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService]
})
export class UsersModule {}

7. 디버깅 및 로깅

7.1 디버깅 설정

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug NestJS",
      "runtimeExecutable": "npm",
      "runtimeArgs": ["run", "start:debug"],
      "sourceMaps": true,
      "envFile": "${workspaceFolder}/.env"
    }
  ]
}

7.2 로깅 설정

// main.ts
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
  const logger = new Logger('Bootstrap');
  const app = await NestFactory.create(AppModule);

  const port = process.env.PORT || 3000;
  await app.listen(port);
  logger.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();

8. 커스텀 CLI 명령어 구현

NestJS에서는 nest-commander 패키지를 사용하여 커스텀 CLI 명령어를 구현할 수 있습니다.

8.1 nest-commander 설치

npm install nest-commander

8.2 CLI 모듈 및 명령어 구현

// src/cli/cli.module.ts
import { Module } from '@nestjs/common';
import { MyCommand } from './my.command';

@Module({
  providers: [MyCommand],
})
export class CliModule {}
// src/cli/my.command.ts
import { Command, CommandRunner } from 'nest-commander';
import { Injectable } from '@nestjs/common';

@Injectable()
@Command({ name: 'my-command', description: '커스텀 명령어 실행' })
export class MyCommand extends CommandRunner {
  async run(): Promise<void> {
    console.log('커스텀 명령어가 실행되었습니다!');
  }
}

8.3 CLI 진입점 구현

// src/cli.ts
import { CommandFactory } from 'nest-commander';
import { CliModule } from './cli/cli.module';

async function bootstrap() {
  await CommandFactory.run(CliModule, {
    errorHandler: (err) => {
      // 에러 발생 시 처리
      console.error('Command execution error:', err);
      process.exit(1);
    },
    logger: ['log', 'error', 'warn', 'debug', 'verbose'],
  });
}

bootstrap();

8.4 커스텀 CLI 실행

package.json에 스크립트를 추가하여 실행할 수 있습니다:

{
  "scripts": {
    "cli": "IS_LOCAL=true NODE_ENV=production ts-node -r tsconfig-paths/register --project tsconfig.json src/cli.ts"
  }
}

CLI 스크립트 구성 요소 설명

  1. 환경 변수 설정

    • IS_LOCAL=true: 로컬 환경에서 실행됨을 나타내는 플래그
    • NODE_ENV=production: Node.js 실행 환경을 프로덕션으로 설정
  2. ts-node 실행 옵션

    • ts-node: TypeScript 파일을 직접 실행할 수 있게 해주는 도구
    • -r tsconfig-paths/register: TypeScript의 path alias를 지원하기 위한 모듈
    • --project tsconfig.json: 사용할 TypeScript 설정 파일 지정
  3. 실행 파일

    • src/cli.ts: CLI 애플리케이션의 진입점 파일

실행 방법

npm run cli my-command

8.5 커스텀 CLI 명령어 활용 예시

8.5.1 데이터베이스 마이그레이션 명령어

// src/cli/migration.command.ts
import { Command, CommandRunner, Option } from 'nest-commander';
import { Injectable } from '@nestjs/common';

@Injectable()
@Command({ name: 'migrate', description: '데이터베이스 마이그레이션 실행' })
export class MigrationCommand extends CommandRunner {
  async run(
    passedParams: string[],
    options?: Record<string, any>,
  ): Promise<void> {
    const { direction = 'up' } = options;
    console.log(`마이그레이션 ${direction} 실행 중...`);
    // 마이그레이션 로직 구현
  }

  @Option({
    flags: '-d, --direction [direction]',
    description: '마이그레이션 방향 (up/down)',
  })
  parseDirection(val: string): string {
    return val;
  }
}

8.5.2 데이터 시드 명령어

// src/cli/seed.command.ts
import { Command, CommandRunner, Option } from 'nest-commander';
import { Injectable } from '@nestjs/common';

@Injectable()
@Command({ name: 'seed', description: '데이터베이스 시드 데이터 생성' })
export class SeedCommand extends CommandRunner {
  async run(
    passedParams: string[],
    options?: Record<string, any>,
  ): Promise<void> {
    const { type = 'all' } = options;
    console.log(`${type} 시드 데이터 생성 중...`);
    // 시드 데이터 생성 로직 구현
  }

  @Option({
    flags: '-t, --type [type]',
    description: '시드 데이터 타입 (all/users/products)',
  })
  parseType(val: string): string {
    return val;
  }
}

8.6 환경 변수 설정

.env 파일을 사용하여 CLI 실행 환경을 설정할 수 있습니다:

# .env
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
REDIS_URL=redis://localhost:6379

8.7 커스텀 CLI 활용 시 주의사항

  1. 에러 처리: 모든 명령어에서 적절한 에러 처리를 구현해야 합니다.
  2. 로깅: 중요한 작업의 진행 상황을 로깅하여 디버깅을 용이하게 합니다.
  3. 환경 설정: 개발/스테이징/프로덕션 환경에 따른 설정을 분리합니다.
  4. 테스트: CLI 명령어에 대한 단위 테스트를 작성하여 안정성을 확보합니다.
// src/cli/my.command.spec.ts
import { Test } from '@nestjs/testing';
import { MyCommand } from './my.command';

describe('MyCommand', () => {
  let command: MyCommand;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [MyCommand],
    }).compile();

    command = module.get(MyCommand);
  });

  it('should be defined', () => {
    expect(command).toBeDefined();
  });

  it('should execute command successfully', async () => {
    const consoleSpy = jest.spyOn(console, 'log');
    await command.run();
    expect(consoleSpy).toHaveBeenCalledWith('커스텀 명령어가 실행되었습니다!');
  });
});

이러한 커스텀 CLI 명령어를 활용하면 데이터베이스 마이그레이션, 데이터 시드, 배치 작업 등 다양한 작업을 자동화할 수 있습니다. 특히 대규모 프로젝트에서는 이러한 자동화 도구가 개발 생산성을 크게 향상시킬 수 있습니다.

9. 결론

NestJS CLI는 개발 생산성을 크게 향상시키는 강력한 도구입니다. 이 가이드에서 다룬 명령어들과 모범 사례를 활용하면 효율적인 NestJS 애플리케이션 개발이 가능합니다. 특히 모듈, 컨트롤러, 서비스 등의 생성과 프로젝트 구조화에 있어 CLI의 역할은 매우 중요합니다.

실제 프로젝트에서는 이러한 CLI 기능들을 적극 활용하면서, 프로젝트의 규모와 요구사항에 맞게 구조를 조정하고 확장해 나가는 것이 좋습니다. 또한, 테스트와 디버깅 도구를 적극 활용하여 안정적인 애플리케이션을 개발하는 것이 중요합니다.

반응형