2차 프립 클론프로젝트

박준영·2021년 7월 4일
0
post-thumbnail

프립 소개

프립은 웹사이트와 애플리케이션을 통해 액티비티, 원데이 클래스, 소셜클럽, 여행 상품 등을 탐색부터 결제, 참여까지 간편하게 이용할 수 있는 서비스를 제공하고 있습니다.


팀원소개✔️

팀원구성 : 프론트 2명, 백엔드 3명
팀명 : 드립 - 모바일버전


모델링


구현기능

User app

  • kakao 소셜 로그인 구현

Review app

  • aws s3 이용한 리뷰 작성, 리뷰 좋아요, 리뷰 댓글 구현

Product app

  • 메인 페이지, 제품 상세페이지, 상품 리스트 페이지 구현

Order app

  • 주문페에지, 북마크페이지 , 마이페이지 구현

내가 구현한 기능🎉

이번에는 팀원분들이 많이 양보해줘서 api 2개를 이용할 수 있게 되었고, 리뷰쪽이랑, 소셜로그인을 구현하게 되었습니다.

카카오소셜로그인

  • 처음으로 api를 이용해봤는데 어려운 부분이 많았습니다. 먼저 kakao developer 사이트에 엄청 자세히 나와있다고 봤지만 몇 번을 봐도 어떤식으로 로직을 작성해야하고 어떻게 token을 받는지 잘 몰랐지만 이번에는 프론트쪽에서 토큰을 받아서 저희 백에게 준다고 알게 되었고 유저 정보를 받아오는 부분을 보고 소셜 로그인에 관한 글이 많아서 참고를 하면서 로직을 작성했던것 같습니다.


class KakaoSigninView(View):
    def post(self, request):
        try:
            access_token = request.headers.get("Authorization")
            
            if not access_token:
                return JsonResponse({'message' : 'ACCESS_TOKEN_DOES_NOT_EXITS'}, status=401)

            headers   = ({'Authorization':f'Bearer {access_token}'})
            kakao_url = 'https://kapi.kakao.com/v2/user/me'
            response  = requests.get(kakao_url, headers = headers)
            user      = response.json()
            
            if not user:
                return JsonResponse({'message' : 'INVALID_TOKEN'}, status=401)

            user, created = User.objects.get_or_create(
                name          = user['kakao_account']['profile'].get('nickname'),
                email         = user['kakao_account'].get('email'),
                profile_image = user['kakao_account']['profile'].get('profile_image_url'),
                origin_pk     = user.get('id')
            )
            
            result = {
                'name'          : user.name,
                'profile_image' : user.profile_image,
                'email'         : user.email
            }
            token = jwt.encode({'id' : user.id}, SECRET_KEY, ALGORITHM)
            
            return JsonResponse({'token' : token, 'data' : result}, status = 201)
            
        except IntegrityError:
            return JsonResponse({'message' : 'INTEGRITY_ERROR'}, status=401)

        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'}, status=400)

리뷰기능

  • 리뷰를 작성하는데도 이미지가 db에 데이터를 많이 잡아먹기 때문에 s3에 업로드 함으로써 s3에서 로직에서 지정한 URL로 나오기 때문에 db 공간을 줄일 수 있었습니다. s3는 boto3를 이용했고, 로직을 작성하기에 엄청 어려웠던 것 같습니다. s3도 어떤 식으로 하는지 최대한 찾아보았고 여러 내용을 적용해보며 로직을 작성해보았고, 최대한 내 것으로 만드는 게 중요했던 것 같습니다.

class ReviewView(View):
    @login_decorator
    def post(self, request):
        try:
            user    = request.user
            content = request.POST['content']
            rating  = request.POST['rating']
            product = Product.objects.get(id=request.POST['product'])
            
            s3_client = boto3.client(
                's3',
                aws_access_key_id     = ACCESS_KEY,
                aws_secret_access_key = SECRET_ACCESS_KEY
            )
            
            image      = request.FILES['filename']
            image_time = (str(datetime.now())).replace(" ","")
            image_type = (image.content_type).split("/")[1]                                     
            
            s3_client.upload_fileobj(
                image,
                "dripawsbucket21",
                image_time+"."+image_type,
                ExtraArgs = {
                    "ContentType" : image.content_type
                }
            )

            image_url = "https://dripawsbucket21.s3.ap-northeast-2.amazonaws.com/"+image_time+"."+image_type

            Review.objects.create(
                user      = user,
                content   = content,
                rating    = rating,
                image_url = image_url if image else None,
                product   = product,
            )

            return JsonResponse({'result' : 'SUCCESS'}, status = 201)

        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'}, status = 400)

    @login_decorator
    def get(self, request, product_id):
        try:
            user    = request.user
            order   = request.GET.get('order')

            order_conditions = {
                'new' : '-created_at',
                'old' : 'created_at',
                'like' : '-like_count',
                'unlike' : 'like_count',
            }

            reviews  = Review.objects.filter(product_id=product_id).annotate(like_count = Count('like')).order_by(order_conditions.get(order, '-created_at'))
            
            review_list = [{
                'created_at'   : review.created_at,
                'user'         : review.user.name,
                'content'      : review.content,
                'rating'       : review.rating,
                'product'      : product_id,
                'like_count'   : review.like_set.count(),
                'review_count' : reviews.count(),
                'image_url'    : review.image_url,
                'like'         : Like.objects.filter(user_id = user.id, review_id = review.id).exists(),
                'user_image'   : review.user.profile_image,
                'avgrating' : reviews.aggregate(avgrating=Avg('rating'))
                } for review in reviews]
            return JsonResponse({'result' : review_list, 'avgrating' : reviews.aggregate(avgrating=Avg('rating'))['avgrating']}, status = 200)

        except Product.DoesNotExist:
            return JsonResponse({"message" : 'DOES_NOT_EXIT_PRODUCT_ID'}, status = 401)

class CommentView(View):
    @login_decorator
    def post(self, request):
        try:
            data    = json.loads(request.body)
            content = data['content']
            user    = request.user
            review  = Review.objects.get(id=data['review'])
        
            Comment.objects.create(
                user    = user,
                content = content,
                review  = review
            )
            return JsonResponse({'message' : 'SUCCESS'}, status = 201)
        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'}, status = 400)

    @login_decorator
    def get(self, review_id):
        try:
            comment_list = [{
                'user'    : comment_list.user.id,
                'content' : comment_list.content,
                'review'  : review_id
            }for comment_list in Comment.objects.filter(review_id = review_id)]
            
            return JsonResponse({'result' : comment_list}, status = 200)
        except Review.DoesNotExist:
            return JsonResponse({"message" : 'DOES_NOT_EXIT_REVIEW_ID'}, status = 401)

class LikeView(View):
    @login_decorator
    def post(self, request):
        try:
            data      = json.loads(request.body)
            user      = request.user
            review_id = data['review_id']

            if not Review.objects.filter(id = review_id).exists():
                return JsonResponse({'message' : 'INVALID_REVIEW'}, status = 401)

            if not Like.objects.filter(user = user, review_id = review_id).exists():
                Like.objects.create(user = user, review_id = review_id)
            
                return JsonResponse({'message' : 'SUCCESS'}, status= 201)

        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'}, status = 400)

    @login_decorator
    def delete(self, request, review_id):
        Like.objects.filter(user = request.user, review_id = review_id).delete()

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

소통

이번 2차 프로젝트는 노션을 통해서 key값, body값, path등을 공유하게 되었고, 기능 구현하느라 늦게 작성할 때가 많았지만 프론트분들이 많이 이해해주셔서 원활하게 진행할 수 있었던 것 같습니다. 감사합니다!!


후기

한마디로 말하면 처음부터 쫄지말고, 도전하면 어떻게든 진행할 수 있다는 것입니다. 저를 돌아보면 프로젝트 진행하기 전에는 실력이 많이 부족하다고 생각해서 어떻게 하지…. 라는 생각을 많이 했습니다. 그렇지만 1차 프로젝트를 진행하면서 서서히 자신감이 생겼고 2차를 진행하면서 2주 동안 2개의 API를 이용했고, unit 테스트 까지 마칠 수 있었던 것 같습니다. 처음에는 불가능할 것 같았지만, 포기만 하지 않으면 할 수 있다는 점을 배우게 되는 계기였습니다. 항상 패기있는 백엔드 개발자가 되도록 항상 도전하는 자세를 가지겠습니다.💡

0개의 댓글

관련 채용 정보