[바미] NestJS를 사용하여 REST API 구현하기
안녕하세요. 사용자 정보를 생성(Create), 읽기(Read), 수정(Update), 삭제(Delete)하는 기본적인 CURD(Create, Read, Update, Delete) 기능을 갖춘 REST API를 구현해보겠습니다.
Nest.js 프로젝트 생성
먼저 Nest CLI를 사용하여 새로운 프로젝트를 생성해봅시다.
nest new Nest-project
모듈 생성
API를 구현할 모듈을 생성해봅시다.
nest g module app
nest g module user
컨트롤러 생성
nest g controller app
nest g controller user
서비스 생성
nest g service app
nest g service user
코드 작성
이제 본격적으로 코드를 작성해볼까요?
먼저 src/user 안에 user.module.ts파일을 작성해보도록 하겠습니다.
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
그 다음 user 정보를 담을 Model이 들어갈 user.entity.ts 파일을 작성해 보도록 하겠습니다.
typeorm을 사용하여 작성해보죠.
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
email: string;
}
그 후 user.module.ts에 이 모델을 추가해주어야 합니다.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
그 다음에는 해당 클래스에서는 사용자 정보를 다루는 메서드들을 구현하고 있는 UserService클래스를 작성해보도록 하겠습니다.
user.service.ts 부분을 수정해봅시다!
import {Injectable, NotFoundException} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import {UpdateUserDto} from "../dto/update-user.dto";
import {CreateUserDto} from "../dto/create-user.dto";
import {DeleteDto} from "../dto/delete.dto";
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
async findAll(): Promise<User[]> {
return this.userRepository.find();
}
async findOne(id: number): Promise<User> {
return this.userRepository.findOne({ where: { id } });
}
async create(createUserDto: CreateUserDto): Promise<User> {
const user = new User();
user.username = createUserDto.username;
user.email = createUserDto.email;
return this.userRepository.save(user);
}
async update(id: number, updateUserDto: UpdateUserDto) {
const user = await this.userRepository.findOne({ where: { id } });
if (!user) {
throw new NotFoundException('User not found');
}
await this.userRepository.update(id, updateUserDto);
return { message: 'User updated successfully' };
}
async remove(id: number): Promise<{ message: string }> {
const user = await this.userRepository.findOne({ where: { id } });
if (!user) {
throw new NotFoundException('User not found');
}
await this.userRepository.delete(id);
return { message: 'User deleted successfully' };
}
}
update, remove 부분만 간단하게 예외처리 하였습니다.
create에 따로 예외 처리 하지 않는 이유는 dto에서 빈 값들을 알아서 잡아내주기 때문입니다.
그 다음에는 데이터를 전달하고 검증하기 편하게 하기 위해 DTO를 작성해 줄 겁니다.
user디렉토리 안에 dto 디렉토리를 추가하여 post, update, delete에 대응하는 DTO를 만들어 봅시다.
create-user.dto.ts
export class CreateUserDto {
readonly username: string;
readonly email: string;
}
update-user.dto.ts
export class UpdateUserDto {
readonly username: string;
readonly email: string;
}
delete.dto.ts
export class DeleteDto {
readonly id: string;
}
이제 라우터를 담당하게 되는 컨트롤러 부분인 user.controller.ts에 코드를 추가해보죠.
아까 만들었던 DTO에 대응하도록 만들겁니다.
import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from '../dto/create-user.dto';
import { UpdateUserDto} from "../dto/update-user.dto";
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: number) {
return this.userService.findOne(id);
}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Put(':id')
update(@Param('id') id: number, @Body() updateUserDto: UpdateUserDto) {
const user = { id, ...updateUserDto };
return this.userService.update(id, user);
}
@Delete(':id')
remove(@Param('id') id: number) {
return this.userService.remove(id);
}
}
참고로 위와 같이 dto를 따로 따로 만들어서 import 하기 귀찮으시다면 user.dto.ts로 만든 다음에 위 클래스를 한 곳에 넣은 다음 3 클래스를 한 곳에 넣으신 다음에
import { CreateUserDto, UpdateUserDto, DeleteDto } from "../dto/user.dto";
위 형태로 하셔도 무방합니다.
자! 이제 마지막으로 app.module.ts 부분에 UserModule을 추가해봅시다!
여기에 Database를 연결해주고, UserModule도 import 해줘야 합니다.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import {UserModule} from "./user/user.module";
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '123456',
database: 'pre-test',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
UserModule
],
})
export class AppModule {}
디렉토리 구조
지금까지 잘 따라오셨다면 디렉토리 구조는 아래와 같습니다.
.spec.ts부분은 제외하고 넣었습니다
Nest-project/
├── src/
│ ├── user/
│ ├── dto/
│ │ ├── create-user.dto.ts
│ │ ├── update-user.dto.ts
│ │ ├── user.controller.ts
│ │ ├── user.model.ts
│ │ ├── user.module.ts
│ │ ├── user.service.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ ├── app.controller.ts
│ ├── main.ts
├── node_modules/
├── package.json
├── tsconfig.json
└── tslint.json
실행
이제 nest명령어를 사용하여 서버를 실행해봅시다.
nest start
그럼 우리가 controller에 추가했던 라우터들이 log에 출력되는 모습을 볼 수 있습니다.
[Nest] 20676 - 2024. 02. 28. 오전 12:42:23 LOG [NestFactory] Starting Nest application...
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [InstanceLoader] AppModule dependencies initialized +66ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +31ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [InstanceLoader] UserModule dependencies initialized +1ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [RoutesResolver] UserController {/users}: +13ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [RouterExplorer] Mapped {/users, GET} route +2ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [RouterExplorer] Mapped {/users/:id, GET} route +0ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [RouterExplorer] Mapped {/users, POST} route +1ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [RouterExplorer] Mapped {/users/:id, PUT} route +0ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [RouterExplorer] Mapped {/users/:id, DELETE} route +0ms
[Nest] 20676 - 2024. 02. 28. 오전 12:42:24 LOG [NestApplication] Nest application successfully started +1ms
이제 Postman을 실행하여 Test를 해보죠.
GET
아직 아무런 데이터를 추가하지 않은 상태이기 때문에 빈 값이 출력되는 모습을 볼 수 있습니다.
POST
POST는 @Body로 넣어주기 때문에 위와 같이 넣어주면 됩니다.
제대로 들어갔는 지 확인해볼까요?
정상적으로 수정된 것을 확인할 수 있습니다.
PUT
정상적으로 수정됐네요.
DELETE
지금까지 간단하게 Nest에서 REST API를 구현해보았습니다.
도움이 되셨다면 공감 버튼 부탁드립니다!