안녕하세요 도깨비 개발자입니다.
쿼리를 직접 짜는 비율을 줄여주는 ORM 기술(Spring의 JPA, Node의 TypeOrm등등)은 요즘 많이 쓰입니다.
엔티티 관점으로 영속성 있는 데이터를 다룰수 있어 저 또한 ORM을 쓰는데요.
많이 사용되는 1:N, N:1 릴레이션을 효과적으로 가져갈 방안을 전달드리려 합니다.
@Entity()
class Customer {
@OneToMany(() => Order, (order) => order.customer)
orders: Order[];
}
@Entity()
class Order {
@ManyToOne(() => Customer, (customer) => customer.orders)
customer: Customer;
}
한 유저는 여러 주문 정보를 가질수 있다로 가정해보겠습니다.
쿼리 성능 관점에선 N:1은 참조 대상이 하나여서 일반 조인으로 조회가 가능합니다.
그러나 1:N은 부모(1)에 대한 자식(N)을 가져올때 Join이나 서브 쿼리를 사용해 성능이 저하됩니다.
-- N:1 관계에서는 단순한 JOIN으로 해결 가능
SELECT * FROM orders o JOIN customers c ON o.customer_id = c.id;
-- 1:N 관계에서 고객과 그 고객이 가진 모든 주문을 한 번에 가져오려면
-- 경우에 따라 서브쿼리, GROUP_CONCAT, JSON_AGG 등 별도 처리가 필요
1:N에선 부모의 자식 정보 조회시, N+1문제도 발생합니다.
보통 1:N은 Lazy Loading이 기본 값인데요.
leftJoin을 쓰지 않는다면 아래 상황에선 customer 1번, 각 customer에 대응되는 order N번을 호출해야합니다.
const customers = await dataSource.getRepository(Customer).find();
for (const customer of customers) {
console.log(customer.orders); // 주문 리스트 접근 (Lazy Loading)
}
-- 1. 먼저 고객 목록을 조회 (1개의 쿼리)
SELECT * FROM customer;
-- 2. 각 고객의 주문을 조회하는 쿼리가 반복 실행됨 (N개의 쿼리)
SELECT * FROM orders WHERE customer_id = 1;
SELECT * FROM orders WHERE customer_id = 2;
SELECT * FROM orders WHERE customer_id = 3;
...
연관 관계 설정의 명확성에도 차이가 납니다.
외래 키(FK)는 보통 자식(N)쪽에 위치합니다. N:1 에서는 N이 1을 참조하므로 FK가 직관적으로 관리됩니다
반대로 1:N은 부모 엔티티에 리스트를 추가해줘야하는데, 별도 설정은 존재하지 않습니다.
@Entity()
class Customer {
@OneToMany(() => Order, (order) => order.customer)
orders: Order[];
}
@Entity()
class Order {
@ManyToOne(() => Customer, (customer) => customer.orders)
@JoinColumn({ name: 'customer_id' })
customer: Customer;
}
그래서 N:1은 기본적으로 사용하고, 1:N은 필요한 경우에 사용하는게 전반적입니다.
다만 저는 엔티티간 릴레이션을 API단에서 모두 표현하는게 직관적이라 생각됩니다.
고객의 주문 정보가 더 많이 쓰이기도 하고, 효율적 접근 가능성을 열어두려면 N:1, 1:N 모두 열어두는게 좋겠죠.
성능 문제가 고민되긴 했는데, 다행히 TypeOrm에선 relation 옵션을 제공합니다.
find시 릴레이션 엔티티 조회가 Eager하지 않고, relation 옵션을 사용할때에만 Inner 조회 하도록 해줍니다.
혹은 QueryBuilder의 LeftJoin으로 접근해도 되구요.
이상입니다
'개발' 카테고리의 다른 글
[CS] 동시성과 병렬성 그리고 비동기 (0) | 2025.03.13 |
---|---|
[k8s] outbound ip 고정 (0) | 2025.02.24 |
[AWS] ECS ec2로 private subnet에 배포하기 (0) | 2024.04.11 |
[AWS] ECS 테스크 생성이 지연될때 (0) | 2024.04.09 |
[yolov5] ImportError: dlopen(/Users/nojeong-u/Library/Python/3.9/lib/python/site-packages/h5py/_errors.cpython-39-darwin.so, 0x0002) (0) | 2023.10.17 |