이 글은 NestJS를 기준으로 작성하였습니다.
데이터베이스에서 데이터를 가져올 때 TypeORM과 같은 ORM들은 클래스(엔티티)에 쿼리 결과를 매핑하여 가져온다.
find, findBy와 같은 함수나 createQueryBuilder의 getMany, getOne과 같은 함수들 말이다.
본론으로 들어가기 전에 위의 함수들을 간단히 살펴보고 들어가자.
find, findOne
간단한 CRUD를 만들 때 굉장히 편하다.
find를 사용하면 여러 개의 데이터를 리턴받을 수 있고, findOne은 한 개의 데이터를 리턴받는다.
파라미터에 select, where, join 등을 명시해주면 원하는 데이터를 엔티티에 매핑해주어 가져올 수 있다.
findBy, findOneBy는 where 조건만 파라미터로 줄 수 있고 나머지는 전부 동일하다.
const employee = await employeeRepository.findOne({
select: ['id', 'name', 'email'],
where: { id: 1, name: 'giwon' },
order: { id: 'DESC', name: 'ASC' },
})
console.log(employee) // { id: 1, name: 'giwon', ... }
아래는 해당 직원이 관리하는 클라이언트를 Join하여 가져오는 예제다.
const employee = await employeeRepository.findOne({
select: ['id', 'name', 'email'],
where: { id: 1, name: 'giwon' },
order: { id: 'DESC', name: 'ASC' },
relations: {
client: true,
}
})
console.log(employee)
/*
{
id: 1,
name: 'giwon',
...,
client: { ... },
}
*/
원래의 결과에서 client가 join 된다. 이 방식의 문제는 Join한 엔티티를 통째로 가져온다.
client가 소유하는 컬럼 중에서 일부만 필요하더라도 전부 가져온다는 것이다.
이러한 점은 데이터가 많아질수록 성능 저하를 불러올 가능성이 높다.
또한 저렇게 json안에 json객체를 포함하는 것이 아닌, flat하게 데이터를 가져오고 싶을 수도 있다.
그럴 때 getRawMany, getRawOne을 사용하면 된다.
getRawMany, getRawOne
{
id: 1,
name: 'giwon',
clientId: 10,
clientName: 'gildong',
}
데이터를 가져오기 위해서는 기존 엔티티에 매핑하는 방법으로는 불가능하다.
getOne, getMany는 쿼리 결과를 엔티티에 매핑해주므로 우리가 원하는 데이터를 원본 그대로 가져오려면 getRawOne, getRawMany를 사용해야한다.
하지만 getRawOne, getRawMany는 타입 추론이 any로 동작한다.
class-transformer의 plainToInstance 함수를 이용하여 우리가 원하는대로 매핑해주어야 한다.
원하는대로 매핑해주기 위해서는 dto가 필요할 것이다.
Expose 데코레이터를 사용하면 우리가 원하는 property를 노출시킬 수 있다.
class EmployeeWithClient {
@Expose()
id: number
@Expose()
name: string
@Expose()
clientId: number
@Expose()
clientName: string
}
혹은 기존 Employee의 name만 제외하고 나머지 property를 가져오면서 clientId, clientName만 가져오고 싶다면 이렇게 하면 된다.
class EmployeeWithClient extends Employee {
@Exclude()
name: string
@Expose()
clientId: number
@Expose()
clientName: string
}
createQueryBuilder를 이용하여 결과를 받아보면 리턴 타입이 any로 추론된다.
const result = await employeeRepository.createQueryBuilder('Employee')
.leftJoin('Employee.client', 'Client')
.select(['id', 'Client.id AS clientId', 'Client.name AS clientName'])
.getRawOne();
console.log(result); // any로 타입추론
/*
{
id: 1,
clientId: 10,
clientName: 'gildong'
}
*/
엔티티에 매핑되지 않았기 때문에 any 타입으로 추론되는 것이다.
즉, response는 엔티티의 인스턴스가 아닌 plain object이다.
이제 이 object를 우리가 만들어놓은 dto에 매핑하자.
import { plainToInstance } from 'class-transformer'
const result = await employeeRepository.createQueryBuilder('Employee')
.leftJoin('Employee.client', 'Client')
.select(['id', 'Client.id AS clientId', 'Client.name AS clientName'])
.getRawOne(); // any로 타입추론
// EmployeeWithClient로 타입추론
const result2 = plainToInstance(EmployeeWithClient, result);
이렇게 하면 우리가 작성한 쿼리에 맞게 response값을 추론할 수 있게 된다.
'BackEnd > Nest.js' 카테고리의 다른 글
NestJS v10.0.0 redis 에러 이슈 (2) | 2023.12.24 |
---|---|
[TypeORM] join한 릴레이션이 property로 추론되는 이슈 (0) | 2022.10.31 |