2주 간의 위코드 1차 프로젝트가 끝이 났다. 모르는 것은 찾아보고 물어보고, 먼저 알게된 것은 알려주면서 하루하루 성장하는 게 느껴졌던 시간이었다!
솔직히 처음 오늘의 집에 팀 배정이 되었을 때는 '이걸 1차 프로젝트에 할 수 있을까?'라는 의심이 들었다. 하지만 팀원들과 첫 미팅을 가지면서 우리가 할 수 있는 것과 없는 것을 구분하고 팀 목표를 세우면서 '할 수 있겠다!'라는 자신감을 얻었다. 막연한 두려움은 사라지고 즐거움과 기대감이 생겨났다!
우리 팀의 이름은 'Sweethome'으로 정했고, 정말 Sweet한 사람들만 모여있었는지 한 번의 다툼없이 잘 마무리되었다.
오늘의 집은 누구나 쉽고 재미있게 자신의 공간을 만들어가는 문화가 널리 퍼지기를 꿈꾸는 소셜 커머스 사이트이다. 1000만 회원이 이용하고 있는 원스톱 인테리어 플랫폼으로서 다양한 인테리어 콘텐츠와 개인 맞춤형 커머스 기능을 제공하고 있다.
사용자가 사이트에 들어와서 경험할 수 있는 하나의 Cycle을 완성하는 게 우리 팀의 목표였다.
회원가입/로그인을 하고
ㅡ> SNS 기능을 둘러보고
ㅡ> 스토어에서 상품을 고르고
ㅡ> 장바구니에 담고
ㅡ> 주문하기로 마무리
그 외 포스팅 업로드 하기, 마이페이지 기능 등은 추가 구현 사항으로 남겨두었다.
프론트엔드 3명 / 백엔드 4명
2021.02.15-2021.02.26 (12일)
✅ 표시는 내가 구현한 기능
공통
회원가입 & 로그인(User)
커뮤니티(Posting)
스토어(Product)
장바구니(Order)
오늘의 집은 인테리어 시장의 정보 비대칭성을 해결하기 위해 만들어졌고, 맞춤형 정보를 검색할 수 있는 기능이 발달했다. 따라서 커뮤니티나 스토어 공간에서 자신이 원하는 컨텐츠나 상품을 조건별로 세부 검색할 수 있다.
나는 스토어 부분을 맡게 되었는데 카테고리별 상품 조회, 다양한 조건별 필터링 및 정렬 기능을 제대로 구현해보고 싶었다.
나는 '잔잔한 바다에서는 좋은 뱃사공이 만들어지지 않는다' 말을 좋아한다.
개발이라는 게 계속해서 새로운 것을 배워가는 과정이기에 모든 것이 낯설고 힘들 수 밖에 없다.
하지만 새로운 것을 마주하는 것을 두려워하지 않고, 어렵다고 포기하지 않고,
이 모든 과정이 좋은 개발자가 시간이라고 생각하며 즐기기로 마음을 다잡았다.
처음엔 팀원으로서의 나는 어떠해야 하는지 깊게 생각해보지 않았다. 하지만 프로젝트가 진행될 수록 소통 또 소통이 가장 중요하다는 것을 느꼈다.
기본적으로 기술적인 소통도 중요하겠지만 그보다 우선되어서 '저건 나랑 상관없는 거야', '알아서 잘 하겠거니'라는 식의 마인드를 버리고 팀원들이 어떤 상황에 처해있는지 잘 살펴야겠다는 생각이 들었다.
낮은가격순, 높은가격순 정렬을 구현하기 위해 extra 메소드를 사용했다.
내가 마주한 문제는 할인가격을 기준으로 정렬해야 하는데 원래 가격(original_price)와 할인율(discount_rate)만 가지고 있다는 것이었다.
annotate로 구현하려고 시도했으나, annotate는 SUM, COUNT, AVG 등의 기본적인 수식계산만 지원하고 할인가격을 계산하는 original_price * (100 - discount_percentage) / 100
수식을 적용할 수 없었다.
폭풍 검색하던 중 extra 메소드로 메인 쿼리에 sql문을 추가 반영할 수 있다는 걸 알게 되었고, 아래와 같이 적용하였다!
order_condition = request.GET.get('order', None)
order_by_price = {'min_price' : 'discount_price', 'max_price' : '-discount_price'}
if order_condition in order_by_price:
products = products.extra(
select={'discount_price': 'original_price * (100 - discount_percentage) / 100'}).order_by(
order_by_price[order_condition])
참고로 파이썬 sorted 메소드를 활용해도 되는데, 이는 데이터를 모두 불러온 뒤에 파이썬이 정렬을 해주는 방식이다. 데이터를 데이터베이스에서 불러올 때부터 정렬을 해서 가져오기 위해서 장고의 order_by와 extra 메소드를 활용했다.
원래는 필터링 조건을 쿼리 파라미터와 if문으로 하나씩 나열했었다. 이렇게 말이다.
(카테고리별 조회도 필터링 개념으로 이해했다.)
category = request.GET.get('category', None)
sub_category = request.GET.get('subcategory', None)
detail_category = request.GET.get('detailcategory', None)
color = request.GET.get('color', None)
size = request.GET.get('size', None)
if category:
products = products.filter(detail_category__sub_category__category=category)
if sub_category:
products = products.filter(detail_category__sub_category=sub_category)
if detail_category:
products = products.filter(detail_category=detail_category)
if color:
products = products.filter(productoption__color=color).distinct()
if size:
products = products.filter(productoption__size=size).distinct()
순서에 맞게 조건을 잘 만들었다는 칭찬도 받았지만, 비슷한 형태의 if문이 반복된다는 review를 받았다.
이틀 정도 찾아보고 고민하면서 딕셔너리로 구현하려고 했는데 쉽지 않았다. 다른 멘토님께 딕셔너리로 구현하려고 노력한 과정과 어떤 부분에서 막히는지 설명을 드렸더니 참고할 수 있는 예시를 보내주셨다. 그 자료를 참고삼아 딕셔너리 방식의 필터링을 구현하였다.
filter_prefixes = {
'category': 'detail_category__sub_category__category__in',
'subcategory': 'detail_category__sub_category__in',
'detailcategory': 'detail_category__in',
'color': 'productoption__color__in',
'size': 'productoption__size__in'
}
filter_set = {
filter_prefixes.get(key): value for (key, value) in dict(request.GET).items() if filter_prefixes.get(key)
}
products = Product.objects.filter(**filter_set).distinct()
그냥 단순하게 딕셔너리 개념만 쓰이는 게 아니라 장고의 __in
과 파이썬의 딕셔너리 언패킹
개념까지 이해해야 했었다.
그리고 이 문제를 해결하면서 print를 찍어보는 게 중요하다는 느꼈다. print를 찍어보니 request.GET
이 QueryDict 형태이고 그 안의 값은 list 형태로 들어 있는 걸 알게 되었다. 어떤 식으로 값이 들어오는지 확인하니 코드를 짜는데 더 수월했던 것 같다.
prefetch_related와 select_related를 이용한 메모리 캐싱은 추가 구현으로 남겨둔 기능이었다. 1차 프로젝트 때는 구현을 못하겠다 싶었는데, 발표 당일 새벽 4시까지 매달려 결국 구현해낸 코드이다..!!
기본적인 prefetch_related와 select_related은 빨리 구현했는데, image 정보를 불러올 때 사용한 first()와 리뷰 별점 평균 rate_average을 계산할 때 사용한 aggregate()가 반복적인 추가 쿼리를 발생시키는 문제가 발생했다.
폭풍 구글링하여 first()는 all()[0]로 aggregate()는 annotate()를 사용하는 방식으로 문제를 해결했다.
(자세한 코드 설명은 추가 포스팅을 작성할 예정이다.)
products = Product.objects.select_related('company', 'delivery__fee') \
.filter(**filter_set) \
.prefetch_related('productimage_set', 'productreview_set') \
.annotate(rate_average=Avg('productreview__rate')).distinct()
products_list = [{
'id': product.id,
'name': product.name,
'discount_percentage': int(product.discount_percentage),
'discount_price': int(product.original_price) * (100 - int(product.discount_percentage)) // 100,
'company': product.company.name,
'image': product.productimage_set.all()[0].image_url,
'rate_average': round(product.rate_average, 1) if product.rate_average else 0,
'review_count': product.productreview_set.count(),
'is_free_delivery': product.delivery.fee.price == 0,
'is_on_sale': not (int(product.discount_percentage) == 0),
} for product in products
]
이렇게 많이 보내졌던 sql문이
이렇게 줄었다!
개발자 문화 중 가장 좋은 점이라고 생각하는 것은 바로 적극적인 지식 공유이다.
그래서 이번 프로젝트에서 내가 배운 것을 공유하려고 노력했다.
특히, 중간발표 때 Product.objects.filter(detail_category__sub_category__category=category)
처럼 fk로 엮여있는 테이블을 언더바 두 개 __
로 이을 수 있다는 걸 공유했는데, 몇몇 분들로부터 도움이 되었다는 피드백을 받아서 좋았다. 이 외에도 내가 먼저 구현한 기능에 관심이 있는 동기들이 있으면 설명해주고, 좋은 블로그 글이나 영상이 있으면 공유했다!
또한, 반대로 다른 사람들이 배운 것을 흡수하려고 노력했다.
다른 팀은 어떻게 하고 있는지 관심을 가지고 어떻게 문제를 해결했는지 먼저 물어보고, 때론 같이 고민했다. 내 프로젝트는 아니어도 언젠가는 내가 마주할 문제이니 충분히 의미있는 시간이었다고 생각한다.
공유하고 싶은 코드에서 작성한 3가지 코드 모두 해결하기 쉽지 않았던 문제들이다.
새벽 3~4시까지 구글링을 하면서 방법이 없을까 찾아보고 고민했다.
할 수 없을 줄 알았는데 결국 되더라!
또한, 설령 구글링으로 해결되지 않더라도, 내가 할 수 있는 만큼 최대한 고민을 하고나니 멘토님께 드리는 질문의 깊이도 달라지는 것을 느낄 수 있었다.
이 과정을 통해 구글링 실력 + 아무리 어려워보이는 문제라도 해결할 수 있다는 자신감을 얻었다.
더 적극적으로, 더 빨리 프론트엔드와 소통을 하면서 개발을 했었으면 좋았을 것 같다.
백엔드로서 API 로직을 잘 짜면 80~90% 이상은 다 끝난 것이라고 생각했다. 하지만, 아무리 로직을 잘 짰더라도 프론트엔드에서 받아주지 못하면 소통이 없었다.
기능 구현 단계에서부터 백엔드는 어떻게 하면 프론트엔드에서 데이터를 받기 좋을지, 또 프론트엔드는 백엔드 쪽에서 어떻게 데이터를 보내줄지 이해해야 한다는 것을 알았다.
우리 팀은 통신을 시작하면서 노션페이지를 열어 통신 방식과 데이터 형식을 맞춰갔다. 2차 프로젝트에서는 개발 초기 단계부터 적극적으로 맞춰가야겠다.
또한 내가 맡은 부분은 장바구니 등의 기능과 달리 프론트와 주고 받는 부분이 많이 없었는데 2차 때는 이런 부분을 더 많이 경험하고 싶다.
프로젝트 기간 중에는 블로그를 제대로 작성하지 못했다. 현업에서 일을 하면서 꾸준히 블로그를 운영하시는 분들이 대단하다고 느껴지는 2주였다.
몇몇 동기들은 프로젝트 기간에도 블로깅을 꾸준히 했었는데, 프로젝트를 하면서 배웠던 점을 생생하게 남겨둘 수 있어서 좋아보였다. 2차 때는 시간을 따로 떼서라도 블로깅을 열심히 해야겠다.
위코드의 장점이 오프라인으로 함께 프로젝트를 할 수 있다는 점이다. 서로의 코드를 보면서 고민하고 시도하고 실패하고 계속 반복하고.. 하지만 결국엔 성공해내는 과정이 너무 재미있었다. 또한, 멘토님들의 적절한 리뷰 덕분에 매번 한 단계 더 성장할 수 있었던 것 같다. 혼자 했다면 절대 이루지 못했을 결과라고 생각한다.
마지막으로 좋은 분위기를 만들어준 스위트홈 팀원들 덕분에 웃으면서 코딩할 수 있었다! 다음엔 더 멋지게 성장한 모습으로 만나요~
많이 배우고 갑니다~ 화이팅