데이터를 조회하다보면 다른 테이블의 데이터를 함께 불러와야 하는 상황이 있다. (사실 거의 대부분이 그렇다.) 이때 Lazy loading, Eager loading에 대해 알고 있으면 쿼리문을 날리는 방법을 더 효율적으로 만들 수 있다.
Lazy Loading
현재 접근하고자 하는 메인 모델과 관련이 있는 모델의 데이터를 조회할 때, 메인 모델에 먼저 접근하고 관련 모델은 필요할 때 접근한다. -> 추가 쿼리를 날림.
Eager Loading
메인 모델에 접근할 때 관련 테이블을 조인해서 한번에 데이터를 조회한다.
👉 테이블 조인이란?
Eager Loading 방식은 추가 쿼리문을 날리지 않고 한번의 쿼리문으로 관련 테이블의 데이터를 모두 가지고 오기 때문에 N+1문제를 해결하여 데이터베이스 부하를 줄일 수 있다.
💡 N+1 문제
한번의 쿼리로 N건의 데이터를 가져온 후 원하는 데이터를 얻기 위해 N건의 데이터 수 만큼 2차 쿼리를 날리는 것이다. ORM 성능 저하를 일으키는 대표적인 이슈이다.
시퀄라이즈에서는 getModel()
메서드를 사용하면 Lazy loading방식으로 동작하고, findAll(), findOne()과 같은 finder쿼리의 include
속성을 이용해 Eager loading을 지원한다.
const productOption = await ProductOption.findOne({where: {id: 1}});
const product = await productOption.getProduct({raw: true});
console.log(product);
현재 노드 프로젝트에는 상품을 관리하는 Product
와 각 상품의 옵션을 관리하는 ProductOption
모델이 1:n
관계로 있다.
ProductOption
의 객체를 하나 가져온 뒤 ProductOption.productId = Product.id
인 Product
객체를 가지고 온다. 여기서 getProduct()
는 관계 설정 시 시퀄라이즈가 자동 생성해주는 메서드이다.
코드를 실행하면 아래 사진과 같은 쿼리문이 실행된다. ProductOption 테이블 한 번, Product 테이블 한 번 => 총 두 번의 DB 히트가 일어났다.
const productOption = await ProductOption.findOne({where: {id: 1}, include: Product});
const product = productOption.Product;
console.log(product);
이번에는 finder에 include 옵션을 주어 Product
테이블의 데이터를 한꺼번에 가져오도록 했고, 아래와 같은 쿼리문이 실행된다.
쿼리문을 잘 보면 LEFT OUTER JOIN으로 두 테이블이 묶인 것을 확인할 수 있다. 객체의 프로퍼티처럼 .
으로 Product에 접근할 수 있다.
여기서 주의할 점은 finder쿼리에 raw: true
속성을 주면 연관 객체에 접근할 수 없다는 점이다. 아마도 순수 데이터 외의 메타 데이터가 없어서 그런 것 같다.
시퀄라이즈에서는 eager loading과 관련한 다양한 옵션들을 제공하고 있는데 그 중 몇 가지만 간단히 살펴보도록 하자.
디폴트 조인인 outer join을 inner join으로 바꿔준다.
const productOption = await ProductOption.findOne({
where: { id: 1 },
include: {
model: Product,
required: true,
},
});
const product = productOption.Product;
console.log(product);
inclue안에 where 조건을 넣어주면 on 절로 변환이 된다.
여러 개의 테이블을 연결하고 싶으면 다음과 같이 각각의 테이블을 적어 줄 수 도 있다.
const productOption = await ProductOption.findOne({
where: { id:1 },
include: [
{
model: Product
required: true
},
{
model: Option
}
],
});
테이블을 결합하여 가져온 하나의 데이터와 연관된 또 다른 테이블을 결합하여 데이터를 조회할 수 있다.
const users = await User.findAll({
include: {
model: Tool,
as: 'Instruments',
include: {
model: Teacher,
include: [ /* etc */ ]
}
}
});
이밖에도 다양하게 eager loading을 활용할 수 있는 방법이 많으므로 더 알고싶다면 Sequelize Docs를 참고하면 좋을 것 같다.
참고
Sequelize Docs - Eager Loading
Sequelize 튜토리얼(13)_Lazy loading vs Eager loading