본문 바로가기
프로그래밍/Django

[Django][Python]QuerySet 캐싱을 통해 문제 해결하기

by 우주를놀라게하자 2022. 4. 6.
반응형
SMALL

[Django][Python]QuerySet을 통해 알아보는 ORM의 특징

 

[Django][Python]QuerySet을 통해 알아보는 ORM의 특징

해당 정보는 Pycon자료를 통해 만들었습니다. 참고자료는 아래와 같습니다 [링크1], [링크2] ORM이란? ORM 설명 Object Relational Mapping, ORM은 객체 지향적인 방법으로 데이터를 쉽게 조작할 수 있게 해준

dentuniverse.tistory.com

위의 링크는 Django ORM이 가지고 있는 문제를 정리하고, 발생할 수 있는 문제에 대해서 정리해두었다.

그중에서 오늘은 Lazy Loading을 통해서 생길 수 있는 문제를 해결하는 방법을 코드로서 구현해보려고 한다.

앞서 올린 글에서도 언급했지만 Django에선 지연로딩(Lazy Loading)으로 인해서 Query문을 여러차례 불러오는 실수를 범할 수 있다.

아래는 단적인 예시다.

def i_am_function_view3(request: WSGIRequest):

    # User를 선언하는 시점에는 SQL이 호출되지 않음
    users: QuerySet = User.objects.all()

    # 0번째 User를 얻어오고싶어서 users쿼리셋은 SQL을 호출
    first_user: User = users[0]

    # 바로 윗줄에서 user1명밖에 가져오지 않아서 모든 user를 얻으려면 어쩔수 없이 다시 SQL을 호출해야함
    user_list: List[User] = list(users)


    # 직렬화 로직
    user_list_dict: List[Dict[str, Any]] = [
        model_to_dict(user, fields=('id', 'username', 'is_staff', 'first_name', 'last_name', 'email'))
        for user in user_list
    ]
    user_list_json_array: str = json.dumps(user_list_dict, indent=1, cls=DjangoJSONEncoder)

    return HttpResponse(content=user_list_json_array, content_type="application/json")

위의 코드로 살펴보면, 첫줄에서 유저에 대한 정보 쿼리를 선언하고 2번째 줄에서 list()로 쿼리문을 다시 실행시켜 결국은 쿼리를 한번만 실행해서 처리할 문제를 2번에 나눠서 처리해야하는 실수를 범하고 있다.

위의 코드를 실제 Django SQL문에서 확인하면 아래와 같다.

이러한 쿼리중복 사용을 막기위해선 간단하게, 쿼리문 사용위치를 바꿔주어 캐싱하는 방식으로 수정할 수 있다.

해결책은 아래와 같다.(위의 코드와 차이점은 쿼리셋을 호출하는 지점의 위치만 다르다! 2번 3번째줄 주목!)

def i_am_function_view4(request: WSGIRequest):

    # User를 선언하는 시점에는 SQL이 호출되지 않음
    users: QuerySet = User.objects.all()


    user_list: List[User] = list(users)
    # 바로 위에서 users쿼리셋은 모든 user를 가져오는 SQL을 이미 호출함 따라서 0번째 user는 users쿼리셋에 캐싱된 값을 재활용함 (SQL호출 X)
    first_user: User = users[0]

    # 이 예제를 통해 배울점: 쿼리셋을 호출하는 순서가 바뀌는 것만으로도 QuerySet캐싱때문에 발생하는 SQL이 달라질수있다.


    # 직렬화 로직
    user_list_dict: List[Dict[str, Any]] = [
        model_to_dict(user, fields=('id', 'username', 'is_staff', 'first_name', 'last_name', 'email'))
        for user in user_list
    ]
    user_list_json_array: str = json.dumps(user_list_dict, indent=1, cls=DjangoJSONEncoder)

    return HttpResponse(content=user_list_json_array, content_type="application/json")

같은 코드지만 친절한 설명이 있는 자료도 함께 첨부했다.

여기서 배울점은 쿼리셋은 캐싱을 하기 때문에 SQL의 순서가 바뀌기만 해도 효율적으로 데이터를 다룰 수 있다는 점이다.

반응형
LIST