어제에 이어 클론 코딩을 본격적으로 들어가기 앞서 TypeORM on NestJS
에 대해 조금 더 자세히 알게되었다. 아! 그리고 클론 코딩 시리즈의 경우 매 게시글 마다 사용중인 스택을 디폴트 값으로 매번 넣어줄 예정이다.
- Programming Language
TypeScript- Framework
NestJS- API 설계
GraphQL and Apollo- Database & Relevant
PostgresQL & TypeORM
@ObjectType()
@Entity()
export class Restaurant {
@PrimaryGeneratedColumn()
@Field((type) => Number)
id: number;
@Field((type) => String)
@Column()
@IsString()
@Length(5)
name: string;
@Field((type) => Boolean, { defaultValue: true })
@Column({ default: true })
@IsOptional()
@IsBoolean()
isVegan: boolean;
}
위와 같이 typeorm
패키지에서 제공하는 @Entity Decorator
를 사용해서 테이블을 정의하게 되며 각 테이블의 Coloumn
값은 보면 알겠지만 @Column
으로 나타낸다.
(여기서 언급되지 않는 Decorator들은 typeorm
에서 제공되는 것이 아닌 다른 패키지에서 제공되는 것으로 생각하면 된다)
지난 시간, TypeORM과 DB를 연결하면서 Synchronize에 대해서 간략하게 언급을 했던 적이 있다.
Synchronize를 True로 설정한 뒤 다음과 같이 Entity를 추가해주면 테이블 생성이 쉬워진다.
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST,
port: +process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
synchronize: process.env.NODE_ENV !== 'prod',
logging: true,
entities: [Restaurant], // << Entity 추가 (Optional)
}),
상황에 맞게 Decorator를 패키지에 가져와서 Entity에 적용해주면 테이블 스키마 관리도 효율적으로 이루어지니 공식문서를 참고해서 적용해보도록 하자.
TypeORM Decorator For Entities
간단한 사용법은 다음과 같다 (공식문서 참고).
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity() //
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
isActive: boolean
}
=> Active Record 사용시 모든 Entity 설정은 extends BaseEntity
를 사용해야 한다.
=> Active Record 패턴은 모델 내에서 데이터베이스에 액세스하는 접근 방식
// example how to save AR entity
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await user.save()
// example how to remove AR entity
await user.remove()
// example how to load AR entities
const users = await User.find({ skip: 2, take: 5 })
const newUsers = await User.findBy({ isActive: true })
const timber = await User.findOneBy({ firstName: "Timber", lastName: "Saw" })
Active Record 사용 이유 : 소규모 프로젝트의 데이터 유지 관리 용이
간단한 사용법은 다음과 같다 (공식문서 참고)
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
isActive: boolean
}
const userRepository = dataSource.getRepository(User)
// example how to save DM entity
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await userRepository.save(user)
// example how to remove DM entity
await userRepository.remove(user)
// example how to load DM entities
const users = await userRepository.find({ skip: 2, take: 5 })
const newUsers = await userRepository.findBy({ isActive: true })
const timber = await userRepository.findOneBy({
firstName: "Timber",
lastName: "Saw",
})
=> Data Mapper는 Active Record와는 다르게 BaseEntity에서 상속을 받지 않아도 되며, 데이타베이스에서 직접 Repository를 가져와서 어디에서나 TypeORM Method
를 적용 할 수 있다.
=> Data Mapper는 모델 대신 리포지토리 내의 데이터베이스에 액세스하는 접근방식. 데이터 매퍼 접근 방식을 사용하여 "리포지토리"라는 별도의 클래스에서 모든 쿼리 메서드를 정의하고 리포지토리를 사용하여 객체를 저장, 제거 및 로드.
Data Mapper 사용 이유 : 대규모 프로젝트의 데이터 유지 관리 용이
사실, 규모가 어중간한 경우에는 둘 중 아무거나 써도 상관이 크게 없지만 이번 클론 코딩에서는 Data Mapper를 사용하게 되는데 그 이유는 다음과 같다.
TypeORM 사용방식은 이거 외에도 다양하므로 매일 매일 새로운 것을 사용 할 때마다 기록을 하려고 한다.