본문 바로가기

NestJS 시작하기: 실전 입문 가이드

민이(MInE) 2024. 11. 9.
반응형

NestJS 시작하기: 실전 입문 가이드

목차

  1. NestJS란?
  2. 개발 환경 설정
  3. 프로젝트 구조
  4. 핵심 개념 이해하기
  5. 실전 예제: REST API 만들기
  6. 테스트 작성하기
  7. 실무 적용 팁

NestJS란?

NestJS는 Node.js의 효율적인 서버 애플리케이션 구축을 위한 프레임워크입니다. Angular에서 영감을 받아 TypeScript를 기본으로 사용하며, OOP(객체지향 프로그래밍), FP(함수형 프로그래밍), FRP(함수형 반응형 프로그래밍)의 요소들을 포함하고 있습니다.

주요 특징

  • TypeScript 기반의 타입 안정성
  • 의존성 주입을 통한 느슨한 결합
  • 모듈화된 아키텍처
  • Express/Fastify 호환성
  • 풍부한 CLI 도구 지원

개발 환경 설정

필수 도구 설치

# Node.js 설치 (14.x 이상)
$ node --version
v16.x.x

# NestJS CLI 설치
$ npm i -g @nestjs/cli

# 프로젝트 생성
$ nest new my-nest-project

프로젝트 구조 설정

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true
  }
}

프로젝트 구조

src/
├── app.module.ts          # 루트 모듈
├── main.ts               # 애플리케이션 시작점
├── users/                # 사용자 관련 모듈
│   ├── dto/
│   │   ├── create-user.dto.ts
│   │   └── update-user.dto.ts
│   ├── entities/
│   │   └── user.entity.ts
│   ├── users.controller.ts
│   ├── users.service.ts
│   └── users.module.ts
└── common/              # 공통 기능
    ├── filters/
    ├── guards/
    ├── interceptors/
    └── pipes/

핵심 개념 이해하기

1. 모듈 (Modules)

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

2. 컨트롤러 (Controllers)

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

  @Get()
  async findAll(): Promise<User[]> {
    return this.usersService.findAll();
  }

  @Post()
  async create(@Body() createUserDto: CreateUserDto): Promise<User> {
    return this.usersService.create(createUserDto);
  }
}

3. 서비스 (Services)

// users.service.ts
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  async findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.usersRepository.create(createUserDto);
    return this.usersRepository.save(user);
  }
}

실전 예제: REST API 만들기

DTO (Data Transfer Object) 정의

// create-user.dto.ts
export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsEmail()
  readonly email: string;

  @IsString()
  @MinLength(6)
  readonly password: string;
}

엔티티 정의

// user.entity.ts
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column({ unique: true })
  email: string;

  @Column()
  password: string;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}

데이터베이스 연결

// app.module.ts
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'postgres',
      database: 'nestjs',
      entities: [User],
      synchronize: true,
    }),
    UsersModule,
  ],
})
export class AppModule {}

테스트 작성하기

단위 테스트

// users.service.spec.ts
describe('UsersService', () => {
  let service: UsersService;
  let repository: Repository<User>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UsersService,
        {
          provide: getRepositoryToken(User),
          useValue: {
            find: jest.fn(),
            create: jest.fn(),
            save: jest.fn(),
          },
        },
      ],
    }).compile();

    service = module.get<UsersService>(UsersService);
    repository = module.get<Repository<User>>(getRepositoryToken(User));
  });

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

  describe('findAll', () => {
    it('should return an array of users', async () => {
      const users = [{ id: 1, name: 'Test User' }];
      jest.spyOn(repository, 'find').mockResolvedValue(users);

      expect(await service.findAll()).toBe(users);
    });
  });
});

실무 적용 팁

1. 환경 변수 관리

// 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,
  },
});

2. 예외 처리

// filters/http-exception.filter.ts
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        path: request.url,
        message: exception.message,
      });
  }
}

3. 미들웨어 사용

// middleware/logger.middleware.ts
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    console.log(`Request...`);
    next();
  }
}

4. API 문서화

// main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('API 문서')
    .setDescription('NestJS API 설명')
    .setVersion('1.0')
    .addTag('users')
    .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}

마치며

NestJS는 엔터프라이즈급 애플리케이션 개발을 위한 강력한 프레임워크입니다. TypeScript의 타입 안정성과 모듈화된 아키텍처를 통해 확장 가능하고 유지보수하기 쉬운 애플리케이션을 구축할 수 있습니다.

추천 학습 자료

  • NestJS 공식 문서
  • TypeORM 문서
  • TypeScript 핸드북
  • Jest 테스팅 가이드
반응형

댓글