객체와 관계형 데이터베이스의 데이터를 자동으로 연결해주는 작업을 의미한다.
📁 TypeORM 공식문서
📁 Nest.js에서의 Database 사용 공식 문서
Nest.js에서 TypeORM
# Nest.js에서 TypeORM을 연동시켜주기 위해 사용하는 모듈
$> npm install --save @nestjs/typeorm
# typeorm module
$> npm install --save typeorm
# 사용하고자 하는 DB의 모듈을 설치해주면 됨(pg는 postgreSql)
$> npm install --save pg
typeORM config 파일을 생성하여 작성
typeorm.config.ts 👇
export const typeORMConfig: TypeOrmModuleOptions = {
type: "database type",
host: "database host",
port: int(database port),
username: "database username",
password: "database password",
database: "database name",
entities: [database에서 사용하는 entity들,,,],
sychronize: false
}
sychronize는 production에서는 false로 설정해주어야한다.
그렇지 않을 경우 데이터를 손실할 수 있는 위험이 있다.
TypeORM은 entity를 이용해서 데이터베이스를 생성하기 때문에 entity 파일이 어디있는지 설정에 적어주어야한다.
config 파일의 작성이 완료되었다면 root module 파일에 typeOrm config를 연결해준다.
app.module.ts 👇
@Module({
imports: [
TypeORMModule.forRoot(typeORMConfig),
]
})
별도로 config 파일을 만들지 않고 forRoot 내에 config 정보들을 작성해주어도 된다.
@Module({
imports: [
type: "database type",
host: "database host",
port: int(database port),
username: "database username",
password: "database password",
database: "database name",
entities: [database에서 사용하는 entity들,,,],
sychronize: false
]
})
TypeORM은 생성한 entity class를 이용하여 database를 생성한다.
MyEntity.ts 👇
// MyEntity class가 Entity임을 알려주는 decorator
@Entity()
export class MyEntity {
// id column이 entity의 primary key임을 알려줌
@PrimaryGeneratedColumn()
id: number
// entity의 column을 표현
@Column()
title: string
@Column()
description: string
}
Repository는 entity의 생성, 업데이트, 삭제, 불러오기 등을 처리한다.
즉, database에서의 CRUD에 대한 동작을 해주기 때문에 데이터베이스에 관련된 로직들은 repository에 작성해주면 된다.
MyEntityRepository.ts 👇
// class를 사용자 정의 repository로 사용하도록 선언하는 decorator
// 인자로 사용할 Entity class를 넣어줌
@EntityRepository(MyEntity)
export class MyEntityRepository extends Repository<MyEntity> {
// entity 컨트롤을 위해서는 Repository<>를 extends 해주어야 함
}
생성한 repository를 다른 곳에서 사용할 수 있도록 하기 위해서는 module에 import해주어야 한다.
module에 import를 하면 respository를 이용하고 싶은 다른 service들에서 사용이 가능하게 된다.
mydb.module.ts 👇
@Module({
imports: [
TypeORMModule.forFeature([MyEntityRepository]),
],
controllers: [MyDBController],
providers: [MyDBService]
})
export class MyDBModule {}
TypeORM 사용을 위한 앞선 설정들을 마무리했다면 이제 데이터베이스에 접근하여 데이터를 다루어볼 수 있다.
먼저 repository를 사용할 service에 repository를 종속성 주입(DI, Dependency Injection) 을 해줘야 사용할 수 있다.
mydb.service.ts 👇
@Injectable()
export class MyDB {
constructor(
@InjectRepository(MyEntityRepository) // 사용하지 않아도 정상적으로 동작하는가?
private myEntityRepository: MyEntityRepository
) {}
}
TypeORM을 연결해서 사용하기 때문에, 데이터베이스 동작(CRUD)을 할 때 TypeORM에서 제공하는 method들을 이용하여 간편하게 데이터베이스 동작을 실행시킬 수 있다.
📁 TypeORM 제공 methods
데이터베이스 동작을 하는 함수를 작성할 때에는 async / await를 이용하여 데이터베이스의 작업이 끝난 후 결과 값을 받을 수 있도록 설정한다.
(javascript를 기본적으로 비동기로 처리되기 때문에 동기로 동작하도록 하기 위해서는 async / await를 사용해주어야 한다.)
create를 이용하여 새로운 entity를 생성하고, 생성한 entity를 insert, save를 이용하여 데이터베이스에 정보를 삽입할 수 있다.
insert와 save 모두 여러개의 값을 동시에 데이터베이스에 입력할 수 있다.
async createEntity(createEntityDto: CreateEntityDto) : Promise <MyEntity> {
const newEntity = this.myEntityRepository.create({
...createEntityDto
})
/* or
const { title, description } = createEntityDto
const newEntity = this.myEntityRepository.create({
title,
description
})
*/
await this.myEntityRepository.save(newEntity)
return newEntity
}
create를 사용해서 entity를 생성하지 않고 바로 insert나 save를 이용해 데이터베이스에 정보를 삽입할 수도 있다.
async createEntity(createEntityDto: CreateEntityDto) {
await this.insert({
title,
description
})
}
TypeORM에서 다양한 method들을 제공해주기 때문에 필요한 방식이 있다면 공식 문서를 참고해서 필요 기능을 제공받으면 된다.
만일 특정 조건에 해당하는 정보들 중 가장 첫번째 값을 불러오고 싶다면 findOne() 을 사용하면 된다.
async getEntity(id: number) : Promise <MyEntity> {
// TypeORM의 .findOne() method를 이용하여 id에 해당하는 값을 DB로부터 불러옴
const result = await this.myEntityRepository.findOne(id)
/* or
const result = await this.myEntity.Repository.findOne({
where: {
id
}
})
*/
if (!result) {
// database에 id에 해당하는 값이 존재하지 않을 경우
throw new NotFoundException('${id} is not exist')
}
return result
}
데이터베이스에 저장되어있는 정보를 갱신하고 싶다면 update 혹은 save method을 사용하여 실행할 수 있다.
save를 사용할 경우에는,
하여 저장된 정보의 값을 변경할 수 있다.
async updateEntity(id: number, updateEntityDto: UpdateEntityDto) {
const entity = await this.myEntityRepository.findOne(id)
newEntity = {
...entity,
...updateEntityDto
}
await this.myEntityRepository.save(newEntity)
}
update를 사용한다면, 주어진 option 혹은 id값에 맞는 데이터를 갱신하고자하는 데이터로 변경할 수 있다.
async updateEntity(id: number, updateEntityDto: UpdateEntityDto) {
await this.myEntityRepository.update(id,{
...updateEntityDto
})
}
데이터베이스에 존재하는 데이터는 remove, delete를 이용하여 삭제할 수 있다,
remove를 사용할 때에는,
의 방식으로 진행을 해주는 것이 좋다.
async deleteEntity(id: number) : Promise <MyEntity> {
const result = await this.myEntityRepository.delete(id)
// delete return 값 -> DeleteResult {raw : [], affected : 1}
// delete가 성공할 경우 affected의 값이 1로 옮
if (result.affected === 0) {
throw new NotFoundException('${id} is not exsit)
}
}
데이터베이스 동작에 관련된 사항은 repository에서 동작하는 것이기 때문에, 위의 예시에서는 service에 작성해두었으나 해당 로직을 repository로 옮겨서 구현하는 것이 맞다.
repository로 옮길 때에는 this.myEntityRepository를 this를 이용해 TypeORM method들을 사용하면 된다.
위에서 다룬 메소드 외에도 TypeORM에서 다양한 메소드를 지원하기 때문에, 구현하고 싶은 기능이 있다면 공식 문서를 참고해보는 것이 좋다.
query() 메소드를 이용하여 직접 sql query를 실행하도록 할 수도 있고, createQueryBuilder() 를 이용해 원하는 query를 커스텀하여 사용할 수도 있다.