[Sequelize] Lazy Loading / Eager Loading (feat.N+1문제, include)

나른한 개발자·2022년 4월 21일
0

node & sequelize

목록 보기
2/2

데이터를 조회하다보면 다른 테이블의 데이터를 함께 불러와야 하는 상황이 있다. (사실 거의 대부분이 그렇다.) 이때 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을 지원한다.

Lazy 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.idProduct객체를 가지고 온다. 여기서 getProduct()는 관계 설정 시 시퀄라이즈가 자동 생성해주는 메서드이다.

코드를 실행하면 아래 사진과 같은 쿼리문이 실행된다. ProductOption 테이블 한 번, Product 테이블 한 번 => 총 두 번의 DB 히트가 일어났다.

Eager Loading

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과 관련한 다양한 옵션들을 제공하고 있는데 그 중 몇 가지만 간단히 살펴보도록 하자.


required: true

디폴트 조인인 outer join을 inner join으로 바꿔준다.

const productOption = await ProductOption.findOne({
    where: { id: 1 },
    include: {
      model: Product,
      required: true,
    },
  });
const product = productOption.Product;
console.log(product);


where

inclue안에 where 조건을 넣어주면 on 절로 변환이 된다.


Mutiple Eager loading

여러 개의 테이블을 연결하고 싶으면 다음과 같이 각각의 테이블을 적어 줄 수 도 있다.

const productOption = await ProductOption.findOne({
    where: { id:1 },
    include: [
      {
        model: Product
        required: true
      },
      {
        model: Option
      }
    ],
  });

Nested Eager loading

테이블을 결합하여 가져온 하나의 데이터와 연관된 또 다른 테이블을 결합하여 데이터를 조회할 수 있다.

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

profile
Start fast to fail fast

0개의 댓글