select & prefetch_related [20211002]

Jungsoo kim·2021년 10월 3일
0

wecode

목록 보기
29/30

  직전 글과 마찬가지로 이번 2차 프로젝트를 진행하던 도중 도움이 많이 됐었던 내용에 대해서 공유하고자 이번 글을 작성하게 되었다. 제목에서 언급한 것과 같이 select_related & prefetch_related에 관련하여 작성하려고 한다.

<목차>
1. 구현한 기능
2. Code
3. 사용 이유 및 이점

1. 구현한 기능

  먼저 내가 구현한 기능에 대해 간단하게 말하자면 아래와 같다.

위 사진은 내가 프로젝트로 구현했던 8percent라는 홈페이지의 상품 상세페이지 인데, 내가 구현한 기능이 상품의 상세페이지였다.

8percent 홈페이지 상세페이지에 방문해 보신분은 알겠지만, 하나의 페이지에 백엔드에서 보내줘야할 데이터가 정말정말 많다. 따라서 최초 프로젝트 시작 시에 데이터 모델링을 할 때, 데이터를 보관하는 테이블을 여러개로 쪼개서 보관하고자 했고 그 결과 상세페이지에 나타나는 데이터와 연관있는 데이터 테이블이 8~9개에 달했다.

2. Code

기능구현에 작성된 코드는 아래와 같다.

class InvestmentDetailView(View):
    def get(self, request, investment_id):
        if not Investment.objects.filter(id = investment_id).exists():
            return JsonResponse({'MESSAGE' : 'NOT_FOUND'}, status = 404)

        investment = Investment.objects.select_related("grade", "repayment_type", "detail", "security", "borrower").prefetch_related("image_set").get(id = investment_id)
        images = investment.image_set.all()

        return JsonResponse(
            {
                "image_list"             : [image.url for image in images], 
                "id"                     : investment.id,
                "name"                   : investment.name,
                "grade"                  : investment.grade.name,
                "return_rate"            : investment.return_rate,
                "duration"               : investment.duration,
                "repayment_types"        : investment.repayment_type.name, 
                "current_amount"         : investment.current_amount, 
                "target_amount"          : investment.target_amount,
                "recrutement_rate"       : int(investment.current_amount/investment.target_amount*100), 
                "LTV"                    : round(((investment.target_amount + investment.detail.priority_bond_amount)/investment.detail.evaluation_price)*100, 2), 
                "repayment_day"          : investment.detail.repayment_day, 
                "loan_type"              : investment.detail.loan_type.name, 
                "evaluation_price"       : investment.detail.evaluation_price, 
                "priority_bond_amount"   : investment.detail.priority_bond_amount, 
                "security_surcharge"     : (investment.detail.evaluation_price - investment.detail.priority_bond_amount - investment.target_amount), 
                "bidding_rate"           : investment.detail.bidding_rate, 
                "expected_recovery"      : (investment.detail.evaluation_price * investment.detail.bidding_rate) - investment.detail.priority_bond_amount, 
                "address"                : investment.security.address, 
                "completion_date"        : investment.security.completion_date, 
                "household"              : investment.security.household, 
                "supply_area"            : investment.security.supply_area, 
                "exclusive_private_area" : investment.security.exclusive_private_area, 
                "lease_status"           : investment.security.lease_status, 
                "latitude"               : investment.security.latitude, 
                "longitude"              : investment.security.longitude, 
                "credit_score"           : investment.borrower.credit_score, 
                "income_type"            : investment.borrower.income_type, 
                "income"                 : investment.borrower.income, 
                "card_usage_amount"      : investment.borrower.card_usage_amount, 
                "loan_amount"            : investment.borrower.loan_amount, 
                "is_overdue"             : investment.borrower.is_overdue, 
                "overdue_tax"            : investment.borrower.overdue_tax
            }, status = 200)

위에서 언급했던 것과 같이 보내줘야 하는 데이터가 정말정말 정말정말 많다... 그러나 내가 여기서 언급하고 싶은 것은 데이터가 많다는 것이 아니라 이 데이터들을 호출하기 위해 많은 테이블을 활용해야 한다는 것인데 그와 관련하여 select_related, prefetch_related 기능을 유용하게 사용하는 방법에 관한 것이다.

3. 사용이유 및 이점

  이미 눈치 빠른 분들은 아셨겠지만 위의 코드를 보면 나는 select_related, prefetch_related를 하나의 코드에 같이 썼다. 거기다가 select_related의 경우에는 하나의 코드 안에 여러개의 테이블이 한꺼번에 호출되는 것을 볼 수 있다.

이는 즉 데이터를 호출하기 위해 아무리 많은 데이터를 거쳐야 한다고 하더라도 select_related와 prefetch_related를 활용하면 한 줄의 코드로 모든 테이블을 호출하는 것이 가능하다.

이는 내가 저번주에 작성하였던 ORM 최적화와 관련된 내용이기도 한데, 이런 방식으로 코드를 작성하게 되면 데이터 베이스에 날리는 query 수를 획기적으로 줄이는 것이 가능하다.

간단하게 하나만 설명하고 넘어가자면, select_related의 경우 포함되어 있는 테이블을 기준이되는 테이블과 연결하여 한꺼번에 호출하는 방식이라 요청 query의 수가 늘어나지 않는다. prefetch_related는 사용할 때마다 하나의 추가 query를 날리긴 하지만 관련 테이블을 한 번에 호출할 수 있다는 장점이 있다. 이와 관련된 내용은 구글링을 통해 좀더 심화 학습하는 것을 권장한다.

이글을 통해 하고 싶었던 얘기는 매우 간단하다. Django를 사용할 때 ORM 최적화를 하기 위해서는 select_related & prefetch_related를 활용하는 것이 매우 중요한데, 이 두가지 기능은 한줄의 코드로 연결하여 사용할 수 있다는 것이다. 생각보다 이 사실을 모르는 동기들이 많이 있었기 때문에 이와 같은 글을 쓰게되었으며 다른 사람들에게 조금이나마 도움이 되었으면 좋겠다.
그럼 20000...

profile
어렵지만 꾸준히 차근차근 해 나가자~!

0개의 댓글