템플릿 둘러보기/이커머스 플랫폼

이커머스 플랫폼

온라인 쇼핑몰 및 마켓플레이스를 위해 설계된 포괄적인 이커머스 데이터베이스 스키마입니다. 카테고리가 포함된 상품 카탈로그, 고객 관리, 주문 처리 및 품목 관리, 결제 추적, 배송지 주소 및 재고 관리를 포함합니다. Shopify와 같은 플랫폼, WooCommerce 대안 또는 맞춤형 이커머스 솔루션 구축에 적합합니다.

PostgreSQL12개 테이블이커머스
e-commerceshoppingpaymentsordersproducts
ERD Studio로 제작

이 스키마에 대하여

이 스키마의 중심은 카탈로그와 주문 사이의 선입니다. products·categories·product_images 는 지금 팔고 있는 것을 설명하고 가게를 손질할 때마다 바뀝니다. orders·order_items 는 실제로 팔린 것을 기록하며, 카탈로그가 바뀌어도 바뀌면 안 됩니다.

그래서 order_items 가 product_name 과 unit_price 를 자기 것으로 들고 있습니다. 집계를 위해 상품을 여전히 가리키지만, 영수증에 적힌 이름과 값은 결제 시점에 뜬 사본입니다. 상품 이름을 바꾸거나 할인을 걸어도 지난달 주문은 그대로 읽힙니다.

장바구니는 그 선 앞에 있고 일부러 느슨합니다. carts 에 user_id 와 session_id 가 둘 다 있어서 로그인 전에도 담을 수 있고, expires_at 이 있어 버려진 장바구니가 끝없이 쌓이지 않습니다. 장바구니의 무엇도 약속이 아닙니다 — 재고를 잡아 두지 않고 값은 products 에서 그때그때 읽습니다.

눈여겨볼 관계

orders → order_items
품목마다 한 행, 각자 quantity·unit_price·line_total 을 들고 있습니다. 주문의 합계는 청구된 그대로의 합이지 오늘 값으로 다시 구한 것이 아닙니다.
order_items → products
무엇이 얼마나 팔렸는지 집계하려고 남겨 둡니다. 이름과 값은 사본이고요. 포인터는 "어느 상품"에, 사본은 "얼마였나"에 답합니다.
categories → categories
parent_id 로 분류 트리를 자기참조로 풉니다. 말단과 최상위가 같은 모양의 행이고, 깊이를 스키마가 고정하지 않습니다.
carts → users / session_id
둘 중 하나가 장바구니의 주인입니다. 비회원은 session_id, 로그인하면 user_id 이고, 로그인 시 앞의 것을 뒤의 것에 합칩니다.
orders → coupons
쿠폰을 주문에 적고, 실제로 깎인 금액은 discount_amount 에 둡니다. 쿠폰 규칙을 나중에 고쳐도 지난 주문의 청구액은 그대로입니다.

설계 판단

주문 품목이 이름과 값을 복사한다

대안은 지난 주문을 그릴 때 products 와 조인하는 것인데, 그러면 가게를 손댈 때마다 영수증이 바뀝니다 — 이름 변경, 가격 인하, 옵션 삭제. 복사는 품목당 컬럼 두 개가 들고, 주문이 스스로 완결됩니다. 외래키는 집계용으로 남겨 두므로 둘 다 가져서 잃는 것이 없습니다.

재고는 상품의 컬럼 하나

stock_quantity 가 입출고 원장이 아니라 숫자 하나입니다. 창고를 겸하지 않는 가게에는 맞는 선택입니다 — 읽기가 상수 시간이고 차감이 갱신 한 번입니다. "왜 지금 이 수량인가" 를 알아야 하는 순간 — 반품, 파손, 입고 — 부족해지고, 그때 이 컬럼은 진실이 아니라 이동 테이블에 대한 캐시가 됩니다.

쿠폰 사용은 카운터

max_uses 에 대한 coupons.current_uses 는 가장 단순한 형태이고, 약점도 분명합니다 — 같은 순간의 두 결제가 같은 수를 읽을 수 있습니다. 한도를 절대 넘으면 안 된다면 쿠폰 행을 잠그는 트랜잭션 안에서 검사하거나, 사용 이력을 사용자별 유니크 제약이 걸린 자기 테이블로 빼야 합니다.

주문이 주소 행을 가리킨다

shipping_address_id 가 addresses 를 참조하므로, 고객이 주소를 고치면 지난 기록이 바뀝니다. 카탈로그에서 얻은 교훈이 여기만 적용되지 않은 자리입니다 — 배송하는 가게라면 값을 복사하듯 주소도 주문에 복사하세요. 흔한 출발 형태라 그대로 두었지만, 가장 먼저 손봐야 할 곳이라는 것은 알고 계시는 편이 좋습니다.

이 템플릿의 테이블

users사용자
8 cols
addresses주소
10 cols
categories카테고리
6 cols
products상품
10 cols
product_images상품 이미지
7 cols
carts장바구니
6 cols
cart_items장바구니 항목
5 cols
orders주문
12 cols
order_items주문 항목
7 cols
payments결제
8 cols
reviews리뷰
8 cols
coupons쿠폰
10 cols

자주 묻는 것

order_items 에 상품 이름을 왜 또 저장하나요?
상품 이름이 바뀌거나 할인되거나 삭제된 뒤에도 지난 주문이 제대로 읽히게 하려고요. 영수증은 일어난 일의 기록인데, 오늘의 카탈로그로 조립되는 것은 기록이 아닙니다.
사이즈·색상 같은 옵션은 어디에 넣나요?
이 형태에는 자리가 없습니다. 값과 재고를 들고 있는 product_variants 를 만들고 order_items·cart_items 가 상품 대신 옵션을 가리키게 해야 합니다. 나중에 끼워 넣는 것보다 초기에 하는 편이 훨씬 쌉니다 — 재고와 가격이 지나는 모든 길이 옮겨지기 때문입니다.
장바구니가 재고를 잡아 둬야 하나요?
여기서는 아니고, 작은 가게라면 대개 안 하는 편이 낫습니다. 잡아 두려면 만료가 있는 예약과 그것을 푸는 작업이 필요하고, 버려진 장바구니가 아무도 못 사는 재고가 됩니다. 결제 시점에 확인하고 거기서 실패시키는 쪽이 단순하고 대부분의 가게가 그렇게 합니다.
carts 에 expires_at 을 왜 두나요?
비회원 장바구니는 치워 줄 주인이 없어서 트래픽만큼 쌓입니다. 이 컬럼이 있으면 무엇이 오래됐는지 짐작하지 않고 지울 수 있고, 다시 찾아온 비회원이 장바구니를 얼마나 오래 유지할지도 같은 값으로 정해집니다.
환불은 어디로 가나요?
모델링돼 있지 않습니다. payments 는 들어온 돈을 적습니다. 환불은 나가는 돈이라 자기 행이 필요하고, 보통 같은 테이블에 부호나 타입을 두고 되돌리는 결제를 가리키게 합니다. 결제에 환불 여부 플래그만 두면 부분 환불과 환불 시점을 잃습니다.