ORM 과 쿼리셋

김지용·2022년 6월 11일
1

ORM(Object Relational Mapping) 사용시 장점

  • 패러다임 불일치 문제 해결 / 객체지향언어가 가진 장점을 활용가능
  • 생산성 / 지루하고 반복적인 CRUD SQL을 개발자가 작성하지 않아도 됨.
  • 데이터 접근 추상화, 벤더 독립성 / 데이터 베이스마다 미묘하게 다른 데이터타입, SQL을 손쉽게 해결
  • 유지보수 / 필드추가, 삭제시 관련된 CRUD 쿼리를 직접 수정하지 않고, 엔티티를 수정하면 됨
  • SQL문으로부터 분리된 설계, 데이터베이스에 의존하지 않는 비즈니스 로직에 집중하는 구현이 가능


장고 ORM에서 가지는 특징들

쿼리셋

  • selected_related() / prefetch_related()

Lazy Loading(지연 로딩): 정말 필요한 시점에 SQL을 호출한다. / ORM의 기본적인 전략

쿼리셋 캐싱 : User[0] 을 부르고 User(all)을 부르면 쿼리를 두번 호출하지만 순서를 바꿔줌으로써 쿼리를 한번 만 호출

Eager Loading : 한번에 많은 데이터를 가져오기 쿼리셋에서는 selected_related, prefetch_related라는 메소드를 제공

N+1 Problem 이라는 대표적인 ORM에서 레이지 로딩을 발생하는 문제
(for user in users처럼 users 수만큼 쿼리를 호출해야할 때)
그래서 이런 케이스는 Eager Loading이라는 전략을 취하게 쿼리셋에 옵션을 줘야함

Eager Loading 옵션으로 쓰는
.selectedrelated('정방향참조필드') # 해당 필드를 join해서 가져온다
.prefetchrelated('역방향참조_필드') # 해당 필드는 추가쿼리로 가져온다

prefetch_related가 추가쿼리로 가져오는 점에서 쿼리셋 작성 순서는
filter(~=~).prefetch_related()를 권장한다.

위에서 유저 프로필들을 EagerLoading했다 쳤을 때 / (이거는 장고에서 이렇게 설계해놓은 듯)
User.user_porfile.all() # 여기서는 result_cache를 재사용
User.user_porfile.filter(hobby="여행") # 여기서는 쿼리가 발생

그래서 쿼리를 한번 더 발생시키지않고 캐시를 사용하려면
user_pofile_list = [profile for profile in User.user_porfile.all() if user_porfile.hobby="여행"]
이런식으로 .all()로 캐시를 사용해서 불러오게하면서 조건을 붙이면 해결가능하다.

.raw를 사용해서 쿼리셋을 사용가능(데이터베이스 매핑을 해야할 때 추가로 사용하면 좋을듯?)

테스트코드 작성시 CaptureQueriesContext 사용 추천

서브쿼리 발생 조건들

  • 쿼리셋 안에 쿼리셋이 있을 때
  • .exclude() 조건절에서도 역방향 참조모델 서브쿼리 발생 / prefetch_related를 사용하는 방식으로 대체
    정방향 참조모델을 쓰면 조인이 잘 됨. / 역방향참조모델에서면 발생하는 버그성 동작임!

쿼리셋의 반환타입 : values(), values_list()

values(), values_list() 이용 시 EagerLoading 옵션(selected_related,prefetch_related) 무시하고 조인해서 가져옴
values(), values_list() 안에 필요한 정보를 담아야 호출 가능


ORM과 쿼리셋을 정리하면서 ORM을 쓰는 이유

로직을 작성할 때 단조로운 SQL 작업을 줄여주고 Object와 Relational을 Mapping 해주는 장점이 있다.
하지만, 복잡성이 올라가면 NativeSQL을 쓰는 것도 좋다고한다. 때로는 가독성이 속도보다 중요할 때가 있다. 더구나 팀프로젝트라면 팀적으로 쿼리셋에 대한 이해를 같이 가져가야하는데 이러한 부분에서 속도만 고집하기보다는 편의성 등 효율성을 추구하는 게 중요하다고 본다.

이러한 점에서 DRF와 Pydantic에 대한 개념도 추가로 정리하고 싶다.

아직도 남은 궁금증들 (이 부분들은 더 찾아보고 정리해봐야겠다)

ORM이라서 쿼리셋이 캐싱이 되는거임?? (아니면 이것은 장고 ORM의 특징인가요??)
장고 ORM이 파이썬 언어로 편하게 db 연결해주는 거라고 보면 되는지?
장고 ORM을 쓰는 이유?? (장고 기본 값이라고 이해해야함?? MTV 모델에서 db에 접근하기 위한?)
장고는 object.all() 이런 걸 쓰니까 무조건 ORM을 쓴다고 봐도 되는지?
쿼리문 object와 objects의 차이점??

참고자료 : https://github.com/KimSoungRyoul/Django_ORM_pratice_project

profile
김죵입니당 ^^

0개의 댓글