반응형
필수구현사항
커머스 사이트에서 유저의 기본 플로우를 따라서 메인페이지 - 회원가입/로그인 - 상품리스트 - 상세페이지 - 장바구니까지 구현을 하기로 했다. 결제도 하면 좋지만 어렵기때문에..
그리고 시간이 남는다면 추가구현으로는 상품검색, 결제(api 사용하지 않고 가입시 포인트를 주고, 구매시 포인트 차감형식으로)기능을 하기로 했다.
기간안에 주문까지 구현이 될지 모르지만 필수구현후 추가구현을 위한 모델링을 다시하는 등의 작업을 하지 않고 모델링을 최대한 변경하지 않기 위해 주문테이블까지 모델링을 했다.
다루는 데이터의 종류에 따라 앱을 3가지로 나누었다.
- users : users
- products : categories, products, product_images, products_options, options
- orders : carts, orders, order_items, statuses
이력관리 테이블
커머스 사이트를 이용하는 유저 입장에서 생각해봤을때는 장바구니에는 이력관리가 필요 없을것같았지만 데이터가 계속 바뀌는 테이블에 대해서는 이력관리가 필요하다는 것을 알았다
-> users, products, carts, order_items, orders 모두 이력관리 필요
core.models.py에 이력관리 테이블을 정의해서 상속해서 사용
# core.models.py
from django.db import models
class TimeStampModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
# users.models.py
.
.
from core.models import TimeStampModel
class User(TimeStampModel):
.
.
1. users app
users
- 유저의 정보를 저장하는 테이블
2. products app
categories
- 배민문방구 카테고리별 페이지에 카테고리별 설명이 있어서 description컬럼을 추가했다(나중에 카테고리별 페이지를 만들면서 보니까 사실 이부분은 데이터로 따로 관리하기보다 프론트엔드에서 페이지별로 뿌려주는 거라고 했지만 일단 이렇게 완료)
products
- 가격을 처음에 int형으로 했는데 할인등등 계산을 생각하면 소수점까지 표현하는 형식이 가격을 표현하는 데 적합하다는 것을 알았다. 그중에 decimal형이 float, double보다 느리지만 정확한 연산을 필요로 하는 금액을 표현할 때 가장 적합한 자료형이라는 것을 알았다.
- 배민문방구 상품페이지에 들어갔을때 상품 사진과 상세설명 기본정보 등 모두 데이터에 넣어야 하는건지 궁금했다. 그래서 처음에 사진 한장만 상세정보에 넣는 것으로 모델링을 했는데 그 부분은 사실 원래 정보를 하나하나 넣는것이 아니고, 백엔드에서 html파일을 통으로 가지고 있다가 요청이 오면 파일을 보내주는거라고 한다. 이부분은 2차 프로젝트때 진행하기로 하고, 이번 프로젝트에서는 상세페이지 부분을 그냥 빼버리고 프론트엔드에서 아무 화면을 그냥 띄우는걸로 진행하기로 했다.
- is_best는 원래 판매순 등으로 해야 하지만 이번에는 그냥 직접 boolean으로 베스트 상품인지 아닌지 지정해서 하는걸로 진행하기로 했다.
products - product_images
- 배민문방구에 미리보기 이미지가 상품별로 두개씩만 있어서 처음에 products에 image1,2컬럼을 넣었었는데 확장성을 고려해서 이미지 테이블을 따로 분리했다. 지금 두장밖에 없다고 이미지를 products의 컬럼으로 넣어버리면 나중에 이미지를 늘리고 싶을때 어쩔 수 없이 products에 컬럼을 추가해야하고 그로 인해 계속 기술부채가 쌓이기 때문이다. (처음에 products테이블에 이미지를 넣어버리고 리뷰를 받으면서 확장성을 고려한 데이터 모델링의 중요성을 다시 짚고 넘어갈 수 있어서 오히려 좋았다) 테이블을 분리하면 이미지를 추가하고 삭제하는데 좀더 자유롭다. 외래키인 상품 아이디로 엮기만 하면 되니까.
products - products_options - options
- 처음에 모든 상품마다 각각의 옵션이 있는줄 알고(겹치는 옵션이 없는줄 알고) 테이블을 상품-옵션 일대다로 만들었다가 홈페이지를 잘 살펴보니 겹치는 옵션도 있어서 다대다 관계로 다시 고쳐 중간테이블을 만들었다.
- 상품의 재고는 상품-옵션의 짝 별로 관리하기 때문에 관계 테이블에 stock컬럼을 추가했다.
- 재고수량을 나타내는 컬럼의 이름은 quantity보다 stock으로 하는것이 알맞다
- 상품과 옵션은 장고의 ManyToManyField를 이용해서 만들고, through 옵션으로 관계테이블을 따로 지정하고, related_name을 이용해서 매니저클래스 이름을 지정해주었다.
class Product(TimeStampModel):
.
.
class Option(models.Model):
name = models.CharField(max_length=50)
products = models.ManyToManyField('Product', through='ProductOption', related_name='options')
class Meta:
db_table = 'options'
class ProductOption(models.Model):
stock = models.IntegerField()
product = models.ForeignKey('Product', on_delete=models.CASCADE)
option = models.ForeignKey('Option', on_delete=models.CASCADE)
class Meta:
db_table = 'products_options'
3. orders app
carts
- 장바구니에도 상품-옵션별로 담아야 한다. 그래서 product_id가 아니라 product_option_id를 참조한다. 상품 가격은 상품에서 가져올 수 있고, 수량만 있으면 총 금액을 계산할 수 있다.
orders
- 주문번호는 회원에게 보이는 난수
- 유저와 주문상태를 참조한다.
- 유저가 탈퇴해도 주문정보를 남겨놓기 위해서 on_delete=models.SET_NULL속성을 지정했다.
- null=True속성은 마이그레이션을 앱별로 나눠서 하다보니 외래키에 기본값이 없으면 안되길래 넣어준 속성이다.
class Order(TimeStampModel):
.
.
user = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True)
.
order_items
- 어떤 주문의 아이템인지 알기 위해 orders 참조
- 여기도 그냥 상품아이디가 아닌 상품-옵션 아이디가 필요하다.
- 한 주문에서도 상품이 부분적으로 처리될 수 있기 때문에 이 테이블에도 status가 필요하다. 부분배송 부분환불 등
- 오더부분이 어려웠는데 이 테이블을 따로 분리해서 생각해보니 이해가 갔다.
반응형
'wecode' 카테고리의 다른 글
구방문방구 | 메인페이지, 상품상세페이지 api (0) | 2022.07.30 |
---|---|
구방문방구 | 전체/카테고리별 상품리스트 api (0) | 2022.07.30 |
[Mission 6] 로그인 JWT 발급 (0) | 2022.07.17 |
[Mission 5] 회원가입 비밀번호 암호화 적용 (0) | 2022.07.17 |
[Mission 4] 로그인 기능 구현 (0) | 2022.07.16 |