Open kakasoo opened 1 year ago
정규화에 대한 설명은 하지 않을 거고요, 여기서는 간단하게 하나의 원칙만 설명하겠습니다.
더 많은 쪽이 더 적은 쪽의 ID를 가진다
.
커머스를 봐요, 판매자는 여러 개의 상품을 업로드하고 판매할 겁니다.
만약 더 적은 쪽인 판매자가 상품의 아이디를 가진다면 아래처럼 판매자의 정보가 계속 중복
될 수 밖에 없을 겁니다.
그러면 이건 굉장히 비효율적인 구조가 되기 때문에 차라리 반대로 아이디를 주는 게 더 낫다는 겁니다.
판매자의 아이디 | 판매자의 이름 | 상품의 아이디 |
---|---|---|
1 | 카카수 | 1 |
1 | 카카수 | 2 |
1 | 카카수 | 3 |
설명을 다시 하죠, 일단 구매자에 대한 거는 일절 생각하지 맙시다.
판매자
는 다수의 상품
을 가진다, 따라서 상품
은 판매자의 아이디
를 가져야 한다.
상품
은 다수의 상품 옵션
을 가진다, 따라서 상품 옵션
은 상품의 아이디
를 가져야 한다.
상품 옵션
은 다수의 입력 옵션
을 가진다, 따라서 입력 옵션
은 상품 옵션의 아이디
를 가진다.상품
은 다수의 상품 선택 옵션
을 가진다, 따라서 상품 선택 옵션
은 상품의 아이디
를 가져야 한다.모든 걸 1:M의 계층 구조를 가지면 마치 트리와 같은 모습이 될 겁니다.
하지만 위 설명에는 하나가 빠져 있는데 바로 묶음 배송
입니다.
왜냐하면 커머스에는 배송비가 무료가 되는 조건이 있는데요, 그걸 표현하기 위해서는 판매자가 묶음 배송
을 먼저 만들어야 합니다.
같은 박스에 담아서 배송할 수 있는 상품을 기준으로 한다고 생각하면 이해가 쉬운데요, 이 묶음 배송이 다시 상품을 여러 개 가지게 해야 합니다.
다시 설명하면 아래와 같은 구조가 될 겁니다.
판매자
는 다수의 묶음 상품
을 가진다, 따라서 묶음 상품
은 판매자의 아이디
를 가져야 한다.
묶음 상품
은 다수의 상품
을 가진다, 따라서 상품
은 묶음 상품의 아이디
를 가져야 한다. (가운데에 새로 추가된 테이블)
상품
은 다수의 상품 옵션
을 가진다, 따라서 상품 옵션
은 상품의 아이디
를 가져야 한다.
상품 옵션
은 다수의 입력 옵션
을 가진다, 따라서 입력 옵션
은 상품 옵션의 아이디
를 가진다.상품
은 다수의 상품 선택 옵션
을 가진다, 따라서 상품 선택 옵션
은 상품의 아이디
를 가져야 한다.다시 말하지만, 묶음 배송은 판매자가 배송비를 1번만 부과한다던가 하는, 배송비 기준을 정하기 위한 테이블입니다.
추가로 상품 옵션은 신발을 구매할 때 '신발끈'처럼 상품에서 선택적으로 구매 가능한 대상이며 절대 별도로 구매할 수 없는 것을 말합니다.
간단하지만 어쨌든 물건을 담고 전시할 수 있는 판매자의 도메인이 완성되었습니다. 이 구조가 이해되었다면 다음은 간단합니다.
구매자 도메인은 판매자 도메인에 있던 6개의 테이블, 그리고 그 트리 구조를 그대로 복사해서 옆에 그려주면 됩니다.
판매자
는 구매자
로묶음 배송
은 장바구니 묶음
으로상품
은 장바구니
로상품 옵션
은 장바구니 옵션
으로상품 선택옵션
은 장바구니 선택 옵션
으로입력 옵션
은 장바구니 입력 옵션
으로그대로 옮기고 아이디들도 다시 엮어주기만 하면 됩니다. 하지만 구매자 도메인에서는 가격을 계산하기 위한 각종 칼럼이 없습니다. 왜 일까요?
장바구니 옵션, 장바구니 선택 옵션 등에는 가격 칼럼이 없습니다.
구매자가 장바구니에 물건을 담았다고 해도 판매자가 가격을 조정했다면 거기에 따라가야 합니다.
아직 구매를 한 건 아니기 때문에 구매자 도메인의 가격 관련 칼럼들은 모두 판매자 쪽으로부터 와야 합니다.
반대로 새로 추가되는 칼럼이 있는데 장바구니에 담은 상품의 수, 즉 수량에 대한 칼럼이 필요합니다.
주문도 똑같이 트리 구조를 그대로 옮겨보면 됩니다.
판매자 | 구매자 | 영수증 |
---|---|---|
묶음 배송 | 장바구니 묶음 | 박스 단위 묶음 |
상품 | 장바구니 상품 | 결제된 상품 |
상품 옵션 | 장바구니 상품 옵션 | 결제된 상품 옵션 |
상품 선택 옵션 | 장바구니 상품 선택 옵션 | 결제된 상품 선택 옵션 |
상품 입력 옵션 | 장바구니 상품 입력 옵션 | 결제된 상품 입력 옵션 |
다만 추가된 게 있다면, 이번에는 결제가 된 것이기 때문에 결제된 당시의 가격과 수량
을 저장해야 합니다.
판매자가 가격을 바꾸든 말든, 배송비 부과 정책을 바꾸든 말든, 이미 확정된 금액은 그대로여야 하고 취소와 환불도 당시의 가격대로 이루어져야 하기 때문입니다.
다시 강조하건대, 이번에는 바뀌면 안 되기 때문에 저장
하는 겁니다.
해당 상품이 없다고 가정하고 전체 가격을 다시 계산하면 됩니다. 이 때, 재계산은 주문 결제 도메인의 정보로만 이루어져야 하고 다른 테이블을 사용해서는 안 됩니다. 그렇게 해서 전체 계산을 다시 했다면, 이전과 이후의 가격을 빼는 것만으로 환불 금액, 취소 금액을 구할 수 있습니다.
그런 건 아니고, 쿠팡처럼 유저의 등급, 회원에 따라 또 가격이 달라지는 경우가 있습니다. 이 또한 배송비를 일괄적으로 무료로 하는 경우도 있지만, 퍼센테이지를 달리 주는 등 차등이 있을 수 있습니다.
결제된 상품
?주문서 쪽의 결제된 상품
이 상품 아이디
를 가지는 거는 이해가 될 거에요.
상품
이 여러 번 결제될 수 있기 때문에, 상품
은 여러 개의 결제된 상품
을 가지고, 그러면 당연히 상품의 아이디를 필요로 하겠죠?
그런데 장바구니 상품
이 여러 개의 결제된 상품
을 가진다고 한다면 그건 좀 이상해요.
결제가 여러 번 될 수 있는 장바구니가 있나?
놀랍게도 있습니다.
이 상품을 계속 장바구니에 담아 둘까요?
: B2B 식자재 유통 서비스를 하는 마켓보로는 어차피 동일 구매자가 동일 상품을 반복적으로 삽니다.정기 결제
: 한 번 담아둔 것을 지속적으로 산다면 그 정기결제들이 어떤 장바구니로부터 비롯되는지 궁금할 수도 있겠죠?설계는 항상 비즈니스적인 관점이 고려가 되어야 해요.
데이터가 많은 테이블은 수정 자체만으로도 많은 리소스를 쓰기 때문에, 설계에 많은 시간을 써야 하고, 당연히 그 기준은 비즈니스가 됩니다.
때로는 설계에 미친 개발자가 있을 것이고, 당장 급한 것만 처리하자고 말하는 개발자가 있을 텐데요 둘 다 공통점이 있습니다.
바로 돈
입니다.
정상적인 개발자라면 돈에 대한 고민을 안할 수가 없고, 따라서 서로 다른 의견을 말하는 두 개발자는 같은 얘기를 하고 있을 겁니다.
개발자끼리 말이 다를 때는 어느 것이 돈을 더 버는지로 얘기하면 됩니다.
제가 가르치는 다른 분인데 참고하세요!
https://github.com/rimo030/Repo-Server/issues/14#issuecomment-1793816598 참고해서 ERD 생성했습니다.
필수옵션
에서 활성화상태
는 어떤 것을 의미하나요?판매자id
만 number
타입인 이유가 있을까요?판매자이름
은 왜 varchar(128)
로 설정했나요? (이름이면 더 작게 해도 되지 않을까요)제가 한 게 아니라 모르겠습니다. @rimo030 님이 답변해주시겠죠. :)
여기서 설명하는 커머스 ERD는 가장 대표적인 커머스들의 공통 특징을 묶으려고 하는 것이고, 디테일한 부분까지 챙기지는 못합니다. 하지만 대충 판매자, 구매자, 주문결제에 대한 각각 6개씩의 테이블만 트리로 잘 만들어둬도 웬만한 커머스 구조를 모방할 수 있습니다.
재밌는 점은 판매자, 구매자, 주문결제로 이어지는 이 세 가지 트리의 구조가 서로 똑같고, 시간 순으로 진행된다는 점
이에요. 그럼 한 번 시작해보죠.