개요설치NestJS 연동Entitykind of decorators데코레이터 중첩 (graphql, TypeORM)synchronizeActive Records vs Data MapperCreate, Save, UpdatEntity와 Dto 동기화 (Mapped type)방법1방법2 - 추천graphql, typeorm, validation 3가지를 entity 하나의 파일에서 하는 예시
- typeormGithubtypeormOwnertypeormUpdatedMar 23, 2025
- https://typeorm.io/
- 타입스크립트 기반이라 추천
- nest 공식문서
- nest 공식문서 database - typeORM initegration란 보면됨
개요
sql을 직접 작성해서 db와 상호작용 할 수도 있지만,
TypeORM을 통하면 코드를 통해 db와 상호작용 할 수 있다.
설치
npm install --save @nestjs/typeorm typeorm pg # postgresql(쓰고자 하는 db 쓰면 됨) # e.g. # npm install --save @nestjs/typeorm typeorm mysql
NestJS 연동
DB 연결정보 등의 typeorm 설정은
ormconfig.json
파일로 할 수도 있고, 코드
에 바로 작성할 수도 있다.
아래는 코드로 설정하는 예시
@Module({ imports: [ TypeOrmModule.forRoot({ type: 'postgres', host: 'localhost', port: 5432, username: 'haneojin', password: '12345', // localhost의 경우 비번 틀려도 됨. database: 'nuber-eats', synchronize: true, logging: true, // 콘솔에 출력 }), ... ], controllers: [], providers: [], })

postgresql
port
확인 방법은 위와 같다.postgresql을 열고 server setting에서
port
를 확인할 수 있다.Entity
- typeorm 공식 문서 : https://typeorm.io/entities
Entity
is aclass
that maps to adatabase table
(or collection when using MongoDB). You can create an entity by defining a new class and mark it with@Entity()
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm" @Entity() export class User { @PrimaryGeneratedColumn() id: number @Column() firstName: string @Column() lastName: string @Column() isActive: boolean }
import { Field, ObjectType } from '@nestjs/graphql'; @ObjectType() export class Restaurant { @Field(() => String) name: string; @Field(() => Boolean, { nullable: true }) isVegan: boolean; @Field(() => String) address: string; @Field(() => String) ownerName: string; }
graphql
의 ObjectType
을 선언하는 것과 굉장히 유사하다.ObjectType
은 자동으로 graphqlschema
를 빌드하기 위해 사용하는데코레이터
다.
Entity
데코레이터
는TypeOrm
이DB
에 이걸 저장할 수 있도록 해준다.
kind of decorators
- https://typeorm.io/entities#special-columns
- @CreateDateColumn
- @UpdateDateColumn
- @DeleteDateColumn
- @VersionColumn
entity를 만들었을 때 자동으로 설정해준다.
데코레이터 중첩 (graphql, TypeORM)
데코레이터라서 중첩으로 쌓으면 된다.
graphql 스키마를 생성하기 위한 데코레이터와, TypeORM을 위한 DB에 저장될 실제 데이터 형식을 표현하는 데코레이터 모두 하나의 entity를 선언하면서 충족시킬 수 있다.
import { Field, ObjectType } from '@nestjs/graphql'; import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; @ObjectType() // for graphql schema @Entity() // for TypeORM export class Restaurant { @Field(() => Number) // for graphql schema @PrimaryGeneratedColumn() // for TypeORM id: number; @Field(() => String) // for graphql schema @Column() // for TypeORM name: string; @Field(() => Boolean, { nullable: true }) // for graphql schema @Column() // for TypeORM isVegan: boolean; @Field(() => String) // for graphql schema @Column() // for TypeORM address: string; @Field(() => String) // for graphql schema @Column() // for TypeORM ownerName: string; }
synchronize

prod가 아닐 경우에는
synchronize=true
로 설정하였으므로 개발 시에는 자동 싱크가 된다.싱크할 entities를 따로 명시하였고,
entities
가 변화되면 TypeORM
의 synchronize
옵션에 의해 자동으로 DB에 변경사항이 마이그레이션 되며 반영된다.Active Records vs Data Mapper
- TypeORM 공식문서 : https://typeorm.io/active-record-data-mapper
선택하면 되는데
Data Mapper
- repository를 통해서 관리됨
- 어디서든 접근할 수 있다. (앱 코드 내에서도, 테스트 코드에서도)
- 대규모 앱에서 유용하다
Active Records
- 소규모 앱에서 단순하게 사용할 수 있도록 도와줌
NestJS
+TypeORM
에서 Repository
를 사용할 수 있다. repository를 활용하면 어디서든 접근할 수 있다. (테스트에서도 사용 가능)
그래서
Data Mapper
형태로 가는 것을 좀 더 추천함// 블라블라 레포지토리 연동하는 과정
// …

graphql로 요청했을 때, 실제 DB에서 쿼리를 쳐서 결과를 반환하도록 된 것을 확인할 수 있다. 연동 성공!
Create, Save, Updat
create
: 자바스크립트 단에서 단순히 인스턴스를 생성해줌 (DB는 건들지 않음)
save
: DB에 반영함 (없다면 만들고, 있다면 업데이트함)
update
: 항목을 업데이트함. - 첫번째 인자는 criteria인데, 업데이트 하고자 하는 항목을 특정하기 위함임. id를 넣어주면 searchById가 되겠지만 {name: "blah"} 와 같은 형태도 서칭도 가능
update
는 실제로 DB에 있는지 없는지 여부를 체크하지 않는다는 점에 유의!!
update
는 entity를 수정하는게 아니라 바로 db에 쿼리를 보냄. 가장 빠른 방식으로 최적화 되어있기 때문. 따라서 @BeforeUpdate와 같은 데코레이터가 동작해야한다면, entity로 수정 후 save를 이용해야 한다.
- i.g. 패스워드 변경 시 해시화를 시킨 후 저장해야하는데, update를 통하면 @BeforeUpdate가 동작하지 않아서 패스워드가 해시화되지 않은 채 db에 평문으로 저장하도록 update query를 치게 됨.createRestaurant( createRestaurantDto: CreateRestaurantDto, ): Promise<Restaurant> { const newRestaurant = this.restaurants.create(createRestaurantDto); // instance 생성 return this.restaurants.save(newRestaurant); // 실제 db에 저장 }
Entity와 Dto 동기화 (Mapped type)
entity
에 필드가 수정됐으면, Dto
에도 마찬가지로 수정이 되어야 한다.하지만 까먹을 경우가 있다.
어떻게 까먹는 휴먼에러를 없애고, 쉽게 동기화 시킬 수 있을까?
이걸 위해 mapped type을 사용할 거다.
- nestjs
mapped type
: https://docs.nestjs.com/techniques/validation#mapped-types - PartialType() : 전부 optional로 해서 새로운 걸 만듦
- PickType() : 특정 부분만 선택해서 새로운 걸 만듦
- OmitType() : 특정 부분만 제외하고 새로운 걸 만듦
- IntersectionType() : 여러개를 합쳐서 새로운 걸 만듦
인자 구성 : ( Base, key, decorator )
InputType
에 대해서만 동작하므로, InputType으로 만들거나 InputType으로 변환해서 사용해야 한다.
mapped type의 세번째 인자를 통해 decorator를 바꿀 수 있도록 지원하고 있어서, 혹시나 첫번째 인자가 InputType이 아닌 경우에는 마지막에 InputType을 명시해서 decorator를 바꿔주면 정상적으로 동작한다.@nestjs/
graphql
이나 @nestjs/swagger
를 쓰고 있다면 해당 라이브러리에 적합한 mapped type을 사용해야 한다.
- mapped type of graphql ← 나는 graphql 쓰니까 이걸 써야함
- mapped type when using swagger방법1
// dto file @InputType() export class CreateRestaurantDto extends OmitType( Restaurant, ['id'], // entity에서 id는 제외하고 만들고 싶음 InputType, // Restaurant가 InputType이 아니라 ObjectType이므로 세번째 인자를 통해 InputType으로 데코레이터를 변형시킨 후 mapped type이 동작하도록 해줘야 함. ) {} // entity file @ObjectType() // for graphql schema @Entity() // for TypeORM export class Restaurant { @Field(() => Number) // for graphql schema @PrimaryGeneratedColumn() // for TypeORM id: number; @Field(() => String) // for graphql schema @Column() // for TypeORM name: string; @Field(() => Boolean, { nullable: true }) // for graphql schema @Column() // for TypeORM isVegan: boolean; @Field(() => String) // for graphql schema @Column() // for TypeORM address: string; @Field(() => String) // for graphql schema @Column() // for TypeORM ownerName: string; }
Restaurant
는 ObjectiveType 데코레이터
로 ObjectiveType이다.하지만
DTO
객체는 InputType
이어야 한다.이 때
MappedType
의 세번째 옵셔널 인자인 decorator
를 명시하여 다른 데코레이터가 적용되도록 덮을 수 있다. 그래서 dto
선언 시 extends
를 하면서 InputType 데코레이터
가 되도록 명시했다.방법2 - 추천
dto 파일 하나에서 다 처리하고 싶으므로, 개인적으로 이 방법을 추천.
1. graphql
2. typeORM
3. validation
@InputType() export class CreateRestaurantDto extends OmitType(Restaurant, ['id']) {} @InputType({ isAbstract: true }) @ObjectType() // for graphql schema @Entity() // for TypeORM export class Restaurant { @Field(() => Number) // for graphql schema @PrimaryGeneratedColumn() // for TypeORM id: number; @Field(() => String) // for graphql schema @Column() // for TypeORM name: string; @Field(() => Boolean, { nullable: true }) // for graphql schema @Column() // for TypeORM isVegan: boolean; @Field(() => String) // for graphql schema @Column() // for TypeORM address: string; @Field(() => String) // for graphql schema @Column() // for TypeORM ownerName: string; }
entity file에서 ObjectType으로 명시할 거지만, InputType을 abstract로 선언한다면,
dto 파일에서 decorators 란을 명시적으로 InputType으로 덮어쓰지 않아도 된다.
두 가지 방법이 있는 것인데, 선택은 자유
graphql, typeorm, validation 3가지를 entity 하나의 파일에서 하는 예시
@Field((type) => Boolean, { defaultValue: true }) // graphql 스키마에서 이 필드의 defaultValue가 true임을 의미 @Column({ default: true }) // database에서 이 필드의 defaultValue가 true임을 의미 @IsOptional() // validation : 이건 옵셔널일 수 있고, @IsBoolean() // validation : 값이 있다면 boolean 이어야 한다. isVegan?: boolean;