A progressive Node.js Framework
: Node.js 런타임 위에서 동작하는 TypeScript용 오픈 소스 백엔드 웹 프레임워크
TypeORM
: 객체와 관계형 데이터베이스의 데이터를 자동으로 연결 시켜주는 프레임워크라고 한다.
nest new [프로젝트 이름]
밑의 명령어를 통해 매번 crud를 작성하지 않아도 기본적인 구조를 자동으로 생성한다.
nest g resource [폴더이름]
먼저 오늘 진행한 프로젝트의 구조는 이렇다.

그럼 본격적으로 시작!
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.userService.remove(+id);
}
}
userService를 사용하기 위하여 constructor를 정의할 수 있다. 생성자는 클래스의 인스턴스를 만들 때 호출하는 메서드이다. 생성자를 통해 외부에서 주입된 userService를 클래스 내부에서 사용할 수 있다.
쿼리 스트링으로 url의 원하는 파라미터를 얻을 수 있다. 함수의 파라미터로 해당 값을 받아야 합니다. @Param("파라미터") 변수 이름 : 변수 Type으로 작성한다.
SpringBoot의 RequestBody와는 다르게 @Body라는 어노테이션으로 요청한 값을 받을 수 있다. @Body() 변수 이름 : 변수 Type으로 작성한다.
NestJs가 Nodejs + Typescript 기반이라 Type을 명시해줘야한다.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
// 1. 인스턴스 생성
const user = this.userRepository.create(createUserDto);
// 2. 인스턴스 Database에 저장
return await this.userRepository.save(user);
}
async findAll(): Promise<User[]> {
return await this.userRepository.find();
}
async findOne(id: number) : Promise<User>{
return await this.userRepository.findOne( { where: {id}});
}
async update(id: number, updateUserDto: UpdateUserDto) : Promise<User> {
// 1. 해당 아이디 유저 찾기
const user = await this.userRepository.findOne({where : {id}});
if (!user) {
throw new Error("유저를 찾을 수 없습니다.");
}
// 2. 기존 user와 새로운 updateUserDto와 병합
this.userRepository.update(id, updateUserDto);
// 3. 변경사항 저장
const updatedUser = await this.userRepository.save(user);
return updatedUser;
}
async remove(id: number): Promise<void> {
await this.userRepository.delete(id);
}
}
Injectable 데코레이터는 클래스가 의존성 주입 가능하게 해준다.
InjectRepository 데코레이터는 TypeORM 리포지토리를 주입하기 위해 사용한다.
Repository는 TypeORM에서 데이터베이스와 상호작용하기 위한 클래스이다.
우선 비동기라는 개념부터 알고가자. 비동기는 쉽게 말하자면 A라는 작업을 처리 여부와는 상관없이 B라는 작업을 하는 것과 같다. async/await는 콜백 함수와 프로미스의 단점을 보완하고 가독성있는 코드를 작성하도록 도와준다. 이를 통해서 더 간결한 코드와, 에러를 쉽게 처리할 수 있다. 특히 API 호출과 데이터베이스를 사용할 때 주로 사용한다.
async function 함수명() {
await 비동기_처리_메서드_명();
}
function 앞에 async를 붙이면 해당 함수는 Promise를 반환한다.
import { Repository } from "typeorm";
import { SetMetadata } from "@nestjs/common";
export const TYPEORM_CUSTOM_REPOSITORY = 'TYPEORM_CUSTOM_REPOSITORY';
export function CustomRepository(entity: Function): ClassDecorator{
return SetMetadata(TYPEORM_CUSTOM_REPOSITORY, entity);
}
위 코드가 @EntityRepository()의 대체 코드이다.
import { extname } from "path";
import { Repository } from "typeorm";
@CustomRepository(User)
export class UserRepository extends Repository<User>{
}
얼마 전까지 @EntityRepository을 사용하여 간편하게 Repository를 만들 수 있었지만, 버전이 업그레이드 되면서 사용이 중지되었다. 하지만 커스텀 Repository를 통하여 구성할 것이다.
CustomRepository에서 정의한대로 entity를 받게 된다. 여기서는 User Entity를 뜻한다. 또한 @EntityRepository()를 대신 @CustomRepository를 주입시켜 줄 것이다.
NestJs에서는 Module이라는 개념이 존재한다. @Module() 데코레이터로 주석이 달린 클래스입니다. @Module() 데코레이터는 Nest가 어플리케이션 구조를 이용하고 정리하게 해주는 메타데이터를 제공합니다.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forFeature([UserRepository, User])
],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
User와 아주 밀접한 관련이 있기 때문에 같은 모듈로 묶어서 사용한다. 여기서 TypeOrmModule.forFeature 메소드가 사용되고, 이 메소드는 TypeORM에서 특정 엔티티를 모듈에 등록하고 사용할 수 있도록 한다.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [UserModule,
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: 'mysql',
host: process.env.DB_HOST,
port: +process.env.DB_PORT,
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
entities: [User],
database: process.env.DB_DATABASE,
synchronize: true,
autoLoadEntities: true
}),
]
})
export class AppModule {}
forRoot() 메서드는 createConnection() 함수에 들어가게 되는 설정 값을 넣어줄 수가 있다. 따라서 env에서 설정한 DB 관련 정보들을 process.env.[변수명]을 통해 Database에 접근한다.
autoLoadEntities는 Entity를 자동으로 로딩해준다. 이때 synchronize는 true로 하면 현재 설정한 entity가 자동으로 생성된다.
[ExceptionHandler] Nest can't resolve dependencies of the UserService (?). Please make sure that the argument RoleRepository at index [1] is available in the UserModule context.
ERROR [ExceptionHandler] Nest can't resolve dependencies of the TypeOrmCoreModule (TypeOrmModuleOptions, ?). Please make sure that the argument ModuleRef at index [1] is available in the TypeOrmCoreModule context.