NestJS에 있는 resource 기능을 이용하여 간단한 CRUD 기능을 하는 REST API를 만들어 보겠습니다.
1. resource 기능
기본적으로 NestJS에서 API기능을 만들기 위해서는 module, controller, service 등 여러 작업을 반복해서 만들어야 합니다.
Nest에서는 이러한 반복적인 프로세스의 속도를 높이기 위해 resource라는 명령어를 만들었습니다.
resource 명령어는 모듈, 서비스, 컨트롤러뿐만 아니라 Entity, DTO 클래스 및 Test 파일도 생성합니다.
// 폴더 이름 users에 resource 생성
> nest g resource users
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
2. TypeOrm & Mysql
Nest에서 Mysql을 사용하기 위해 TypeOrm 라이브러리와 같이 다운로드하여 줍니다.
npm install --save @nestjs/typeorm typeorm mysql2
만약에 설치가 안된다면 npm install --force를 입력해 설치하면 됩니다.
여기서는 mysql이 아닌 mysql2를 다운받습니다.
mysql과 mysql2의 큰 차이점은 Promise의 지원 여부입니다. mysql은 CallBack 기반으로 동작하고, mysql2는 Promise를 지원하며 CallBack과 Promise를 모두 활용할 수 있습니다.
https://hollo-coding.tistory.com/13
mysql과 mysql2 차이
Mysql과 Mysql2의 가장 큰 차이점은 Promise 지원 여부입니다.Mysql은 Callback 기반으로 동작하는 반면에, Mysql2는 Promise를 지원하며 CallBack과 같이 활용할 수 있습니다.Mysql에서 Promise를 사용하기 위해서
hollo-coding.tistory.com
3. Entity
// users/entities/user.entity.ts
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('User')
export class User {
@PrimaryGeneratedColumn()
userId: number;
@Column({ type: 'varchar', length: 20 })
name: string;
@Column({ type: 'varchar', length: 30 })
password: string;
@CreateDateColumn()
createdAt: Date;
}
저는 Entity를 다음과 같이 설정했습니다.
@Entity('User')
- 데이터베이스 테이블을 정의하기 전 실행하는 데코레이터, 이름을 지정하지 않으면 엔티티 클래스명으로 생성됩니다.
@PrimaryGeneratedColumn()
- 값이 자동으로 생성되는 primary 컬럼 생성 ( PK 생성 )
@CreateDateColumn()
- 엔티티의 삽입 날짜를 자동 설정해 줍니다.
4. TypeOrm으로 mysql 연결
4-1. appmodule에 적용
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users/entities/user.entity';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '1234',
database: 'testdb',
entities: [User],
synchronize: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
- TypeOrmModule.forRoot({ ... }) - TypeOrm의 설정을 초기화합니다. forRoot()메서드는 필요한 설정 객체를 반환합니다.
- type - 사용할 데이터베이스 종류 설정
- host - 데이터베이스 서버의 호스트 이름
- entities - 사용할 엔티티 클래스를 지정합니다.
- synchronize - Entity와 데이터베이스 테이블을 자동으로 동기화에 대한 설정, 개발 단계에서는 true를 써도 되지만 배포단계에서는 false로 바꿔주어야 합니다.
4-2. env파일과 Async Configuration 설정
정보를 보호하기 위한 env 파일과 Appmodule에서 TypeOrm옵션을 분리해 설정해 보겠습니다.
1) config 설치
> npm i --save @nestjs/config
@nestjs/config는 내부적으로 dotenv를 사용합니다. ( config는 Typescript 4.1 이상이 필요합니다. )
2) env 파일 생성
@nestjs/config는 루트 디렉토리 폴더에서 env 파일을 찾기 때문에, 가장 상위 폴더에 env파일을 생성해 줍니다.
// env
DB_HOST = 'localhost'
DB_PORT = '3306'
DB_USERNAME = 'root'
DB_PASSWORD = '1234'
DATABASE_NAME = 'testdb'
2) TypeOrm 분리
기존에 했던 것처럼 Appmodule에 넣을 수 도 있지만, Appmodule의 코드를 최소화하기 위해 분리해 작성하겠습니다.
// src/config/typeorm.config.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
@Injectable()
export class TypeOrmConfig implements TypeOrmOptionsFactory {
constructor(private configService: ConfigService) {}
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: this.configService.get('DB_HOST'),
port: this.configService.get<number>('DB_PORT'),
username: this.configService.get('DB_USERNAME'),
password: this.configService.get('DB_PASSWORD'),
database: this.configService.get('DATABASE_NAME'),
entities: [__dirname + '/../users/entities/*.ts'],
synchronize: true,
autoLoadEntities: true,
};
}
}
@Injectable()
- 의존성 주입을 통해 ConfigService를 사용합니다.
constructor(private configService: ConfigService) {}
- 내부에서 configservice를 사용할 수 있도록 생성자(constructor)를 통해 의존성 주입합니다.
createTypeOrmOptions()
- TypeOrmOptionsFactory 인터페이스의 메서드로 typeorm에 필요한 설정에 대한 객체(TypeOrmModuleOptions)를 반환
autoLoadEntities: true
- Entity를 자동으로 읽어오도록 설정합니다.
3) app.module.ts 설정
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { UsersModule } from './users/users.module';
import { TypeOrmConfig } from './config/typeorm.config';
@Module({
imports: [
TypeOrmModule.forRootAsync({ useClass: TypeOrmConfig }),
ConfigModule.forRoot({ isGlobal: true }),
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
TypeOrmModule.forRootAsync({ ... })
- 정적이 아닌 비동기적 모듈 옵션을 전달하고자 할 때 forRootAsync() 메서드를 사용해서 옵션을 전달합니다.
ConfigModule.forRoot({ isGlobal : true })
- ConfigModule을 다른 곳에서도 사용할 때 다시 불러와야 되는 번거로움을 없애기 위해 isGlobal을 true로 설정합니다.
5. DTO 설정
// create-user.dto.ts
export class CreateUserDto {
name: string;
password: string;
}
회원가입 시 필요한 정보가 들어간 DTO를 만들어줍니다.
6. User Service 설정
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Repository } from 'typeorm';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const newUser = this.userRepository.create(createUserDto);
await this.userRepository.save(newUser);
return newUser;
}
async findAll(): Promise<User[]> {
return await this.userRepository.find();
}
async findOne(userId: number): Promise<User> {
const user = await this.userRepository.findOne({ where: { userId } });
if (!user) throw new NotFoundException(`User with Id ${userId} not found`);
return user;
}
async update(userId: number, updateUserDto: UpdateUserDto): Promise<User> {
const user = await this.findOne(userId);
const updateUser = { ...user, ...updateUserDto };
await this.userRepository.save(updateUser);
return updateUser;
}
async remove(userId: number): Promise<void> {
const user = await this.findOne(userId);
await this.userRepository.remove(user);
}
}
기존의 있던 User Service를 Mysql과 TypeOrm을 사용한 CRUD기능을 수행하기 위한 코드로 수정해 주었습니다.
모든 메서드를 비동기적(asynchronous)으로 처리하고, 결과를 Promise로 반환합니다.
@InjectRepository()
- typeorm에서 제공하는 메서드입니다. User 엔티티에 대한 Repository를 주입합니다.
7. User Module
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
TypeOrmModule.forFeature([User])
- User 엔티티를 현재 모듈에서 사용할 수 있도록 설정합니다.
- forFeature 메서드는 특정 엔티티에 대한 저장소(repository)를 현재 범위의 모듈에 주입하는 역할을 합니다.
8. 결과 확인
만들어진 코드를 실행한 후, Postman과 mysql workbench로 결과를 확인해 보겠습니다.
1) 회원가입


2) 조회

resource 명령어를 활용하여 CRUD 기능이 잘 작동하는 것을 확인할 수 있었습니다.
참고
https://docs.nestjs.com/techniques/database
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea
docs.nestjs.com
[NestJS] NestJS CLI로 REST API를 사용한 CRUD 기능 만들기(5분버전 vs. 심화버전) with TypeORM & MySQL
데이터베이스와 데이터베이스 연결을 지원하는 ORM 모듈을 사용하는지 여부에 따라 CRUD 생성 프로젝트 수준이 달라지게 됩니다. NestJS CLI가 제공하는 framing은 굉장히 강력하기 때문에 데이터베이
cdragon.tistory.com
'NestJS' 카테고리의 다른 글
| [NestJS] 알아보기 (0) | 2024.06.21 |
|---|