본문 바로가기
Database

GraphQL 연관관계 객체 쿼리 시 발생하는 Lazy Loading 문제

by blackjack_96 2024. 11. 1.

파이썬 FastAPI와 SQLAlchemy를 이용하여

다음과 같이, 직원들의 직무(Job) 정보를 변경하고, 그 변경된 정보를 가져오는 쿼리를 작성하였다.

 

class UpdateJob(Mutation):
    class Arguments:
        job_id = Int(required=True)
        title = String()
        description = String()
        employer_id = Int()

    job = Field(lambda: JobObject)

    @staticmethod
    def mutate(root, info, job_id, title=None, description=None, employer_id=None):
        session = Session()

        job = session.query(Job).filter(Job.id == job_id).first()

        if title is not None:
            job.title = title
        
        if description is not None:
            job.description = description

        if employer_id is not None:
            job.employer_id = employer_id

        session.commit()
        session.refresh(job)
        session.close()

        return UpdateJob(job = job)

 

이 때 다음과 같은 쿼리를 서버로 보내더니

mutation {
  updateJob (
    jobId: 1,
    title: "machine",
    description: "i have machine",
    employerId: 1
  ) {
    job {
      id,
      title,
      description,
      employer {
        id,
        industry,
        contactEmail
      }
    }
  }
}

 

다음과 같은 오류가 발생하였다.

 

"errors": [
    {
      "message": "Parent instance <Job at 0x104b8a0f0> is not bound to a Session; lazy load operation of attribute 'employer' cannot proceed (Background on this error at: https://sqlalche.me/e/20/bhk3)",
      "locations": [
        {
          "line": 12,
          "column": 7
        }
      ],

 

job과 employer는 Many To One의 연관관계를 가지고 있으며, 

모델링을 할 때 job에서 employer를 참조할 수 있도록 하였다.

그런데 위 update 쿼리를 처리하는 부분을 보면 

 

session을 시작하고, 

Job 정보만을 가져온 뒤에

session을 종료한다.

 

sqlalchemy에서는 기본적으로 모든 경우에서 Lazy Loading으로 처리되기 때문에

Job과 연관관계가 설정된 employer정보는 가져오지 못하게 된 상태에서 세션이 종료되었고,

 

이렇게 세션이 종료된 이후에 employer 데이터에 접근하고자 했기 때문에 예외가 발생한 것이다.

 

그래서 다음과 같이, Job객체를 데이터베이스에서 불러올 때

Eager Loading을 이용해 Employer객체들도 한꺼번에 가져올 수 있도록

코드를 수정하였다.

@staticmethod
    def mutate(root, info, job_id, title=None, description=None, employer_id=None):
        session = Session()
        job = session.query(Job)\
             .options(joinedload(Job.employer))\
             .filter(Job.id == job_id).first()
        # job = session.query(Job).filter(Job.id == job_id).first()

 

이렇게 한 뒤에 다시 GraphQL 쿼리문을 서버로 보냈더니,

문제 없이 Job과 연관관계로 설정된 Employer정보도 함께 가져올 수 있었다.