1차 프로젝트 후기 (샤넬 클론)

위코드 부트캠프에서 1차 프로젝트를 2주간 진행하였고 저는 백엔드 부분을 담당했습니다.

wechanel 소개

세계적인 명품 브랜드 샤넬의 한국 공식 홈페이지를 클론하는 프로젝트입니다. US사이트와 다르게 한국 공식 홈페이지는 로그인 기능이 없어 해당 부분은 US홈페이지를 클론 하였습니다. 개발 인원은 총 6명이며 백엔드 3명 프론트엔드 3명으로 구성되었습니다.

Demo

적용 기술 (Backend)

  • Python
  • Django
  • Beautifulsoup
  • Selenium
  • Bcrypt
  • JWT
  • Mysql
  • CORS headers
  • AWS : EC2, RDS
  • Docker

담당했던 부분

  • Trello를 활용한 Task 관리
  • DB Modeling 및 ERD, Django Model작성
  • Look, Product Crawling 및 DB Upload
  • 모든 Look의 데이터를 GET방식으로 전송하는 기능 구현
  • Look의 구성 Product 데이터를 GET 방식으로 전송하는 기능 구현
  • Look, Product를 Wishlist에 추가하는 기능 구현
  • Look, Product Wishlist를 취합하여 GET방식으로 전송하는 기능 구현
  • Postman을 활용한 API Document 작성
  • AWS EC2 및 RDS를 활용한 배포
  • Docker를 활용한 배포

GOOD

  • 프로젝트 초반, 데이터 모델링에 공을 많이 들여서 로직을 구현하는 단계에서 데이터 모델링을 변경해야 하는 이슈가 없었습니다.
  • Postman API Documentation을 잘 작성하여 이를 통해 Frontend 개발자에게 API 동작 방식을 설명하는데 편리했습니다.

BAD

  • 클론대상 웹사이트에 장바구니 기능이 없어 구현하지 못했습니다.
  • 클론 대상 사이트를 세밀하게 분석하지 못했습니다.
  • 구현할 기능을 Frontend 개발자와 소통이 부족하여 데이터를 미리 준비하지 못해 급하게 준비해야만 했었습니다.

개선점

  • Frontend 개발자와 미리 구현할 기능을 확실하게 상의해야겠습니다.
  • 클론 대상 사이트의 기능을 더욱 세밀하게 분석해야겠습니다.
  • List Comprehension을 적극적으로 활용하고 selectrelated, prefetchrelated의 개념을 더 공부해야겠습니다.

기록하고 싶은 코드

LookDetail

class LookDetail(View):
    def get(self, request, look_num):
        try:
            if Look.objects.filter(id=look_num).exists():
                looks       = Look.objects.prefetch_related('product').get(id=look_num)
                products    = looks.product.all()

                product_info = [{
                    'product_id'    : product.id,
                    'product_name'  : product.name,
                    'product_code'  : product.product_code,
                    'product_price' : product.price,
                    'color'         : [{
                        'color_id'      : color.id,
                        'color_name'    : color.name
                    } for color in product.color.all()],
                    'texture'       : [{
                        'texture_id'    : texture.id,
                        'texture_name'  : texture.name
                    } for texture in product.texture.all()]
                } for product in products]

                look_images = LookImage.objects.filter(look_id=look_num)
                look_image  = [image.url for image in look_images]

                return JsonResponse({
                    'img'        : look_image,
                    'products'   : product_info,
                }, status=200)

						else:
                return JsonResponse(
                    {'Message': f'INVALID_LOOK : {look_num}'},
                    status=400
                )

Look의 image들과 product들의 정보를 제공하는 코드입니다. 처음에는 각 정보를 각각의 테이블에서 정보를 가져오는 식으로 코드를 구성했으나 불러올때 마다 구성하는 Product의 수량에 의해 DB hit가 증가되게 되는 효율성이 안좋은 코드가 되었습니다.

그러나 Look테이블에서 prefetch_related를 통해 각각의 정보를 가져와 List Comprehension으로 Product_info를 만들었습니다.

이렇게 코드를 짜니 코드도 간결해지고 가독성도 좋아지는거 같습니다.

Wishlist

# Product Application

class LookWishlist(View):
    @login_decorator
    def post(self, request, look_id):
        looks           = Look.objects.all()
        user            = Account.objects.get(email=request.userinfo.email).id
        user_lookwish   = Look_wishlist.objects.filter(account_id = user)
        look            = looks.get(id=look_id)

        if user_lookwish.filter(look_id=look).exists():
            Look_wishlist.objects.get(
                account_id = user,
                look_id = look.id
            ).delete()

            return JsonResponse({"message" : f'delete look:{look_id}'}, status=200)

        else:
            Look_wishlist.objects.create(
                account_id  = user,
                look_id     = look.id
            )

        return JsonResponse({'message' : 'SUCCESS'}, status=200)

class ProductWishlist(View):
    @login_decorator
    def post(self, request, product_id):
        products        = Product.objects.all()
        user            = Account.objects.get(email=request.userinfo.email).id
        user_prodwish   = Product_wishlist.objects.filter(account_id=user)
        product         = products.get(id=product_id)

        if user_prodwish.filter(product_id=product).exists():
            Product_wishlist.objects.get(
                account_id = user,
                product_id = product.id
            ).delete()

            return JsonResponse({"message" : "delete product:{product_id}"}, status=200)

        else:
            Product_wishlist.objects.create(
                account_id = user,
                product_id = product.id
            )

        return JsonResponse({'message' : 'SUCCESS'}, status=200)
# Account Application

class Wishlist(View):
    @login_decorator
    def get(self, request):
        look_wishlist = []
        prod_wishlist = []

        uid             = Account.objects.get(email = request.userinfo.email).id
        lookwishes      = LookWishlist.objects.filter(account_id = uid)
        productwishes   = ProductWishlist.objects.filter(account_id = uid)

        for lookwish in lookwishes:
            look_images = [look_img.url for look_img in LookImage.objects.filter(look_id = lookwish.look.id)]
            look_image  = look_images[0]

            look_wishlist.append({
                'collection_id'     : lookwish.look.collection.id,
                'collection_name'   : lookwish.look.collection.name,
                'look_id'           : lookwish.look.id,
                'look_name'         : lookwish.look.name,
                'look_image'        : look_image
            })

        for productwish in productwishes:
            product_images  = ProductImage.objects.filter(product_id = productwish.product.id).first().url
            product_texture = [prod_texture.texture.name for prod_texture in TextureProduct.objects.filter(product_id = productwish.product.id)]
            menu            = ProductCategory.objects.filter(product_id = productwish.product.id).first().category.menu.name

            prod_wishlist.append({
                'menu'              : menu,
                'product_id'        : productwish.product.id,
                'product_name'      : productwish.product.name,
                'product_price'     : productwish.product.price,
                'product_texture'   : product_texture,
                'image'             : product_images

            })

        return JsonResponse({
            'look_wishlist' : look_wishlist,
            'prod_wishlist' : prod_wishlist
        }, status=200)

look과 product의 wishlist를 저장 및 삭제를 하며 두 wishlist의 정보를 취합하여 데이터를 전달합니다.

아릅답지 못한 코드지만 wishlist기능은 다양한 서비스에서 제공하는 기능이며 이를 조금 변형하면 like기능이 될거 같아 개인적으로 더 연구하고 싶어서 선정하였습니다 물론 이제 이 코드를 더 아름답게 만드는 방법에도 고민해봐야 할 것 같습니다.


Written by@Yongineer
Backend Developer

GitHubInstagram