쇼핑몰 DB 설계 완벽 가이드: ERD부터 구현까지 단계별 튜토리얼
쇼핑몰 데이터베이스를 처음부터 설계하는 완벽한 가이드. 회원, 상품, 주문 시스템의 ERD 작성부터 MySQL 구현까지 단계별로 설명합니다.
럿지 AI 팀
6분 읽기
목차
쇼핑몰 DB 설계 완벽 가이드
개요
이 가이드는 김영한의 실전 데이터베이스 강의의 쇼핑몰 프로젝트 패턴을 기반으로 작성되었습니다.
**목표:**
완전한 쇼핑몰 DB를 3단계 설계 프로세스로 구축
1단계: 요구사항 분석
핵심 기능
**회원 관리:**
- 회원 가입/로그인
- 프로필 관리
- 배송지 관리
**상품 관리:**
- 상품 등록
- 카테고리 분류
- 재고 관리
**주문 관리:**
- 장바구니
- 주문/결제
- 주문 상태 추적
- 배송 관리
2단계: 개념적 설계 (ERD)
핵심 엔티티
**1. 회원 (Member)**
- 회원 ID
- 이메일
- 비밀번호
- 이름
- 전화번호
**2. 배송지 (Address)**
- 배송지 ID
- 회원 ID (FK)
- 주소
- 기본 배송지 여부
**3. 상품 (Product)**
- 상품 ID
- 상품명
- 가격
- 재고
**4. 카테고리 (Category)**
- 카테고리 ID
- 카테고리명
- 상위 카테고리 (자기참조)
**5. 주문 (Order)**
- 주문 ID
- 회원 ID (FK)
- 주문 일시
- 주문 상태
**6. 주문 상세 (OrderItem)**
- 주문 상세 ID
- 주문 ID (FK)
- 상품 ID (FK)
- 수량
- 가격
**7. 배송 (Delivery)**
- 배송 ID
- 주문 ID (FK)
- 배송지 ID (FK)
- 배송 상태
관계 정의
**회원 ↔ 배송지:**
1:N (한 회원이 여러 배송지)
**상품 ↔ 카테고리:**
N:M → 중간 테이블 (ProductCategory)
**회원 ↔ 주문:**
1:N (한 회원이 여러 주문)
**주문 ↔ 주문 상세:**
1:N (한 주문에 여러 상품)
**주문 ↔ 배송:**
1:1 (한 주문에 한 배송)
3단계: 논리적 설계 (정규화)
정규화 적용
**1NF: 원자값**
- 모든 속성이 원자값
- 주소를 우편번호/기본주소/상세주소로 분리
**2NF: 부분 함수 종속 제거**
- 주문 상세에서 상품 정보 분리
- 상품 테이블 참조
**3NF: 이행 함수 종속 제거**
- 배송 정보를 주문과 분리
- 배송 테이블 별도 생성
결과
**회원 (members)**
``
- id (PK)
- email (UNIQUE)
- password
- name
- phone
`
**배송지 (addresses)**
`
- id (PK)
- member_id (FK)
- postal_code
- address1
- address2
- is_default
`
**상품 (products)**
`
- id (PK)
- name
- price
- stock
- description
`
**카테고리 (categories)**
`
- id (PK)
- name
- parent_id (FK, self-reference)
`
**상품-카테고리 (product_categories)**
`
- product_id (PK, FK)
- category_id (PK, FK)
`
**주문 (orders)**
`
- id (PK)
- member_id (FK)
- order_date
- status (ENUM)
- total_amount
`
**주문 상세 (order_items)**
`
- id (PK)
- order_id (FK)
- product_id (FK)
- quantity
- price
`
**배송 (deliveries)**
`
- id (PK)
- order_id (FK)
- address_id (FK)
- status (ENUM)
- tracking_number
`
4단계: 물리적 설계 (MySQL)
테이블 생성
`sql
-- 회원
CREATE TABLE members (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
name VARCHAR(50) NOT NULL,
phone VARCHAR(20),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 배송지
CREATE TABLE addresses (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
member_id BIGINT NOT NULL,
postal_code VARCHAR(10),
address1 VARCHAR(255) NOT NULL,
address2 VARCHAR(255),
is_default BOOLEAN DEFAULT FALSE,
FOREIGN KEY (member_id) REFERENCES members(id) ON DELETE CASCADE,
INDEX idx_member_id (member_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 카테고리
CREATE TABLE categories (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
parent_id BIGINT,
FOREIGN KEY (parent_id) REFERENCES categories(id),
INDEX idx_parent_id (parent_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 상품
CREATE TABLE products (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
description TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_name (name),
FULLTEXT INDEX idx_search (name, description)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 상품-카테고리
CREATE TABLE product_categories (
product_id BIGINT NOT NULL,
category_id BIGINT NOT NULL,
PRIMARY KEY (product_id, category_id),
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 주문
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
member_id BIGINT NOT NULL,
order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
status ENUM('PENDING', 'PAID', 'SHIPPED', 'DELIVERED', 'CANCELLED') DEFAULT 'PENDING',
total_amount DECIMAL(10,2) NOT NULL CHECK (total_amount >= 0),
FOREIGN KEY (member_id) REFERENCES members(id),
INDEX idx_member_id (member_id),
INDEX idx_order_date (order_date),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 주문 상세
CREATE TABLE order_items (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
order_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL CHECK (quantity > 0),
price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id),
INDEX idx_order_id (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 배송
CREATE TABLE deliveries (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
order_id BIGINT NOT NULL UNIQUE,
address_id BIGINT NOT NULL,
status ENUM('PREPARING', 'SHIPPED', 'DELIVERED') DEFAULT 'PREPARING',
tracking_number VARCHAR(50),
delivered_at DATETIME,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (address_id) REFERENCES addresses(id),
INDEX idx_tracking (tracking_number)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`
인덱스 전략
**회원 조회 (이메일 로그인):**
`sql
INDEX idx_email (email)
`
**주문 조회 (회원별, 날짜별):**
`sql
INDEX idx_member_id (member_id)
INDEX idx_order_date (order_date)
`
**상품 검색:**
`sql
FULLTEXT INDEX idx_search (name, description)
`
5단계: 샘플 데이터 삽입
`sql
-- 회원
INSERT INTO members (email, password, name, phone) VALUES
('user1@example.com', 'hashed_pw', '홍길동', '010-1234-5678'),
('user2@example.com', 'hashed_pw', '김철수', '010-2345-6789');
-- 카테고리
INSERT INTO categories (name, parent_id) VALUES
('전자제품', NULL),
('노트북', 1),
('스마트폰', 1);
-- 상품
INSERT INTO products (name, price, stock, description) VALUES
('MacBook Pro', 2500000, 10, 'M3 칩셋 탑재'),
('iPhone 15', 1200000, 20, '최신 스마트폰');
-- 상품-카테고리 연결
INSERT INTO product_categories (product_id, category_id) VALUES
(1, 2), -- MacBook → 노트북
(2, 3); -- iPhone → 스마트폰
`
6단계: 활용 쿼리
주문 생성 (트랜잭션)
`sql
START TRANSACTION;
-- 1. 주문 생성
INSERT INTO orders (member_id, total_amount)
VALUES (1, 2500000);
SET @order_id = LAST_INSERT_ID();
-- 2. 주문 상세
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES (@order_id, 1, 1, 2500000);
-- 3. 재고 차감
UPDATE products
SET stock = stock - 1
WHERE id = 1 AND stock >= 1;
-- 4. 배송 정보
INSERT INTO deliveries (order_id, address_id)
VALUES (@order_id, 1);
COMMIT;
`
회원별 주문 내역
`sql
SELECT
o.id AS order_id,
o.order_date,
o.status,
o.total_amount,
GROUP_CONCAT(p.name) AS products
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.member_id = 1
GROUP BY o.id
ORDER BY o.order_date DESC;
`
핵심 포인트
1. 정규화
**3NF 적용:**
- 배송 정보를 주문과 분리
- 데이터 중복 제거
2. 제약 조건
**CHECK:**
`sql
CHECK (price >= 0)
CHECK (stock >= 0)
``**FOREIGN KEY:**
CASCADE 옵션으로 참조 무결성
3. 인덱스
**자주 조회되는 컬럼:**
- email, member_id, order_date
4. 트랜잭션
**주문 프로세스:**
모두 성공 or 모두 실패
더 배우기
이 튜토리얼은 기본 구조만 다룹니다.
**심화 학습:**
김영한의 실전 데이터베이스
- 파티셔닝
- 성능 최적화
- 복잡한 쿼리
---
**태그**: #쇼핑몰DB #ERD #DB설계 #튜토리얼 #MySQL
L
럿지 AI 팀
AI 기술과 비즈니스 혁신을 선도하는 럿지 AI의 콘텐츠 팀입니다.
관련 포스트
비즈니스
탄탄한 DB 아키텍처로 시리즈 B 200억 투자 유치 성공
VC들이 가장 주목한 기술 경쟁력, 확장 가능한 DB 설계로 기업 가치 3배 인정받은 스타트업 사례입니다.
•3분 읽기
비즈니스
DB 설계로 스타트업 스케일링 성공: 유저 10배 증가 무장애 운영
처음부터 제대로 된 DB 설계로 유저 100만 돌파까지 리팩토링 없이 성공한 스타트업 사례입니다.
•4분 읽기
비즈니스
DB 최적화로 연간 서버 비용 12억 절감: 클라우드 비용 80% 감소
올바른 DB 설계와 최적화로 AWS 비용을 80% 절감하고 연간 12억을 아낀 중견 기업 사례입니다.
•4분 읽기