DotFriends

제품 상세페이지 구현과 고민 01

King of Silicon Valley 2021. 9. 5. 22:29
728x90

제품 리스트 API구현을 마치고 상세페이지 API구현을 하게 되었다. 

 

우선 상세페이지 view는 path 파라미터로 받아서 어떤 상품인지 식별하게 했다. 

 

urlpatterns = [
    path('', ProductsView.as_view()),
    path('/<int:product_id>', ProductDetailView.as_view())
]

프론트에 던져 줄 데이터는 

 

상품 아이디, 이름, 가격, 좋아요 수, 이미지  총 5개 였다. 

 

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

        product = Product.objects.annotate(likes=Count("userproductlike")).get(id=product_id)
        
        results = {
            'id':product.id,
            'name':product.name,
            'price':int(product.price),
            'like': product.likes,
            'images':[image.url for image in product.image_set.all()],         
            }
        
        return JsonResponse({'results': results}, status=200)

간단하게 구현할 수 있었다. 

 

path파라미터로 받은 값으로 상품을 조회하고 없으면 404에러를 반환하고 있으면 다음 분기를 실행하게 했다. 

 

5가지 항목 중 좋아요 수가 문제였다. 

 

좋아요는 Many To Many 관계로 엮여있고 그 수를 세서 값으로 던져줘야했다. 

 

# 상품과 좋아요 관계 테이블 

class Product(models.Model):
    name             = models.CharField(max_length=64)
    price            = models.DecimalField(max_digits=10, decimal_places=3)
    discount_percent = models.DecimalField(max_digits=5, decimal_places=3,blank=True) 
    is_new           = models.BooleanField(default=False)
    category         = models.ForeignKey('category', on_delete=models.SET_NULL,null=True)
    like             = models.ManyToManyField(User,through='Userproductlike',related_name='products')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'products'

class UserProductLike(models.Model):
    user    = models.ForeignKey(User,on_delete=models.CASCADE)
    product = models.ForeignKey('product',on_delete=models.CASCADE)

    class Meta:
        db_table = 'userproductlikes'

그래서 annotate 문법을 사용했다. 

 

annotate는 테이블에 컬럼을 추가할 수 있는 문법이다. 

 

이번 경우에는 likes라는 컬럼을 추가했고 값으로는 좋아요 숫자를 넣어주었다. 

 

 product = Product.objects.annotate(likes=Count("userproductlike")).get(id=product_id)

그래서 깔끔하게 likes 항목에 product.likes를 넣어주었다. 

 

results = {
            'id':product.id,
            'name':product.name,
            'price':int(product.price),
            'like': product.likes,
            'images':[image.url for image in product.image_set.all()],         
            }

위 코드는 총 3번의 쿼리문을 날렸다. 

 

총 3번의 쿼리문이 날라간다.

이번에도 성능을 올리려고 prefetch_related를 사용해서 image_set을 적용하려 했으나 

 

똑같이 쿼리가 3번 호출됐다. 

 

상품이 하나이기 때문에 prefetch의 효과가 없었다. 

 

그리고 마무리를 하려했으나 리뷰가 추가되었다...