์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ›’ ์‡ผํ•‘๋ชฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ์™„๋ฒฝ ๊ฐ€์ด๋“œ: ์ƒํ’ˆยท์ฃผ๋ฌธยทํšŒ์› ๊ด€๋ฆฌ ๊ตฌ์กฐ์˜ ๋ชจ๋“  ๊ฒƒ

๐Ÿ›’ ์‡ผํ•‘๋ชฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ์™„๋ฒฝ ๊ฐ€์ด๋“œ: ์ƒํ’ˆยท์ฃผ๋ฌธยทํšŒ์› ๊ด€๋ฆฌ ๊ตฌ์กฐ์˜ ๋ชจ๋“  ๊ฒƒ

์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์จ๋จน๋Š” ์‡ผํ•‘๋ชฐ DB ์„ค๊ณ„ ๋…ธํ•˜์šฐ ๐Ÿ’ก

Shopping Mall Database ํšŒ์› ์ƒํ’ˆ ์ฃผ๋ฌธ DATABASE

๐ŸŽฏ ์‡ผํ•‘๋ชฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์™œ ์ค‘์š”ํ• ๊นŒ?

์•ˆ๋…•! ์˜ค๋Š˜์€ ์‡ผํ•‘๋ชฐ์„ ๋งŒ๋“ค ๋•Œ ๊ฐ€์žฅ ํ•ต์‹ฌ์ด ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณผ๊ฒŒ. ๐Ÿ˜Š

์‡ผํ•‘๋ชฐ์„ ์šด์˜ํ•œ๋‹ค๋Š” ๊ฑด ๋‹จ์ˆœํžˆ ์˜ˆ์œ ํ™”๋ฉด๋งŒ ๋งŒ๋“œ๋Š” ๊ฒŒ ์•„๋‹ˆ์•ผ. ์ˆ˜๋งŽ์€ ์ƒํ’ˆ ์ •๋ณด, ํšŒ์›๋“ค์˜ ๊ฐœ์ธ์ •๋ณด, ๋งค์ผ๋งค์ผ ์Œ“์ด๋Š” ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ... ์ด ๋ชจ๋“  ๊ฑธ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ๋‚˜์ค‘์— ์ •๋ง ํฐ์ผ ๋‚˜๊ฑฐ๋“ ! ๐Ÿ”ฅ

์‹ค์ œ๋กœ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ์ฒ˜์Œ์—” "๋Œ€์ถฉ ํ…Œ์ด๋ธ” ๋ช‡ ๊ฐœ ๋งŒ๋“ค๋ฉด ๋˜๊ฒ ์ง€?"๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค๊ฐ€, ๋‚˜์ค‘์— ๋ฐ์ดํ„ฐ๊ฐ€ ๊ผฌ์ด๊ณ  ์„ฑ๋Šฅ์ด ๋–จ์–ด์ ธ์„œ ์ „์ฒด๋ฅผ ๋‹ค์‹œ ์„ค๊ณ„ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ •๋ง ๋งŽ์•„. ๋‚˜๋„ ์˜ˆ์ „์— ๊ทธ๋Ÿฐ ๊ฒฝํ—˜์ด ์žˆ์–ด์„œ... ๐Ÿ˜…

๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‡ผํ•‘๋ชฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ๋ฐฉ๋ฒ•์„ ์นœ๊ตฌํ•œํ…Œ ์„ค๋ช…ํ•˜๋“ฏ์ด ์‰ฝ๊ฒŒ ํ’€์–ด๋ณผ๊ฒŒ!

๐Ÿ’ก ์ด ๊ธ€์—์„œ ๋‹ค๋ฃฐ ๋‚ด์šฉ
  • โœ… ์‡ผํ•‘๋ชฐ DB์˜ ํ•ต์‹ฌ 3๋Œ€ ์š”์†Œ (ํšŒ์›, ์ƒํ’ˆ, ์ฃผ๋ฌธ)
  • โœ… ์‹ค๋ฌด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ…Œ์ด๋ธ” ๊ตฌ์กฐ์™€ ๊ด€๊ณ„ ์„ค์ •
  • โœ… ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ ์ธ๋ฑ์Šค ์ „๋žต
  • โœ… ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์„ค๊ณ„ ํŒจํ„ด
  • โœ… ์‹ค์ œ SQL ์˜ˆ์ œ ์ฝ”๋“œ

๐Ÿ—๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„์˜ ๊ธฐ๋ณธ ์›์น™

๋ณธ๊ฒฉ์ ์œผ๋กœ ์‡ผํ•‘๋ชฐ DB๋ฅผ ์„ค๊ณ„ํ•˜๊ธฐ ์ „์—, ๋จผ์ € ์•Œ์•„์•ผ ํ•  ๊ธฐ๋ณธ ์›์น™๋“ค์ด ์žˆ์–ด. ์ด๊ฑธ ๋ชจ๋ฅด๊ณ  ์‹œ์ž‘ํ•˜๋ฉด ๋‚˜์ค‘์— ๊ผญ ํ›„ํšŒํ•˜๊ฒŒ ๋˜๊ฑฐ๋“ ! ๐Ÿ˜ฑ

๐Ÿ“Œ ์ •๊ทœํ™”(Normalization)์˜ ์ดํ•ด

์ •๊ทœํ™”๋ผ๋Š” ๋ง ๋“ค์–ด๋ดค์ง€? ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด ๋ฐ์ดํ„ฐ ์ค‘๋ณต์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๊ฑฐ์•ผ.

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ. ๋งŒ์•ฝ ์ฃผ๋ฌธ ํ…Œ์ด๋ธ”์— ๊ณ ๊ฐ ์ด๋ฆ„, ์ „ํ™”๋ฒˆํ˜ธ, ์ฃผ์†Œ๋ฅผ ๋ชจ๋‘ ์ €์žฅํ•œ๋‹ค๋ฉด?
๊ฐ™์€ ๊ณ ๊ฐ์ด 10๋ฒˆ ์ฃผ๋ฌธํ•˜๋ฉด ๋™์ผํ•œ ์ •๋ณด๊ฐ€ 10๋ฒˆ ์ค‘๋ณต ์ €์žฅ๋˜๋Š” ๊ฑฐ์ง€. ๊ทธ๋ฆฌ๊ณ  ๊ณ ๊ฐ์ด ์ „ํ™”๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๊พธ๋ฉด? 10๊ฐœ์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ์ˆ˜์ •ํ•ด์•ผ ํ•ด! ๐Ÿ˜ฐ

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ํšŒ์› ์ •๋ณด๋Š” ํšŒ์› ํ…Œ์ด๋ธ”์—, ์ฃผ๋ฌธ ์ •๋ณด๋Š” ์ฃผ๋ฌธ ํ…Œ์ด๋ธ”์— ๋ถ„๋ฆฌํ•ด์„œ ์ €์žฅํ•˜๊ณ , ๋‘˜์„ ์™ธ๋ž˜ํ‚ค(Foreign Key)๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฑฐ์•ผ.

๐ŸŽ“ ์ •๊ทœํ™”์˜ ๋‹จ๊ณ„

1์ฐจ ์ •๊ทœํ™” (1NF)
- ๋ชจ๋“  ์†์„ฑ์€ ์›์ž๊ฐ’(Atomic Value)์„ ๊ฐ€์ ธ์•ผ ํ•จ
- ๋ฐ˜๋ณต๋˜๋Š” ๊ทธ๋ฃน์ด ์—†์–ด์•ผ ํ•จ

2์ฐจ ์ •๊ทœํ™” (2NF)
- 1NF๋ฅผ ๋งŒ์กฑํ•˜๋ฉด์„œ
- ๋ถ€๋ถ„ ํ•จ์ˆ˜ ์ข…์†์„ฑ์„ ์ œ๊ฑฐ (๊ธฐ๋ณธํ‚ค์˜ ์ผ๋ถ€์—๋งŒ ์ข…์†๋˜๋Š” ์†์„ฑ ์ œ๊ฑฐ)

3์ฐจ ์ •๊ทœํ™” (3NF)
- 2NF๋ฅผ ๋งŒ์กฑํ•˜๋ฉด์„œ
- ์ดํ–‰์  ํ•จ์ˆ˜ ์ข…์†์„ฑ์„ ์ œ๊ฑฐ (๊ธฐ๋ณธํ‚ค๊ฐ€ ์•„๋‹Œ ์†์„ฑ์— ์ข…์†๋˜๋Š” ์†์„ฑ ์ œ๊ฑฐ)

์‹ค๋ฌด์—์„œ๋Š” ๋ณดํ†ต 3์ฐจ ์ •๊ทœํ™”๊นŒ์ง€๋งŒ ์ ์šฉํ•ด. ๊ทธ ์ด์ƒ์€ ์˜คํžˆ๋ ค ๋ณต์žก๋„๋งŒ ๋†’์•„์งˆ ์ˆ˜ ์žˆ๊ฑฐ๋“ ! ๐Ÿค”
๐Ÿ“Œ ์—ญ์ •๊ทœํ™”(Denormalization)๋Š” ์–ธ์ œ?

"์–ด? ๊ทธ๋Ÿผ ๋ฌด์กฐ๊ฑด ์ •๊ทœํ™”๊ฐ€ ์ข‹์€ ๊ฑฐ ์•„๋‹ˆ์•ผ?"๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ผญ ๊ทธ๋ ‡์ง€๋งŒ์€ ์•Š์•„! ๐Ÿ˜…

์ •๊ทœํ™”๋ฅผ ๋„ˆ๋ฌด ๋งŽ์ด ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์„ JOINํ•ด์•ผ ํ•ด์„œ ์„ฑ๋Šฅ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์–ด. ํŠนํžˆ ์กฐํšŒ๊ฐ€ ๋งŽ์€ ์‡ผํ•‘๋ชฐ์—์„œ๋Š” ์น˜๋ช…์ ์ด์ง€!

๊ทธ๋ž˜์„œ ์‹ค๋ฌด์—์„œ๋Š” ์ ์ ˆํ•œ ์—ญ์ •๊ทœํ™”๋ฅผ ํ†ตํ•ด ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•ด. ์˜ˆ๋ฅผ ๋“ค๋ฉด:

๐Ÿ’ก ์—ญ์ •๊ทœํ™” ์˜ˆ์‹œ

โ€ข ์ฃผ๋ฌธ ํ…Œ์ด๋ธ”์— ์ƒํ’ˆ๋ช…์„ ์ค‘๋ณต ์ €์žฅ (์ƒํ’ˆ๋ช…์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ๊ณผ๊ฑฐ ์ฃผ๋ฌธ ๋‚ด์—ญ์€ ์œ ์ง€)
โ€ข ํ†ต๊ณ„์šฉ ์ง‘๊ณ„ ํ…Œ์ด๋ธ” ๋ณ„๋„ ์ƒ์„ฑ (๋งค๋ฒˆ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๊ณ  ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ๋œ ๊ฐ’ ์ €์žฅ)
โ€ข ์ž์ฃผ ์กฐํšŒ๋˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์บ์‹œ ํ…Œ์ด๋ธ”๋กœ ๋ถ„๋ฆฌ

๐Ÿ‘ฅ ํšŒ์› ๊ด€๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‡ผํ•‘๋ชฐ์˜ ํ•ต์‹ฌ ํ…Œ์ด๋ธ”๋“ค์„ ์„ค๊ณ„ํ•ด๋ณด์ž! ๋จผ์ € ํšŒ์› ๊ด€๋ฆฌ๋ถ€ํ„ฐ ์‹œ์ž‘ํ• ๊ฒŒ. ๐Ÿš€

๐Ÿ“Œ ํšŒ์›(Members) ํ…Œ์ด๋ธ” ๊ธฐ๋ณธ ๊ตฌ์กฐ

ํšŒ์› ํ…Œ์ด๋ธ”์€ ์‡ผํ•‘๋ชฐ์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ด ๋˜๋Š” ํ…Œ์ด๋ธ”์ด์•ผ. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฑด ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ์„ ๊ณ ๋ คํ•˜๋Š” ๊ฑฐ์•ผ!

CREATE TABLE members (
    member_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    name VARCHAR(50) NOT NULL,
    phone VARCHAR(20),
    birth_date DATE,
    gender ENUM('M', 'F', 'O') DEFAULT 'O',
    member_grade ENUM('BRONZE', 'SILVER', 'GOLD', 'VIP') DEFAULT 'BRONZE',
    total_purchase_amount DECIMAL(15,2) DEFAULT 0,
    point_balance INT DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    email_verified BOOLEAN DEFAULT FALSE,
    marketing_agreed BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    last_login_at TIMESTAMP NULL,
    INDEX idx_email (email),
    INDEX idx_phone (phone),
    INDEX idx_member_grade (member_grade),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
โš ๏ธ ๋ณด์•ˆ ์ฃผ์˜์‚ฌํ•ญ

โ€ข ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ ˆ๋Œ€ ํ‰๋ฌธ์œผ๋กœ ์ €์žฅํ•˜๋ฉด ์•ˆ ๋ผ! bcrypt, Argon2 ๊ฐ™์€ ํ•ด์‹œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‚ฌ์šฉ ํ•„์ˆ˜
โ€ข ๊ฐœ์ธ์ •๋ณด๋Š” ์•”ํ˜ธํ™” ์ €์žฅ ๊ณ ๋ ค (ํŠนํžˆ ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ, ๊ณ„์ขŒ๋ฒˆํ˜ธ ๋“ฑ)
โ€ข GDPR, ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ• ์ค€์ˆ˜๋ฅผ ์œ„ํ•œ ๋™์˜ ์—ฌ๋ถ€ ํ•„๋“œ ํ•„์ˆ˜
๐Ÿ“Œ ํšŒ์› ์ฃผ์†Œ(Member_Addresses) ํ…Œ์ด๋ธ”

ํ•œ ํšŒ์›์ด ์—ฌ๋Ÿฌ ๋ฐฐ์†ก์ง€๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ, ์ฃผ์†Œ๋Š” ๋ณ„๋„ ํ…Œ์ด๋ธ”๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒŒ ์ข‹์•„! ๐Ÿ“ฆ

CREATE TABLE member_addresses (
    address_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    member_id BIGINT NOT NULL,
    address_name VARCHAR(50) NOT NULL COMMENT '๋ฐฐ์†ก์ง€๋ช… (์˜ˆ: ์ง‘, ํšŒ์‚ฌ)',
    recipient_name VARCHAR(50) NOT NULL,
    recipient_phone VARCHAR(20) NOT NULL,
    postal_code VARCHAR(10) NOT NULL,
    address_line1 VARCHAR(200) NOT NULL COMMENT '๊ธฐ๋ณธ์ฃผ์†Œ',
    address_line2 VARCHAR(200) COMMENT '์ƒ์„ธ์ฃผ์†Œ',
    is_default BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (member_id) REFERENCES members(member_id) ON DELETE CASCADE,
    INDEX idx_member_id (member_id),
    INDEX idx_is_default (member_id, is_default)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

์—ฌ๊ธฐ์„œ ON DELETE CASCADE๋ฅผ ์‚ฌ์šฉํ•œ ์ด์œ ๋Š” ํšŒ์›์ด ํƒˆํ‡ดํ•˜๋ฉด ๊ด€๋ จ ์ฃผ์†Œ๋„ ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ์•ผ. ํ•˜์ง€๋งŒ ์‹ค๋ฌด์—์„œ๋Š” ํšŒ์› ํƒˆํ‡ด ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๋กœ ์‚ญ์ œํ•˜์ง€ ์•Š๊ณ  ์†Œํ”„ํŠธ ์‚ญ์ œ(Soft Delete) ๋ฐฉ์‹์„ ๋งŽ์ด ์‚ฌ์šฉํ•ด!

๐Ÿ“Œ ํšŒ์› ๋“ฑ๊ธ‰ ๊ด€๋ฆฌ ์ „๋žต

์‡ผํ•‘๋ชฐ์—์„œ ํšŒ์› ๋“ฑ๊ธ‰์€ ์ •๋ง ์ค‘์š”ํ•ด! ๋“ฑ๊ธ‰์— ๋”ฐ๋ผ ํ• ์ธ์œจ, ์ ๋ฆฝ๋ฅ , ํ˜œํƒ์ด ๋‹ฌ๋ผ์ง€๋‹ˆ๊นŒ. ๐ŸŽ

๊ฐ„๋‹จํ•œ ์‡ผํ•‘๋ชฐ์ด๋ผ๋ฉด members ํ…Œ์ด๋ธ”์˜ member_grade ์ปฌ๋Ÿผ์œผ๋กœ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ, ๋ณต์žกํ•œ ๋“ฑ๊ธ‰ ์‹œ์Šคํ…œ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ณ„๋„ ํ…Œ์ด๋ธ”๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ์ข‹์•„:

CREATE TABLE member_grades (
    grade_id INT PRIMARY KEY AUTO_INCREMENT,
    grade_name VARCHAR(20) NOT NULL,
    min_purchase_amount DECIMAL(15,2) NOT NULL COMMENT '๋“ฑ๊ธ‰ ๋‹ฌ์„ฑ ์ตœ์†Œ ๊ตฌ๋งค์•ก',
    discount_rate DECIMAL(5,2) DEFAULT 0 COMMENT 'ํ• ์ธ์œจ (%)',
    point_rate DECIMAL(5,2) DEFAULT 1 COMMENT '์ ๋ฆฝ๋ฅ  (%)',
    free_shipping_threshold DECIMAL(10,2) COMMENT '๋ฌด๋ฃŒ๋ฐฐ์†ก ๊ธฐ์ค€๊ธˆ์•ก',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ๊ธฐ๋ณธ ๋“ฑ๊ธ‰ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
INSERT INTO member_grades (grade_name, min_purchase_amount, discount_rate, point_rate) VALUES
('BRONZE', 0, 0, 1),
('SILVER', 100000, 3, 1.5),
('GOLD', 500000, 5, 2),
('VIP', 1000000, 10, 3);

๐Ÿ›๏ธ ์ƒํ’ˆ ๊ด€๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„

์ด์ œ ์‡ผํ•‘๋ชฐ์˜ ๊ฝƒ! ์ƒํ’ˆ ๊ด€๋ฆฌ ๋ถ€๋ถ„์„ ์„ค๊ณ„ํ•ด๋ณผ๊ฒŒ. ์ƒํ’ˆ DB๋Š” ์ƒ๊ฐ๋ณด๋‹ค ๋ณต์žกํ•ด์„œ ์‹ ์ค‘ํ•˜๊ฒŒ ์„ค๊ณ„ํ•ด์•ผ ํ•ด! ๐ŸŽจ

๐Ÿ“Œ ์นดํ…Œ๊ณ ๋ฆฌ(Categories) ํ…Œ์ด๋ธ”

์ƒํ’ˆ์„ ๋ถ„๋ฅ˜ํ•˜๊ธฐ ์œ„ํ•œ ์นดํ…Œ๊ณ ๋ฆฌ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ณด์ž. ์นดํ…Œ๊ณ ๋ฆฌ๋Š” ๋ณดํ†ต ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€์ง€? (์˜ˆ: ์˜๋ฅ˜ > ๋‚จ์„ฑ์˜๋ฅ˜ > ์…”์ธ )

CREATE TABLE categories (
    category_id INT PRIMARY KEY AUTO_INCREMENT,
    parent_category_id INT NULL COMMENT '์ƒ์œ„ ์นดํ…Œ๊ณ ๋ฆฌ ID',
    category_name VARCHAR(50) NOT NULL,
    category_level TINYINT NOT NULL COMMENT '์นดํ…Œ๊ณ ๋ฆฌ ๊นŠ์ด (1: ๋Œ€๋ถ„๋ฅ˜, 2: ์ค‘๋ถ„๋ฅ˜, 3: ์†Œ๋ถ„๋ฅ˜)',
    display_order INT DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (parent_category_id) REFERENCES categories(category_id),
    INDEX idx_parent (parent_category_id),
    INDEX idx_level (category_level),
    INDEX idx_display_order (display_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
๐Ÿ’ก ์นดํ…Œ๊ณ ๋ฆฌ ์„ค๊ณ„ ํŒ

โ€ข ๊นŠ์ด๋Š” 3~4๋‹จ๊ณ„๋ฅผ ๋„˜์ง€ ์•Š๋Š” ๊ฒŒ ์ข‹์•„ (์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ณ ๋ ค)
โ€ข display_order๋กœ ๊ด€๋ฆฌ์ž๊ฐ€ ์›ํ•˜๋Š” ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌ ๊ฐ€๋Šฅ
โ€ข ์นดํ…Œ๊ณ ๋ฆฌ ์‚ญ์ œ ์‹œ ํ•˜์œ„ ์นดํ…Œ๊ณ ๋ฆฌ์™€ ์ƒํ’ˆ ์ฒ˜๋ฆฌ ์ •์ฑ… ๋ฏธ๋ฆฌ ์ •์˜ ํ•„์š”
๐Ÿ“Œ ์ƒํ’ˆ(Products) ํ…Œ์ด๋ธ”

๋“œ๋””์–ด ๋ฉ”์ธ ์ด๋ฒคํŠธ! ์ƒํ’ˆ ํ…Œ์ด๋ธ”์ด์•ผ. ์—ฌ๊ธฐ๊ฐ€ ์ œ์ผ ์ค‘์š”ํ•˜๋‹ˆ๊นŒ ์ง‘์ค‘! ๐ŸŽฏ

CREATE TABLE products (
    product_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    category_id INT NOT NULL,
    product_code VARCHAR(50) UNIQUE NOT NULL COMMENT '์ƒํ’ˆ ๊ณ ์œ  ์ฝ”๋“œ',
    product_name VARCHAR(200) NOT NULL,
    description TEXT,
    price DECIMAL(10,2) NOT NULL,
    discount_price DECIMAL(10,2) COMMENT 'ํ• ์ธ๊ฐ€',
    cost_price DECIMAL(10,2) COMMENT '์›๊ฐ€ (๊ด€๋ฆฌ์šฉ)',
    stock_quantity INT DEFAULT 0,
    min_order_quantity INT DEFAULT 1,
    max_order_quantity INT DEFAULT 999,
    weight DECIMAL(8,2) COMMENT '๋ฌด๊ฒŒ(kg)',
    manufacturer VARCHAR(100) COMMENT '์ œ์กฐ์‚ฌ',
    brand VARCHAR(100) COMMENT '๋ธŒ๋žœ๋“œ',
    origin_country VARCHAR(50) COMMENT '์›์‚ฐ์ง€',
    is_display BOOLEAN DEFAULT TRUE,
    is_soldout BOOLEAN DEFAULT FALSE,
    view_count INT DEFAULT 0,
    sales_count INT DEFAULT 0,
    rating_average DECIMAL(3,2) DEFAULT 0,
    review_count INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id) REFERENCES categories(category_id),
    INDEX idx_category (category_id),
    INDEX idx_product_code (product_code),
    INDEX idx_price (price),
    INDEX idx_created_at (created_at),
    INDEX idx_sales_count (sales_count),
    INDEX idx_rating (rating_average),
    FULLTEXT INDEX ft_idx_name_desc (product_name, description)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

์—ฌ๊ธฐ์„œ ์ฃผ๋ชฉํ•  ์ ! ๐Ÿ‘€

1. FULLTEXT INDEX
์ƒํ’ˆ๋ช…๊ณผ ์„ค๋ช…์œผ๋กœ ๊ฒ€์ƒ‰ํ•  ๋•Œ ์„ฑ๋Šฅ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์ „๋ฌธ ๊ฒ€์ƒ‰ ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ–ˆ์–ด. ํ•˜์ง€๋งŒ ํ•œ๊ธ€ ๊ฒ€์ƒ‰์€ MySQL 5.7.6 ์ด์ƒ์—์„œ๋งŒ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋‹ˆ ์ฃผ์˜!

2. ์žฌ๊ณ  ๊ด€๋ฆฌ
stock_quantity๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณ€ํ•˜๋Š” ๊ฐ’์ด๋ผ ๋™์‹œ์„ฑ ์ œ์–ด๊ฐ€ ์ค‘์š”ํ•ด. ๋‚˜์ค‘์— ์ฃผ๋ฌธ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ž์„ธํžˆ ๋‹ค๋ฃฐ๊ฒŒ!

3. ํ†ต๊ณ„ ์ •๋ณด
view_count, sales_count, rating_average ๊ฐ™์€ ์ง‘๊ณ„ ๋ฐ์ดํ„ฐ๋Š” ์—ญ์ •๊ทœํ™”์˜ ์ข‹์€ ์˜ˆ์‹œ์•ผ. ๋งค๋ฒˆ ๊ณ„์‚ฐํ•˜๋ฉด ๋А๋ฆฌ๋‹ˆ๊นŒ ๋ฏธ๋ฆฌ ์ €์žฅํ•ด๋‘๋Š” ๊ฑฐ์ง€! ๐Ÿ“Š

๐Ÿ“Œ ์ƒํ’ˆ ์˜ต์…˜(Product_Options) ํ…Œ์ด๋ธ”

์˜ท์ด๋ผ๋ฉด ์‚ฌ์ด์ฆˆ์™€ ์ƒ‰์ƒ, ์ „์ž์ œํ’ˆ์ด๋ผ๋ฉด ์šฉ๋Ÿ‰๊ณผ ์ƒ‰์ƒ... ์ด๋Ÿฐ ์˜ต์…˜๋“ค์„ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ• ๊นŒ? ๐Ÿค”

์˜ต์…˜ ๊ด€๋ฆฌ๋Š” ์ •๋ง ๊นŒ๋‹ค๋กœ์šด ๋ถ€๋ถ„์ด์•ผ. ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์ด ์žˆ๋Š”๋ฐ, ๊ฐ€์žฅ ์œ ์—ฐํ•œ ๋ฐฉ์‹์„ ์†Œ๊ฐœํ• ๊ฒŒ:

-- ์˜ต์…˜ ๊ทธ๋ฃน ํ…Œ์ด๋ธ” (์˜ˆ: ์ƒ‰์ƒ, ์‚ฌ์ด์ฆˆ)
CREATE TABLE product_option_groups (
    option_group_id INT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT NOT NULL,
    group_name VARCHAR(50) NOT NULL COMMENT '์˜ต์…˜๋ช… (์˜ˆ: ์ƒ‰์ƒ, ์‚ฌ์ด์ฆˆ)',
    display_order INT DEFAULT 0,
    FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE,
    INDEX idx_product (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ์˜ต์…˜ ๊ฐ’ ํ…Œ์ด๋ธ” (์˜ˆ: ๋นจ๊ฐ•, ํŒŒ๋ž‘ / S, M, L)
CREATE TABLE product_option_values (
    option_value_id INT PRIMARY KEY AUTO_INCREMENT,
    option_group_id INT NOT NULL,
    value_name VARCHAR(50) NOT NULL,
    additional_price DECIMAL(10,2) DEFAULT 0 COMMENT '์ถ”๊ฐ€ ๊ธˆ์•ก',
    display_order INT DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    FOREIGN KEY (option_group_id) REFERENCES product_option_groups(option_group_id) ON DELETE CASCADE,
    INDEX idx_option_group (option_group_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ์˜ต์…˜ ์กฐํ•ฉ๋ณ„ ์žฌ๊ณ  ๊ด€๋ฆฌ (์˜ˆ: ๋นจ๊ฐ•-S, ๋นจ๊ฐ•-M, ํŒŒ๋ž‘-S...)
CREATE TABLE product_option_stocks (
    stock_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT NOT NULL,
    option_combination VARCHAR(200) NOT NULL COMMENT '์˜ต์…˜ ์กฐํ•ฉ (JSON ๋˜๋Š” ๊ตฌ๋ถ„์ž)',
    sku VARCHAR(100) UNIQUE COMMENT 'Stock Keeping Unit',
    stock_quantity INT DEFAULT 0,
    additional_price DECIMAL(10,2) DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE,
    INDEX idx_product (product_id),
    INDEX idx_sku (sku)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
๐ŸŽจ ์˜ต์…˜ ์„ค๊ณ„ ์˜ˆ์‹œ

ํ‹ฐ์…”์ธ ๋ฅผ ํŒ๋งคํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž:

product_option_groups
- ๊ทธ๋ฃน1: ์ƒ‰์ƒ
- ๊ทธ๋ฃน2: ์‚ฌ์ด์ฆˆ

product_option_values
- ์ƒ‰์ƒ: ๋นจ๊ฐ•, ํŒŒ๋ž‘, ๊ฒ€์ •
- ์‚ฌ์ด์ฆˆ: S, M, L, XL

product_option_stocks
- ๋นจ๊ฐ•-S: ์žฌ๊ณ  10๊ฐœ
- ๋นจ๊ฐ•-M: ์žฌ๊ณ  15๊ฐœ
- ํŒŒ๋ž‘-S: ์žฌ๊ณ  5๊ฐœ
- ... (์ด 12๊ฐœ ์กฐํ•ฉ)
๐Ÿ“Œ ์ƒํ’ˆ ์ด๋ฏธ์ง€(Product_Images) ํ…Œ์ด๋ธ”

์‡ผํ•‘๋ชฐ์—์„œ ์ด๋ฏธ์ง€๋Š” ์ •๋ง ์ค‘์š”ํ•˜์ง€! ํ•œ ์ƒํ’ˆ์— ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€๊ฐ€ ํ•„์š”ํ•˜๋‹ˆ๊นŒ ๋ณ„๋„ ํ…Œ์ด๋ธ”๋กœ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด. ๐Ÿ“ธ

CREATE TABLE product_images (
    image_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT NOT NULL,
    image_url VARCHAR(500) NOT NULL,
    thumbnail_url VARCHAR(500),
    image_type ENUM('MAIN', 'SUB', 'DETAIL') DEFAULT 'SUB',
    display_order INT DEFAULT 0,
    alt_text VARCHAR(200) COMMENT 'SEO์šฉ ๋Œ€์ฒด ํ…์ŠคํŠธ',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE,
    INDEX idx_product (product_id),
    INDEX idx_type (product_id, image_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
๐Ÿ’ก ์ด๋ฏธ์ง€ ๊ด€๋ฆฌ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

โ€ข ์›๋ณธ ์ด๋ฏธ์ง€๋Š” CDN์— ์ €์žฅํ•˜๊ณ  URL๋งŒ DB์— ์ €์žฅ
โ€ข ์ธ๋„ค์ผ์€ ์ž๋™ ์ƒ์„ฑํ•˜์—ฌ ๋ณ„๋„ ์ €์žฅ (๋กœ๋”ฉ ์†๋„ ๊ฐœ์„ )
โ€ข ์ด๋ฏธ์ง€ ํŒŒ์ผ๋ช…์€ UUID ๋“ฑ์œผ๋กœ ์ค‘๋ณต ๋ฐฉ์ง€
โ€ข WebP ํฌ๋งท ์‚ฌ์šฉ ๊ณ ๋ ค (์šฉ๋Ÿ‰ 30% ๊ฐ์†Œ)

๐Ÿ“ฆ ์ฃผ๋ฌธ ๊ด€๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„

์ž, ์ด์ œ ๊ฐ€์žฅ ๋ณต์žกํ•˜์ง€๋งŒ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ฃผ๋ฌธ ๊ด€๋ฆฌ ๋ถ€๋ถ„์ด์•ผ! ์—ฌ๊ธฐ์„œ ์‹ค์ˆ˜ํ•˜๋ฉด ๋ˆ์ด ์™”๋‹ค๊ฐ”๋‹คํ•˜๋Š” ๊ฑฐ๋ผ ์ •๋ง ์กฐ์‹ฌํ•ด์•ผ ํ•ด! ๐Ÿ’ฐ

๐Ÿ“Œ ์ฃผ๋ฌธ(Orders) ํ…Œ์ด๋ธ”

์ฃผ๋ฌธ ํ…Œ์ด๋ธ”์€ ์ฃผ๋ฌธ์˜ ํ—ค๋” ์ •๋ณด๋ฅผ ๋‹ด๋Š” ๊ฑฐ์•ผ. ๋ˆ„๊ฐ€, ์–ธ์ œ, ์–ผ๋งˆ์— ์ฃผ๋ฌธํ–ˆ๋Š”์ง€ ๊ฐ™์€ ๊ธฐ๋ณธ ์ •๋ณด๋“ค!

CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_number VARCHAR(50) UNIQUE NOT NULL COMMENT '์ฃผ๋ฌธ๋ฒˆํ˜ธ (๊ณ ๊ฐ์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š”)',
    member_id BIGINT NOT NULL,
    order_status ENUM('PENDING', 'PAID', 'PREPARING', 'SHIPPING', 'DELIVERED', 'CANCELLED', 'REFUNDED') DEFAULT 'PENDING',
    payment_method ENUM('CARD', 'BANK_TRANSFER', 'VIRTUAL_ACCOUNT', 'MOBILE', 'POINT') NOT NULL,
    payment_status ENUM('PENDING', 'COMPLETED', 'FAILED', 'CANCELLED') DEFAULT 'PENDING',
    
    -- ๊ธˆ์•ก ์ •๋ณด
    total_product_price DECIMAL(12,2) NOT NULL COMMENT '์ƒํ’ˆ ์ด์•ก',
    total_discount_amount DECIMAL(12,2) DEFAULT 0 COMMENT 'ํ• ์ธ ์ด์•ก',
    delivery_fee DECIMAL(8,2) DEFAULT 0 COMMENT '๋ฐฐ์†ก๋น„',
    total_payment_amount DECIMAL(12,2) NOT NULL COMMENT '์ตœ์ข… ๊ฒฐ์ œ ๊ธˆ์•ก',
    used_point INT DEFAULT 0 COMMENT '์‚ฌ์šฉ ํฌ์ธํŠธ',
    earned_point INT DEFAULT 0 COMMENT '์ ๋ฆฝ ํฌ์ธํŠธ',
    
    -- ๋ฐฐ์†ก ์ •๋ณด
    recipient_name VARCHAR(50) NOT NULL,
    recipient_phone VARCHAR(20) NOT NULL,
    postal_code VARCHAR(10) NOT NULL,
    address_line1 VARCHAR(200) NOT NULL,
    address_line2 VARCHAR(200),
    delivery_message TEXT COMMENT '๋ฐฐ์†ก ๋ฉ”์‹œ์ง€',
    
    -- ๊ธฐํƒ€
    order_memo TEXT COMMENT '์ฃผ๋ฌธ ๋ฉ”๋ชจ',
    admin_memo TEXT COMMENT '๊ด€๋ฆฌ์ž ๋ฉ”๋ชจ',
    cancelled_at TIMESTAMP NULL,
    cancel_reason TEXT,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    FOREIGN KEY (member_id) REFERENCES members(member_id),
    INDEX idx_member (member_id),
    INDEX idx_order_number (order_number),
    INDEX idx_status (order_status),
    INDEX idx_created_at (created_at),
    INDEX idx_payment_status (payment_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
โš ๏ธ ์ฃผ๋ฌธ๋ฒˆํ˜ธ ์ƒ์„ฑ ์ฃผ์˜์‚ฌํ•ญ

order_id๋Š” ๋‚ด๋ถ€์šฉ ์ž๋™์ฆ๊ฐ€ ๊ฐ’์ด๊ณ , order_number๋Š” ๊ณ ๊ฐ์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ์ฃผ๋ฌธ๋ฒˆํ˜ธ์•ผ!

์ฃผ๋ฌธ๋ฒˆํ˜ธ ์ƒ์„ฑ ์˜ˆ์‹œ: ORD20240115001234 (์ฃผ๋ฌธ์ผ์ž + ์‹œํ€€์Šค)
โ€ข ์ถ”์ธก ๊ฐ€๋Šฅํ•œ ํŒจํ„ด์€ ํ”ผํ•  ๊ฒƒ (๋ณด์•ˆ)
โ€ข ์ค‘๋ณต ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ UNIQUE ์ œ์•ฝ ํ•„์ˆ˜
โ€ข ๊ณ ๊ฐ ๋ฌธ์˜ ์‹œ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ๊ฐ„๊ฒฐํ•˜๊ฒŒ
๐Ÿ“Œ ์ฃผ๋ฌธ ์ƒ์„ธ(Order_Items) ํ…Œ์ด๋ธ”

ํ•œ ์ฃผ๋ฌธ์—๋Š” ์—ฌ๋Ÿฌ ์ƒํ’ˆ์ด ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ์ง€? ๊ทธ๋ž˜์„œ ์ฃผ๋ฌธ ์ƒ์„ธ ํ…Œ์ด๋ธ”์ด ํ•„์š”ํ•ด! ๐Ÿ›’

CREATE TABLE order_items (
    order_item_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    product_name VARCHAR(200) NOT NULL COMMENT '์ฃผ๋ฌธ ๋‹น์‹œ ์ƒํ’ˆ๋ช… (๋ณ€๊ฒฝ ๋Œ€๋น„)',
    product_code VARCHAR(50) NOT NULL,
    option_info VARCHAR(500) COMMENT '์„ ํƒํ•œ ์˜ต์…˜ ์ •๋ณด (JSON)',
    sku VARCHAR(100) COMMENT 'SKU',
    
    quantity INT NOT NULL,
    unit_price DECIMAL(10,2) NOT NULL COMMENT '๋‹จ๊ฐ€',
    discount_amount DECIMAL(10,2) DEFAULT 0 COMMENT '์ƒํ’ˆ๋ณ„ ํ• ์ธ์•ก',
    total_price DECIMAL(12,2) NOT NULL COMMENT '์†Œ๊ณ„ (๋‹จ๊ฐ€ * ์ˆ˜๋Ÿ‰ - ํ• ์ธ)',
    
    item_status ENUM('PENDING', 'CONFIRMED', 'PREPARING', 'SHIPPING', 'DELIVERED', 'CANCELLED', 'REFUNDED') DEFAULT 'PENDING',
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(product_id),
    INDEX idx_order (order_id),
    INDEX idx_product (product_id),
    INDEX idx_status (item_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ํฌ์ธํŠธ! ๐ŸŽฏ

์™œ product_name์„ ์ค‘๋ณต ์ €์žฅํ• ๊นŒ?
์ƒํ’ˆ๋ช…์ด ๋‚˜์ค‘์— ๋ณ€๊ฒฝ๋˜์–ด๋„ ์ฃผ๋ฌธ ๋‚ด์—ญ์€ ์ฃผ๋ฌธ ๋‹น์‹œ์˜ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์•ผ! ์ด๊ฒŒ ๋ฐ”๋กœ ์—ญ์ •๊ทœํ™”์˜ ์‹ค์šฉ์ ์ธ ์˜ˆ์‹œ์ง€. ๐Ÿ˜Š

์˜ต์…˜ ์ •๋ณด๋Š” ์–ด๋–ป๊ฒŒ ์ €์žฅํ• ๊นŒ?
option_info ์ปฌ๋Ÿผ์— JSON ํ˜•ํƒœ๋กœ ์ €์žฅํ•˜๋Š” ๊ฒŒ ํŽธํ•ด. ์˜ˆ๋ฅผ ๋“ค๋ฉด:
{"์ƒ‰์ƒ": "๋นจ๊ฐ•", "์‚ฌ์ด์ฆˆ": "M"}

๐Ÿ“Œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ(Cart) ํ…Œ์ด๋ธ”

์ฃผ๋ฌธ ์ „์— ์žฅ๋ฐ”๊ตฌ๋‹ˆ๊ฐ€ ์žˆ์–ด์•ผ๊ฒ ์ง€? ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋Š” ์ž„์‹œ ๋ฐ์ดํ„ฐ๋ผ์„œ ์„ค๊ณ„๊ฐ€ ์ข€ ๋‹ฌ๋ผ! ๐Ÿ›’

CREATE TABLE cart_items (
    cart_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    member_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    option_info VARCHAR(500) COMMENT '์„ ํƒํ•œ ์˜ต์…˜ (JSON)',
    sku VARCHAR(100),
    quantity INT NOT NULL DEFAULT 1,
    is_selected BOOLEAN DEFAULT TRUE COMMENT '์ฃผ๋ฌธ ์‹œ ์„ ํƒ ์—ฌ๋ถ€',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    FOREIGN KEY (member_id) REFERENCES members(member_id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE,
    INDEX idx_member (member_id),
    INDEX idx_product (product_id),
    UNIQUE KEY unique_cart_item (member_id, product_id, sku)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
๐Ÿ’ก ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๊ด€๋ฆฌ ํŒ

โ€ข ๋น„ํšŒ์› ์žฅ๋ฐ”๊ตฌ๋‹ˆ๋Š” ์„ธ์…˜์ด๋‚˜ ์ฟ ํ‚ค๋กœ ๊ด€๋ฆฌ (๋กœ๊ทธ์ธ ์‹œ DB๋กœ ์ด์ „)
โ€ข ์˜ค๋ž˜๋œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์•„์ดํ…œ์€ ์ฃผ๊ธฐ์ ์œผ๋กœ ์‚ญ์ œ (์˜ˆ: 30์ผ ์ด์ƒ)
โ€ข ์žฌ๊ณ  ๋ถ€์กฑ ์‹œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์—์„œ ์ž๋™ ์ œ๊ฑฐ ๋˜๋Š” ์•Œ๋ฆผ
โ€ข ๊ฐ€๊ฒฉ ๋ณ€๋™ ์‹œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์—์„œ ์ตœ์‹  ๊ฐ€๊ฒฉ์œผ๋กœ ํ‘œ์‹œ
๐Ÿ“Œ ๊ฒฐ์ œ ์ •๋ณด(Payments) ํ…Œ์ด๋ธ”

๊ฒฐ์ œ ์ •๋ณด๋Š” ์ •๋ง ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ผ์„œ ๋ณ„๋„๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ์ข‹์•„! ๐Ÿ’ณ

CREATE TABLE payments (
    payment_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id BIGINT NOT NULL,
    payment_method ENUM('CARD', 'BANK_TRANSFER', 'VIRTUAL_ACCOUNT', 'MOBILE', 'POINT') NOT NULL,
    payment_amount DECIMAL(12,2) NOT NULL,
    payment_status ENUM('PENDING', 'COMPLETED', 'FAILED', 'CANCELLED', 'REFUNDED') DEFAULT 'PENDING',
    
    -- PG์‚ฌ ์ •๋ณด
    pg_provider VARCHAR(50) COMMENT 'PG์‚ฌ๋ช… (์˜ˆ: ํ† ์ŠคํŽ˜์ด๋จผ์ธ , ๋‚˜์ด์ŠคํŽ˜์ด)',
    pg_transaction_id VARCHAR(100) COMMENT 'PG์‚ฌ ๊ฑฐ๋ž˜ ID',
    approval_number VARCHAR(50) COMMENT '์Šน์ธ๋ฒˆํ˜ธ',
    
    -- ์นด๋“œ ์ •๋ณด (๋งˆ์Šคํ‚น ์ฒ˜๋ฆฌ)
    card_company VARCHAR(50),
    card_number_masked VARCHAR(20) COMMENT '๋งˆ์Šคํ‚น๋œ ์นด๋“œ๋ฒˆํ˜ธ (์˜ˆ: 1234-****-****-5678)',
    installment_month TINYINT DEFAULT 0 COMMENT 'ํ• ๋ถ€ ๊ฐœ์›” (0: ์ผ์‹œ๋ถˆ)',
    
    -- ๊ฐ€์ƒ๊ณ„์ขŒ ์ •๋ณด
    virtual_account_bank VARCHAR(50),
    virtual_account_number VARCHAR(50),
    virtual_account_holder VARCHAR(50),
    virtual_account_due_date TIMESTAMP NULL,
    
    paid_at TIMESTAMP NULL,
    cancelled_at TIMESTAMP NULL,
    cancel_reason TEXT,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    INDEX idx_order (order_id),
    INDEX idx_pg_transaction (pg_transaction_id),
    INDEX idx_status (payment_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
โš ๏ธ ๊ฒฐ์ œ ์ •๋ณด ๋ณด์•ˆ ํ•„์ˆ˜์‚ฌํ•ญ

โ€ข ์นด๋“œ๋ฒˆํ˜ธ ์ „์ฒด๋Š” ์ ˆ๋Œ€ ์ €์žฅ ๊ธˆ์ง€! (PCI-DSS ์œ„๋ฐ˜)
โ€ข ๋งˆ์Šคํ‚น๋œ ์ •๋ณด๋งŒ ์ €์žฅ (์˜ˆ: 1234-****-****-5678)
โ€ข ์‹ค์ œ ๊ฒฐ์ œ๋Š” PG์‚ฌ API๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ
โ€ข ๊ฒฐ์ œ ๋กœ๊ทธ๋Š” ๋ณ„๋„ ์•”ํ˜ธํ™” ์ €์žฅ ๊ณ ๋ ค
โ€ข ๊ฐœ์ธ์ •๋ณด ๋ณด์œ  ๊ธฐ๊ฐ„ ์ค€์ˆ˜ (์ „์ž์ƒ๊ฑฐ๋ž˜๋ฒ•: 5๋…„)

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๊ฐ€ ๋๋‚ฌ๋‹ค๊ณ  ๋์ด ์•„๋‹ˆ์•ผ! ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ์„ฑ๋Šฅ์ด ์ •๋ง ์ค‘์š”ํ•˜๊ฑฐ๋“ . ๐Ÿš€

ํŠนํžˆ ์‡ผํ•‘๋ชฐ์€ ๋™์‹œ ์ ‘์†์ž๊ฐ€ ๋งŽ๊ณ , ์ฃผ๋ฌธ์ด ๋ชฐ๋ฆฌ๋Š” ์‹œ๊ฐ„๋Œ€๊ฐ€ ์žˆ์–ด์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ํ•„์ˆ˜์•ผ!

๐Ÿ“Œ ์ธ๋ฑ์Šค ์ „๋žต

์ธ๋ฑ์Šค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์„ฑ๋Šฅ์„ ์ขŒ์šฐํ•˜๋Š” ํ•ต์‹ฌ ์š”์†Œ์•ผ. ํ•˜์ง€๋งŒ ๋ฌด์ž‘์ • ๋งŽ์ด ๋งŒ๋“ค๋ฉด ์˜คํžˆ๋ ค ๋…์ด ๋  ์ˆ˜ ์žˆ์–ด! ๐ŸŽฏ

๐ŸŽ“ ์ธ๋ฑ์Šค ์ƒ์„ฑ ๊ธฐ์ค€

โœ… ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
โ€ข WHERE ์ ˆ์— ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ปฌ๋Ÿผ
โ€ข JOIN ์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์ปฌ๋Ÿผ
โ€ข ORDER BY, GROUP BY์— ์‚ฌ์šฉ๋˜๋Š” ์ปฌ๋Ÿผ
โ€ข ์นด๋””๋„๋ฆฌํ‹ฐ(๊ณ ์œ ๊ฐ’์˜ ๋น„์œจ)๊ฐ€ ๋†’์€ ์ปฌ๋Ÿผ

โŒ ์ธ๋ฑ์Šค๋ฅผ ํ”ผํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
โ€ข ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ(INSERT, UPDATE, DELETE)์ด ๋นˆ๋ฒˆํ•œ ์ปฌ๋Ÿผ
โ€ข ํ…Œ์ด๋ธ” ํฌ๊ธฐ๊ฐ€ ์ž‘์€ ๊ฒฝ์šฐ (์ˆ˜์ฒœ ๊ฑด ์ดํ•˜)
โ€ข ์นด๋””๋„๋ฆฌํ‹ฐ๊ฐ€ ๋‚ฎ์€ ์ปฌ๋Ÿผ (์˜ˆ: ์„ฑ๋ณ„, Boolean)
โ€ข ์ปฌ๋Ÿผ ๊ฐ’์˜ ๋Œ€๋ถ€๋ถ„์ด NULL์ธ ๊ฒฝ์šฐ
-- ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์˜ˆ์‹œ (์ฃผ๋ฌธ ์กฐํšŒ ์ตœ์ ํ™”)
CREATE INDEX idx_order_search 
ON orders(member_id, order_status, created_at);

-- ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์•„๋ž˜ ์ฟผ๋ฆฌ๋“ค์ด ๋ชจ๋‘ ์ธ๋ฑ์Šค๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด!
-- SELECT * FROM orders WHERE member_id = 123;
-- SELECT * FROM orders WHERE member_id = 123 AND order_status = 'PAID';
-- SELECT * FROM orders WHERE member_id = 123 AND order_status = 'PAID' 
--   ORDER BY created_at DESC;

-- ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค (์ฟผ๋ฆฌ์— ํ•„์š”ํ•œ ๋ชจ๋“  ์ปฌ๋Ÿผ์„ ์ธ๋ฑ์Šค์— ํฌํ•จ)
CREATE INDEX idx_product_list 
ON products(category_id, is_display, created_at, product_id, product_name, price);

-- ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ…Œ์ด๋ธ” ์ ‘๊ทผ ์—†์ด ์ธ๋ฑ์Šค๋งŒ์œผ๋กœ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ ๊ฐ€๋Šฅ!
-- SELECT product_id, product_name, price 
-- FROM products 
-- WHERE category_id = 10 AND is_display = TRUE 
-- ORDER BY created_at DESC;
๐Ÿ“Œ ์ฟผ๋ฆฌ ์ตœ์ ํ™” ๊ธฐ๋ฒ•

์ข‹์€ ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“ค์—ˆ์–ด๋„ ์ฟผ๋ฆฌ๋ฅผ ์ž˜๋ชป ์ž‘์„ฑํ•˜๋ฉด ์†Œ์šฉ์—†์–ด! ์‹ค๋ฌด์—์„œ ์ž์ฃผ ์“ฐ๋Š” ์ตœ์ ํ™” ๊ธฐ๋ฒ•๋“ค์„ ์•Œ๋ ค์ค„๊ฒŒ. ๐Ÿ’ช

-- โŒ ๋‚˜์œ ์˜ˆ: SELECT *๋Š” ํ”ผํ•˜์ž
SELECT * FROM products WHERE category_id = 10;

-- โœ… ์ข‹์€ ์˜ˆ: ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ์„ ํƒ
SELECT product_id, product_name, price, thumbnail_url 
FROM products 
WHERE category_id = 10;

-- โŒ ๋‚˜์œ ์˜ˆ: ํ•จ์ˆ˜ ์‚ฌ์šฉ์œผ๋กœ ์ธ๋ฑ์Šค ๋ฌดํšจํ™”
SELECT * FROM orders 
WHERE DATE(created_at) = '2024-01-15';

-- โœ… ์ข‹์€ ์˜ˆ: ๋ฒ”์œ„ ์กฐ๊ฑด์œผ๋กœ ๋ณ€๊ฒฝ
SELECT * FROM orders 
WHERE created_at >= '2024-01-15 00:00:00' 
  AND created_at < '2024-01-16 00:00:00';

-- โŒ ๋‚˜์œ ์˜ˆ: OR ์กฐ๊ฑด์€ ์ธ๋ฑ์Šค ํ™œ์šฉ์ด ์–ด๋ ค์›€
SELECT * FROM products 
WHERE category_id = 10 OR category_id = 20;

-- โœ… ์ข‹์€ ์˜ˆ: IN ์‚ฌ์šฉ
SELECT * FROM products 
WHERE category_id IN (10, 20);

-- โŒ ๋‚˜์œ ์˜ˆ: LIKE์˜ ์•ž์ชฝ ์™€์ผ๋“œ์นด๋“œ
SELECT * FROM products 
WHERE product_name LIKE '%์…”์ธ %';

-- โœ… ์ข‹์€ ์˜ˆ: ์ „๋ฌธ ๊ฒ€์ƒ‰ ์ธ๋ฑ์Šค ํ™œ์šฉ
SELECT * FROM products 
WHERE MATCH(product_name, description) AGAINST('์…”์ธ ' IN NATURAL LANGUAGE MODE);
๐Ÿ“Œ ์žฌ๊ณ  ๊ด€๋ฆฌ์˜ ๋™์‹œ์„ฑ ์ œ์–ด

์‡ผํ•‘๋ชฐ์—์„œ ๊ฐ€์žฅ ๊นŒ๋‹ค๋กœ์šด ๋ถ€๋ถ„์ด ๋ฐ”๋กœ ์žฌ๊ณ  ๊ด€๋ฆฌ์•ผ! ๐Ÿ˜ฐ

์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๊ณ ๊ฐ€ 1๊ฐœ ๋‚จ์€ ์ƒํ’ˆ์„ 100๋ช…์ด ๋™์‹œ์— ์ฃผ๋ฌธํ•˜๋ ค๊ณ  ํ•˜๋ฉด? ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์œผ๋ฉด ์žฌ๊ณ ๊ฐ€ ๋งˆ์ด๋„ˆ์Šค๊ฐ€ ๋  ์ˆ˜๋„ ์žˆ์–ด!

-- โŒ ๋‚˜์œ ์˜ˆ: Race Condition ๋ฐœ์ƒ ๊ฐ€๋Šฅ
-- 1. ์žฌ๊ณ  ํ™•์ธ
SELECT stock_quantity FROM products WHERE product_id = 123;
-- 2. ์žฌ๊ณ ๊ฐ€ ์ถฉ๋ถ„ํ•˜๋ฉด ์ฐจ๊ฐ
UPDATE products SET stock_quantity = stock_quantity - 1 
WHERE product_id = 123;

-- โœ… ์ข‹์€ ์˜ˆ 1: ์›์ž์  ์—…๋ฐ์ดํŠธ (Atomic Update)
UPDATE products 
SET stock_quantity = stock_quantity - 1 
WHERE product_id = 123 
  AND stock_quantity >= 1;

-- ์˜ํ–ฅ๋ฐ›์€ ํ–‰์ด 0์ด๋ฉด ์žฌ๊ณ  ๋ถ€์กฑ
-- ์˜ํ–ฅ๋ฐ›์€ ํ–‰์ด 1์ด๋ฉด ์„ฑ๊ณต

-- โœ… ์ข‹์€ ์˜ˆ 2: ๋น„๊ด€์  ์ž ๊ธˆ (Pessimistic Lock)
START TRANSACTION;

SELECT stock_quantity 
FROM products 
WHERE product_id = 123 
FOR UPDATE;  -- ํ–‰ ์ž ๊ธˆ

-- ์žฌ๊ณ  ํ™•์ธ ํ›„ ์ฐจ๊ฐ
UPDATE products 
SET stock_quantity = stock_quantity - 1 
WHERE product_id = 123;

COMMIT;

-- โœ… ์ข‹์€ ์˜ˆ 3: ๋‚™๊ด€์  ์ž ๊ธˆ (Optimistic Lock)
-- products ํ…Œ์ด๋ธ”์— version ์ปฌ๋Ÿผ ์ถ”๊ฐ€
ALTER TABLE products ADD COLUMN version INT DEFAULT 0;

-- ์—…๋ฐ์ดํŠธ ์‹œ version๋„ ํ•จ๊ป˜ ์ฒดํฌ
UPDATE products 
SET stock_quantity = stock_quantity - 1,
    version = version + 1
WHERE product_id = 123 
  AND version = 5  -- ์ฝ์—ˆ์„ ๋•Œ์˜ version
  AND stock_quantity >= 1;

-- ์˜ํ–ฅ๋ฐ›์€ ํ–‰์ด 0์ด๋ฉด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ๋จผ์ € ์ˆ˜์ •ํ•จ (์žฌ์‹œ๋„ ํ•„์š”)
๐Ÿ’ก ์‹ค๋ฌด ์žฌ๊ณ  ๊ด€๋ฆฌ ์ „๋žต

1. ์•ˆ์ „ ์žฌ๊ณ  ์„ค์ •
์‹ค์ œ ์žฌ๊ณ ๋ณด๋‹ค ์ ๊ฒŒ ํ‘œ์‹œ (์˜ˆ: ์‹ค์žฌ๊ณ  100๊ฐœ โ†’ ํŒ๋งค๊ฐ€๋Šฅ 95๊ฐœ)

2. ์žฌ๊ณ  ์˜ˆ์•ฝ ์‹œ์Šคํ…œ
์ฃผ๋ฌธ ์‹œ ์ฆ‰์‹œ ์ฐจ๊ฐํ•˜์ง€ ์•Š๊ณ  '์˜ˆ์•ฝ' ์ƒํƒœ๋กœ ๊ด€๋ฆฌ
๊ฒฐ์ œ ์™„๋ฃŒ ์‹œ ํ™•์ •, ๊ฒฐ์ œ ์‹คํŒจ ์‹œ ์˜ˆ์•ฝ ํ•ด์ œ

3. ์žฌ๊ณ  ๋กœ๊ทธ ๊ด€๋ฆฌ
๋ชจ๋“  ์žฌ๊ณ  ๋ณ€๋™ ์ด๋ ฅ์„ ๋ณ„๋„ ํ…Œ์ด๋ธ”์— ๊ธฐ๋ก
๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ถ”์  ๊ฐ€๋Šฅ

4. Redis ํ™œ์šฉ
์‹ค์‹œ๊ฐ„ ์žฌ๊ณ ๋Š” Redis์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์ฃผ๊ธฐ์ ์œผ๋กœ DB ๋™๊ธฐํ™”
-- ์žฌ๊ณ  ์ด๋ ฅ ํ…Œ์ด๋ธ”
CREATE TABLE stock_histories (
    history_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT NOT NULL,
    sku VARCHAR(100),
    change_type ENUM('IN', 'OUT', 'ADJUST', 'RESERVE', 'RELEASE') NOT NULL,
    change_quantity INT NOT NULL COMMENT '๋ณ€๋™๋Ÿ‰ (์–‘์ˆ˜: ์ฆ๊ฐ€, ์Œ์ˆ˜: ๊ฐ์†Œ)',
    before_quantity INT NOT NULL,
    after_quantity INT NOT NULL,
    reference_type VARCHAR(50) COMMENT '์ฐธ์กฐ ํƒ€์ž… (ORDER, RETURN, MANUAL)',
    reference_id BIGINT COMMENT '์ฐธ์กฐ ID',
    memo TEXT,
    created_by BIGINT COMMENT '์ฒ˜๋ฆฌ์ž ID',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    FOREIGN KEY (product_id) REFERENCES products(product_id),
    INDEX idx_product (product_id),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

๐ŸŽ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ ํ…Œ์ด๋ธ” ์„ค๊ณ„

๊ธฐ๋ณธ์ ์ธ ์‡ผํ•‘๋ชฐ ๊ธฐ๋Šฅ ์™ธ์—๋„ ์‹ค๋ฌด์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ๋“ค์ด ์žˆ์–ด. ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž! ๐ŸŒŸ

๐Ÿ“Œ ์ฟ ํฐ(Coupons) ์‹œ์Šคํ…œ

์ฟ ํฐ์€ ๋งˆ์ผ€ํŒ…์˜ ํ•ต์‹ฌ ๋„๊ตฌ์•ผ! ํ• ์ธ ์ฟ ํฐ, ๋ฌด๋ฃŒ๋ฐฐ์†ก ์ฟ ํฐ ๋“ฑ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ๊ฐ€ ์žˆ์ง€. ๐ŸŽซ

-- ์ฟ ํฐ ๋งˆ์Šคํ„ฐ ํ…Œ์ด๋ธ”
CREATE TABLE coupons (
    coupon_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    coupon_code VARCHAR(50) UNIQUE NOT NULL,
    coupon_name VARCHAR(100) NOT NULL,
    coupon_type ENUM('FIXED', 'PERCENT', 'FREE_SHIPPING') NOT NULL,
    discount_value DECIMAL(10,2) NOT NULL COMMENT 'ํ• ์ธ๊ฐ’ (๊ธˆ์•ก ๋˜๋Š” %)',
    min_purchase_amount DECIMAL(10,2) DEFAULT 0 COMMENT '์ตœ์†Œ ๊ตฌ๋งค ๊ธˆ์•ก',
    max_discount_amount DECIMAL(10,2) COMMENT '์ตœ๋Œ€ ํ• ์ธ ๊ธˆ์•ก (% ์ฟ ํฐ์šฉ)',
    
    total_quantity INT COMMENT '์ด ๋ฐœํ–‰ ์ˆ˜๋Ÿ‰ (NULL: ๋ฌด์ œํ•œ)',
    used_quantity INT DEFAULT 0,
    
    valid_from TIMESTAMP NOT NULL,
    valid_until TIMESTAMP NOT NULL,
    
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_code (coupon_code),
    INDEX idx_valid_period (valid_from, valid_until)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ํšŒ์›๋ณ„ ์ฟ ํฐ ๋ฐœ๊ธ‰ ํ…Œ์ด๋ธ”
CREATE TABLE member_coupons (
    member_coupon_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    member_id BIGINT NOT NULL,
    coupon_id BIGINT NOT NULL,
    is_used BOOLEAN DEFAULT FALSE,
    used_at TIMESTAMP NULL,
    order_id BIGINT COMMENT '์‚ฌ์šฉํ•œ ์ฃผ๋ฌธ ID',
    issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    FOREIGN KEY (member_id) REFERENCES members(member_id) ON DELETE CASCADE,
    FOREIGN KEY (coupon_id) REFERENCES coupons(coupon_id),
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    INDEX idx_member (member_id),
    INDEX idx_coupon (coupon_id),
    INDEX idx_used (is_used)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
๐Ÿ“Œ ๋ฆฌ๋ทฐ(Reviews) ์‹œ์Šคํ…œ

๋ฆฌ๋ทฐ๋Š” ๊ตฌ๋งค ์ „ํ™˜์œจ์„ ๋†’์ด๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์•ผ! ์ž˜ ์„ค๊ณ„ํ•ด๋ณด์ž. โญ

CREATE TABLE product_reviews (
    review_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT NOT NULL,
    member_id BIGINT NOT NULL,
    order_item_id BIGINT NOT NULL COMMENT '๊ตฌ๋งค ํ™•์ธ์šฉ',
    
    rating TINYINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
    title VARCHAR(200),
    content TEXT NOT NULL,
    
    is_photo_review BOOLEAN DEFAULT FALSE,
    helpful_count INT DEFAULT 0 COMMENT '๋„์›€๋จ ์ˆ˜',
    
    admin_reply TEXT COMMENT '๊ด€๋ฆฌ์ž ๋‹ต๋ณ€',
    admin_replied_at TIMESTAMP NULL,
    
    is_visible BOOLEAN DEFAULT TRUE,
    is_best BOOLEAN DEFAULT FALSE COMMENT '๋ฒ ์ŠคํŠธ ๋ฆฌ๋ทฐ ์—ฌ๋ถ€',
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    FOREIGN KEY (product_id) REFERENCES products(product_id),
    FOREIGN KEY (member_id) REFERENCES members(member_id),
    FOREIGN KEY (order_item_id) REFERENCES order_items(order_item_id),
    INDEX idx_product (product_id),
    INDEX idx_member (member_id),
    INDEX idx_rating (rating),
    INDEX idx_created_at (created_at),
    UNIQUE KEY unique_review (order_item_id) -- ํ•œ ์ฃผ๋ฌธ ์ƒํ’ˆ๋‹น ํ•˜๋‚˜์˜ ๋ฆฌ๋ทฐ๋งŒ
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- ๋ฆฌ๋ทฐ ์ด๋ฏธ์ง€ ํ…Œ์ด๋ธ”
CREATE TABLE review_images (
    image_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    review_id BIGINT NOT NULL,
    image_url VARCHAR(500) NOT NULL,
    display_order INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    FOREIGN KEY (review_id) REFERENCES product_reviews(review_id) ON DELETE CASCADE,
    INDEX idx_review (review_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
๐Ÿ’ก ๋ฆฌ๋ทฐ ์‹œ์Šคํ…œ ์šด์˜ ํŒ

โ€ข ๊ตฌ๋งค ํ™•์ธ ํ›„์—๋งŒ ๋ฆฌ๋ทฐ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ œํ•œ
โ€ข ํฌํ†  ๋ฆฌ๋ทฐ ์ž‘์„ฑ ์‹œ ์ถ”๊ฐ€ ํฌ์ธํŠธ ์ง€๊ธ‰์œผ๋กœ ์œ ๋„
โ€ข ๋ฒ ์ŠคํŠธ ๋ฆฌ๋ทฐ๋Š” ์ž๋™ ์„ ์ • (ํ‰์ , ๋„์›€๋จ ์ˆ˜, ๊ธธ์ด ๋“ฑ ๊ณ ๋ ค)
โ€ข ๋ฆฌ๋ทฐ ์ž‘์„ฑ ์‹œ ์ƒํ’ˆ์˜ rating_average, review_count ์ž๋™ ์—…๋ฐ์ดํŠธ
โ€ข ๋ถ€์ ์ ˆํ•œ ๋ฆฌ๋ทฐ ์‹ ๊ณ  ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๊ณ ๋ ค
๐Ÿ“Œ ์œ„์‹œ๋ฆฌ์ŠคํŠธ(Wishlist) ์‹œ์Šคํ…œ

๊ด€์‹ฌ ์ƒํ’ˆ์„ ์ €์žฅํ•˜๋Š” ์œ„์‹œ๋ฆฌ์ŠคํŠธ! ๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ „ํ™˜์œจ์„ ๋†’์ด๋Š” ๋ฐ ํšจ๊ณผ์ ์ด์•ผ. โค๏ธ

CREATE TABLE wishlists (
    wishlist_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    member_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    FOREIGN KEY (member_id) REFERENCES members(member_id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(product_id) ON DELETE CASCADE,
    INDEX idx_member (member_id),
    INDEX idx_product (product_id),
    UNIQUE KEY unique_wishlist (member_id, product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
๐Ÿ“Œ ํฌ์ธํŠธ(Points) ์‹œ์Šคํ…œ

ํฌ์ธํŠธ๋Š” ๊ณ ๊ฐ ์ถฉ์„ฑ๋„๋ฅผ ๋†’์ด๋Š” ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด์•ผ! ์ ๋ฆฝ๊ณผ ์‚ฌ์šฉ ๋‚ด์—ญ์„ ์ •ํ™•ํžˆ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด. ๐Ÿ’ฐ

CREATE TABLE point_histories (
    history_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    member_id BIGINT NOT NULL,
    point_type ENUM('EARN', 'USE', 'CANCEL', 'EXPIRE') NOT NULL,
    point_amount INT NOT NULL COMMENT 'ํฌ์ธํŠธ ๊ธˆ์•ก (์–‘์ˆ˜: ์ ๋ฆฝ, ์Œ์ˆ˜: ์‚ฌ์šฉ)',
    point_balance INT NOT NULL COMMENT '๋ณ€๋™ ํ›„ ์ž”์•ก',
    
    reason VARCHAR(100) NOT NULL COMMENT '์ ๋ฆฝ/์‚ฌ์šฉ ์‚ฌ์œ ',
    reference_type VARCHAR(50) COMMENT '์ฐธ์กฐ ํƒ€์ž… (ORDER, REVIEW, EVENT)',
    reference_id BIGINT COMMENT '์ฐธ์กฐ ID',
    
    expire_date DATE COMMENT 'ํฌ์ธํŠธ ๋งŒ๋ฃŒ์ผ',
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    FOREIGN KEY (member_id) REFERENCES members(member_id),
    INDEX idx_member (member_id),
    INDEX idx_created_at (created_at),
    INDEX idx_expire_date (expire_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
โš ๏ธ ํฌ์ธํŠธ ๊ด€๋ฆฌ ์ฃผ์˜์‚ฌํ•ญ

โ€ข ํฌ์ธํŠธ ์ž”์•ก์€ members ํ…Œ์ด๋ธ”๊ณผ point_histories ํ…Œ์ด๋ธ” ์–‘์ชฝ์—์„œ ๊ด€๋ฆฌ
โ€ข ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด์„œ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๋ณด์žฅ ํ•„์ˆ˜
โ€ข ํฌ์ธํŠธ ๋งŒ๋ฃŒ ์ฒ˜๋ฆฌ๋Š” ๋ฐฐ์น˜ ์ž‘์—…์œผ๋กœ ์ •๊ธฐ ์‹คํ–‰
โ€ข ์ฃผ๋ฌธ ์ทจ์†Œ ์‹œ ์‚ฌ์šฉํ•œ ํฌ์ธํŠธ ์ž๋™ ๋ณต๊ตฌ
โ€ข ํฌ์ธํŠธ ๋ถ€์ • ์‚ฌ์šฉ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ๋กœ์ง ํ•„์š”

๐Ÿ”„ ์‹ค์ „ ์ฟผ๋ฆฌ ์˜ˆ์ œ

์ด๋ก ๋งŒ ์•Œ์•„์„œ๋Š” ์†Œ์šฉ์—†์ง€! ์‹ค์ œ๋กœ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ฟผ๋ฆฌ๋“ค์„ ์˜ˆ์ œ๋กœ ๋งŒ๋“ค์–ด๋ดค์–ด. ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์จ๋จน์„ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ’ช

๐Ÿ“Œ ์ƒํ’ˆ ๋ชฉ๋ก ์กฐํšŒ (ํŽ˜์ด์ง•, ์ •๋ ฌ, ํ•„ํ„ฐ)
-- ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒํ’ˆ ๋ชฉ๋ก (์ธ๊ธฐ์ˆœ, ํŽ˜์ด์ง•)
SELECT 
    p.product_id,
    p.product_name,
    p.price,
    p.discount_price,
    COALESCE(p.discount_price, p.price) AS final_price,
    p.rating_average,
    p.review_count,
    p.sales_count,
    pi.image_url AS thumbnail
FROM products p
LEFT JOIN product_images pi ON p.product_id = pi.product_id 
    AND pi.image_type = 'MAIN'
WHERE p.category_id = 10
    AND p.is_display = TRUE
    AND p.is_soldout = FALSE
ORDER BY p.sales_count DESC, p.created_at DESC
LIMIT 20 OFFSET 0;  -- ํŽ˜์ด์ง€๋‹น 20๊ฐœ, 1ํŽ˜์ด์ง€

-- ๊ฐ€๊ฒฉ๋Œ€๋ณ„ ํ•„ํ„ฐ๋ง
SELECT 
    p.product_id,
    p.product_name,
    COALESCE(p.discount_price, p.price) AS final_price
FROM products p
WHERE p.category_id = 10
    AND COALESCE(p.discount_price, p.price) BETWEEN 10000 AND 50000
    AND p.is_display = TRUE
ORDER BY final_price ASC;

-- ์ „๋ฌธ ๊ฒ€์ƒ‰ (์ƒํ’ˆ๋ช…, ์„ค๋ช…)
SELECT 
    p.product_id,
    p.product_name,
    p.price,
    MATCH(p.product_name, p.description) AGAINST('์…”์ธ ' IN NATURAL LANGUAGE MODE) AS relevance
FROM products p
WHERE MATCH(p.product_name, p.description) AGAINST('์…”์ธ ' IN NATURAL LANGUAGE MODE)
    AND p.is_display = TRUE
ORDER BY relevance DESC, p.sales_count DESC
LIMIT 20;
๐Ÿ“Œ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค
-- ์ฃผ๋ฌธ ์ƒ์„ฑ (ํŠธ๋žœ์žญ์…˜ ํ•„์ˆ˜!)
START TRANSACTION;

-- 1. ์ฃผ๋ฌธ ์ƒ์„ฑ
INSERT INTO orders (
    order_number, member_id, order_status, payment_method,
    total_product_price, total_discount_amount, delivery_fee, 
    total_payment_amount, used_point,
    recipient_name, recipient_phone, postal_code, 
    address_line1, address_line2
) VALUES (
    'ORD20240115001234', 123, 'PENDING', 'CARD',
    50000, 5000, 3000, 48000, 0,
    'ํ™๊ธธ๋™', '010-1234-5678', '12345',
    '์„œ์šธ์‹œ ๊ฐ•๋‚จ๊ตฌ', 'ํ…Œํ—ค๋ž€๋กœ 123'
);

SET @order_id = LAST_INSERT_ID();

-- 2. ์ฃผ๋ฌธ ์ƒ์„ธ ์ƒ์„ฑ ๋ฐ ์žฌ๊ณ  ์ฐจ๊ฐ
INSERT INTO order_items (
    order_id, product_id, product_name, product_code,
    quantity, unit_price, discount_amount, total_price
)
SELECT 
    @order_id,
    p.product_id,
    p.product_name,
    p.product_code,
    c.quantity,
    COALESCE(p.discount_price, p.price),
    0,
    COALESCE(p.discount_price, p.price) * c.quantity
FROM cart_items c
JOIN products p ON c.product_id = p.product_id
WHERE c.member_id = 123 AND c.is_selected = TRUE;

-- 3. ์žฌ๊ณ  ์ฐจ๊ฐ (์›์ž์  ์—…๋ฐ์ดํŠธ)
UPDATE products p
JOIN cart_items c ON p.product_id = c.product_id
SET p.stock_quantity = p.stock_quantity - c.quantity,
    p.sales_count = p.sales_count + c.quantity
WHERE c.member_id = 123 
    AND c.is_selected = TRUE
    AND p.stock_quantity >= c.quantity;

-- 4. ์žฌ๊ณ  ๋ถ€์กฑ ์ฒดํฌ
SELECT COUNT(*) INTO @stock_check
FROM cart_items c
JOIN products p ON c.product_id = p.product_id
WHERE c.member_id = 123 
    AND c.is_selected = TRUE
    AND p.stock_quantity < 0;

-- ์žฌ๊ณ  ๋ถ€์กฑ ์‹œ ๋กค๋ฐฑ
IF @stock_check > 0 THEN
    ROLLBACK;
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '์žฌ๊ณ ๊ฐ€ ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค';
END IF;

-- 5. ํฌ์ธํŠธ ์‚ฌ์šฉ (์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ)
-- UPDATE members SET point_balance = point_balance - ์‚ฌ์šฉํฌ์ธํŠธ
-- INSERT INTO point_histories ...

-- 6. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋น„์šฐ๊ธฐ
DELETE FROM cart_items 
WHERE member_id = 123 AND is_selected = TRUE;

COMMIT;
๐Ÿ“Œ ํ†ต๊ณ„ ๋ฐ ๋ฆฌํฌํŠธ ์ฟผ๋ฆฌ
-- ์ผ๋ณ„ ๋งค์ถœ ํ†ต๊ณ„
SELECT 
    DATE(created_at) AS order_date,
    COUNT(*) AS order_count,
    SUM(total_payment_amount) AS total_sales,
    AVG(total_payment_amount) AS avg_order_value,
    COUNT(DISTINCT member_id) AS unique_customers
FROM orders
WHERE order_status IN ('PAID', 'PREPARING', 'SHIPPING', 'DELIVERED')
    AND created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY DATE(created_at)
ORDER BY order_date DESC;

-- ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ๋งค์ถœ ์ˆœ์œ„
SELECT 
    c.category_name,
    COUNT(DISTINCT oi.order_id) AS order_count,
    SUM(oi.quantity) AS total_quantity,
    SUM(oi.total_price) AS total_sales
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_status IN ('PAID', 'PREPARING', 'SHIPPING', 'DELIVERED')
    AND o.created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY c.category_id, c.category_name
ORDER BY total_sales DESC
LIMIT 10;

-- ๋ฒ ์ŠคํŠธ์…€๋Ÿฌ ์ƒํ’ˆ (์ตœ๊ทผ 30์ผ)
SELECT 
    p.product_id,
    p.product_name,
    p.price,
    SUM(oi.quantity) AS total_sold,
    SUM(oi.total_price) AS total_revenue,
    p.rating_average,
    p.review_count
FROM products p
JOIN order_items oi ON p.product_id = oi.product_id
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_status IN ('PAID', 'PREPARING', 'SHIPPING', 'DELIVERED')
    AND o.created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY p.product_id
ORDER BY total_sold DESC
LIMIT 20;

-- ํšŒ์› ๋“ฑ๊ธ‰๋ณ„ ๊ตฌ๋งค ํŒจํ„ด
SELECT 
    m.member_grade,
    COUNT(DISTINCT m.member_id) AS member_count,
    COUNT(o.order_id) AS total_orders,
    ROUND(COUNT(o.order_id) / COUNT(DISTINCT m.member_id), 2) AS avg_orders_per_member,
    SUM(o.total_payment_amount) AS total_sales,
    ROUND(AVG(o.total_payment_amount), 0) AS avg_order_value
FROM members m
LEFT JOIN orders o ON m.member_id = o.member_id 
    AND o.order_status IN ('PAID', 'PREPARING', 'SHIPPING', 'DELIVERED')
    AND o.created_at >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
GROUP BY m.member_grade
ORDER BY total_sales DESC;
๐Ÿ“Œ ๊ณ ๊ฐ ๋ถ„์„ ์ฟผ๋ฆฌ
-- RFM ๋ถ„์„ (Recency, Frequency, Monetary)
SELECT 
    m.member_id,
    m.email,
    m.name,
    DATEDIFF(CURDATE(), MAX(o.created_at)) AS recency_days,
    COUNT(o.order_id) AS frequency,
    SUM(o.total_payment_amount) AS monetary
FROM members m
LEFT JOIN orders o ON m.member_id = o.member_id
    AND o.order_status IN ('PAID', 'PREPARING', 'SHIPPING', 'DELIVERED')
WHERE m.is_active = TRUE
GROUP BY m.member_id
HAVING frequency > 0
ORDER BY monetary DESC, frequency DESC, recency_days ASC;

-- ์ดํƒˆ ์œ„ํ—˜ ๊ณ ๊ฐ (์ตœ๊ทผ 90์ผ ๊ตฌ๋งค ์—†์Œ, ๊ณผ๊ฑฐ ๊ตฌ๋งค ์ด๋ ฅ ์žˆ์Œ)
SELECT 
    m.member_id,
    m.email,
    m.name,
    m.total_purchase_amount,
    MAX(o.created_at) AS last_order_date,
    DATEDIFF(CURDATE(), MAX(o.created_at)) AS days_since_last_order
FROM members m
JOIN orders o ON m.member_id = o.member_id
WHERE o.order_status IN ('PAID', 'PREPARING', 'SHIPPING', 'DELIVERED')
GROUP BY m.member_id
HAVING days_since_last_order > 90
ORDER BY m.total_purchase_amount DESC
LIMIT 100;

๐Ÿ›ก๏ธ ๋ฐ์ดํ„ฐ ๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ ์ „๋žต

์•„๋ฌด๋ฆฌ ์ž˜ ์„ค๊ณ„ํ•ด๋„ ์žฅ์• ๋Š” ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด! ๋ฐ์ดํ„ฐ ์†์‹ค์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฑ์—… ์ „๋žต์ด ํ•„์ˆ˜์•ผ. ๐Ÿ”’

๐Ÿ“‹ ๋ฐฑ์—… ์ „๋žต

1. ์ •๊ธฐ ๋ฐฑ์—… (Full Backup)
โ€ข ๋งค์ผ ์ƒˆ๋ฒฝ ์‹œ๊ฐ„๋Œ€์— ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—…
โ€ข ์ตœ์†Œ 7์ผ์น˜ ๋ฐฑ์—… ํŒŒ์ผ ๋ณด๊ด€
โ€ข ์›”๋ณ„ ๋ฐฑ์—…์€ 1๋…„๊ฐ„ ๋ณด๊ด€

2. ์ฆ๋ถ„ ๋ฐฑ์—… (Incremental Backup)
โ€ข ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋งŒ ๋ฐฑ์—… (๋””์Šคํฌ ๊ณต๊ฐ„ ์ ˆ์•ฝ)
โ€ข ๋งค ์‹œ๊ฐ„๋งˆ๋‹ค ์‹คํ–‰

3. ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ ๋ฐฑ์—…
โ€ข MySQL์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ๋ฅผ ๋ณ„๋„ ์ €์žฅ
โ€ข ํŠน์ • ์‹œ์ ์œผ๋กœ ๋ณต๊ตฌ ๊ฐ€๋Šฅ (Point-in-Time Recovery)

4. ๋ณต์ œ ์„œ๋ฒ„ ์šด์˜
โ€ข Master-Slave ๊ตฌ์กฐ๋กœ ์‹ค์‹œ๊ฐ„ ๋ณต์ œ
โ€ข ์žฅ์•  ๋ฐœ์ƒ ์‹œ ์ฆ‰์‹œ ์ „ํ™˜ ๊ฐ€๋Šฅ
-- MySQL ๋ฐฑ์—… ๋ช…๋ น์–ด ์˜ˆ์‹œ
-- ์ „์ฒด ๋ฐฑ์—…
mysqldump -u root -p --all-databases --single-transaction \
  --routines --triggers --events > backup_$(date +%Y%m%d).sql

-- ํŠน์ • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ ๋ฐฑ์—…
mysqldump -u root -p shopping_mall --single-transaction \
  --routines --triggers > shopping_mall_$(date +%Y%m%d).sql

-- ๋ณต๊ตฌ
mysql -u root -p shopping_mall < backup_20240115.sql

-- ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ ํ™œ์„ฑํ™” (my.cnf)
[mysqld]
log-bin=mysql-bin
server-id=1
binlog_format=ROW
expire_logs_days=7

๐Ÿš€ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์•„ํ‚คํ…์ฒ˜

์ฒ˜์Œ์—” ์ž‘๊ฒŒ ์‹œ์ž‘ํ•ด๋„ ์„ฑ๊ณตํ•˜๋ฉด ํŠธ๋ž˜ํ”ฝ์ด ํญ๋ฐœ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด! ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ณ ๋ คํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด. ๐Ÿ“ˆ

๐Ÿ“Œ ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ถ„๋ฆฌ (Read/Write Splitting)

์‡ผํ•‘๋ชฐ์€ ์กฐํšŒ(์ฝ๊ธฐ)๊ฐ€ ์ฃผ๋ฌธ(์“ฐ๊ธฐ)๋ณด๋‹ค ํ›จ์”ฌ ๋งŽ์•„. ๊ทธ๋ž˜์„œ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋ผ! ๐ŸŽฏ

๐Ÿ”„ Master-Slave ๊ตฌ์กฐ

Master DB (์“ฐ๊ธฐ ์ „์šฉ)
โ€ข INSERT, UPDATE, DELETE ์ฒ˜๋ฆฌ
โ€ข ์ฃผ๋ฌธ, ํšŒ์›๊ฐ€์ž…, ์ƒํ’ˆ ๋“ฑ๋ก ๋“ฑ

Slave DB (์ฝ๊ธฐ ์ „์šฉ)
โ€ข SELECT ์ฒ˜๋ฆฌ
โ€ข ์ƒํ’ˆ ๋ชฉ๋ก, ์ƒ์„ธ ์กฐํšŒ ๋“ฑ
โ€ข ์—ฌ๋Ÿฌ ๋Œ€์˜ Slave๋กœ ๋ถ€ํ•˜ ๋ถ„์‚ฐ ๊ฐ€๋Šฅ

๋ณต์ œ ์ง€์—ฐ(Replication Lag) ์ฃผ์˜
โ€ข Master์—์„œ Slave๋กœ ๋ฐ์ดํ„ฐ ๋ณต์ œ์— ์•ฝ๊ฐ„์˜ ์‹œ๊ฐ„ ์†Œ์š”
โ€ข ์‹ค์‹œ๊ฐ„์„ฑ์ด ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” Master์—์„œ ์กฐํšŒ
๐Ÿ“Œ ์ƒค๋”ฉ(Sharding) ์ „๋žต

๋ฐ์ดํ„ฐ๊ฐ€ ์ •๋ง ๋งŽ์•„์ง€๋ฉด ํ•œ ๋Œ€์˜ DB๋กœ๋Š” ๊ฐ๋‹น์ด ์•ˆ ๋ผ. ๊ทธ๋Ÿด ๋•Œ ์ƒค๋”ฉ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ด! ๐Ÿ”ช

๐Ÿ—‚๏ธ ์ƒค๋”ฉ ๋ฐฉ๋ฒ•

1. ์ˆ˜ํ‰ ์ƒค๋”ฉ (Horizontal Sharding)
โ€ข ํšŒ์› ID ๊ธฐ์ค€์œผ๋กœ ๋ถ„ํ• 
โ€ข ์˜ˆ: ID 1~100๋งŒ โ†’ DB1, 101๋งŒ~200๋งŒ โ†’ DB2

2. ์ˆ˜์ง ์ƒค๋”ฉ (Vertical Sharding)
โ€ข ๊ธฐ๋Šฅ๋ณ„๋กœ DB ๋ถ„๋ฆฌ
โ€ข ์˜ˆ: ํšŒ์›DB, ์ƒํ’ˆDB, ์ฃผ๋ฌธDB ๋ถ„๋ฆฌ

3. ์ง€์—ญ๋ณ„ ์ƒค๋”ฉ
โ€ข ๊ธ€๋กœ๋ฒŒ ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ ์ง€์—ญ๋ณ„๋กœ DB ๋ถ„๋ฆฌ
โ€ข ์˜ˆ: ํ•œ๊ตญDB, ๋ฏธ๊ตญDB, ์œ ๋ŸฝDB
โš ๏ธ ์ƒค๋”ฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

โ€ข JOIN ์ฟผ๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์ง (์—ฌ๋Ÿฌ ์ƒค๋“œ์— ๊ฑธ์นœ JOIN ๋ถˆ๊ฐ€)
โ€ข ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด์ง
โ€ข ์ƒค๋“œ ํ‚ค ์„ ํƒ์ด ๋งค์šฐ ์ค‘์š” (์žฌ๋ถ„๋ฐฐ ์–ด๋ ค์›€)
โ€ข ์ฒ˜์Œ๋ถ€ํ„ฐ ์ƒค๋”ฉํ•˜์ง€ ๋ง๊ณ , ์ •๋ง ํ•„์š”ํ•  ๋•Œ๋งŒ!
๐Ÿ“Œ ์บ์‹ฑ ์ „๋žต

DB ๋ถ€ํ•˜๋ฅผ ์ค„์ด๋Š” ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ•์€ ์บ์‹ฑ์ด์•ผ! Redis๋‚˜ Memcached๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์‘๋‹ต ์†๋„๊ฐ€ ์ˆ˜์‹ญ ๋ฐฐ ๋นจ๋ผ์ ธ. โšก

๐Ÿ’พ ์บ์‹ฑ ๋Œ€์ƒ

1. ์ƒํ’ˆ ์ •๋ณด
โ€ข ์ƒํ’ˆ ์ƒ์„ธ ์ •๋ณด (๋ณ€๊ฒฝ ๋นˆ๋„ ๋‚ฎ์Œ)
โ€ข TTL: 1์‹œ๊ฐ„

2. ์นดํ…Œ๊ณ ๋ฆฌ ๋ชฉ๋ก
โ€ข ์นดํ…Œ๊ณ ๋ฆฌ ํŠธ๋ฆฌ ๊ตฌ์กฐ
โ€ข TTL: 24์‹œ๊ฐ„

3. ๋ฒ ์ŠคํŠธ์…€๋Ÿฌ ๋ชฉ๋ก
โ€ข ์ธ๊ธฐ ์ƒํ’ˆ ์ˆœ์œ„
โ€ข TTL: 10๋ถ„

4. ํšŒ์› ์„ธ์…˜
โ€ข ๋กœ๊ทธ์ธ ์ •๋ณด
โ€ข TTL: 30๋ถ„

5. ์žฅ๋ฐ”๊ตฌ๋‹ˆ
โ€ข ๋น„ํšŒ์› ์žฅ๋ฐ”๊ตฌ๋‹ˆ
โ€ข TTL: 7์ผ

๐Ÿ’ก ์‹ค๋ฌด ํŒ & ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

๋งˆ์ง€๋ง‰์œผ๋กœ ๋‚ด๊ฐ€ ์‹ค๋ฌด์—์„œ ๊ฒฝํ—˜ํ•œ ๋…ธํ•˜์šฐ๋“ค์„ ๊ณต์œ ํ• ๊ฒŒ! ์ด๊ฒƒ๋งŒ ์•Œ์•„๋„ ๋งŽ์€ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ๐Ÿ˜Š

โœจ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

โœ… ๋ช…๋ช… ๊ทœ์น™
โ€ข ํ…Œ์ด๋ธ”๋ช…: ๋ณต์ˆ˜ํ˜• ์‚ฌ์šฉ (products, orders)
โ€ข ์ปฌ๋Ÿผ๋ช…: snake_case ์‚ฌ์šฉ (product_name, created_at)
โ€ข ์™ธ๋ž˜ํ‚ค: ์ฐธ์กฐ ํ…Œ์ด๋ธ”๋ช…_id (member_id, product_id)
โ€ข ์ธ๋ฑ์Šค: idx_์ปฌ๋Ÿผ๋ช… (idx_email, idx_created_at)

โœ… ๋ฐ์ดํ„ฐ ํƒ€์ž… ์„ ํƒ
โ€ข ๊ธˆ์•ก: DECIMAL(์ •ํ™•ํ•œ ๊ณ„์‚ฐ ํ•„์š”)
โ€ข ๋‚ ์งœ/์‹œ๊ฐ„: TIMESTAMP (์‹œ๊ฐ„๋Œ€ ๊ณ ๋ ค) ๋˜๋Š” DATETIME
โ€ข ์ƒํƒœ๊ฐ’: ENUM (๊ณ ์ •๋œ ๊ฐ’) ๋˜๋Š” TINYINT
โ€ข ํ…์ŠคํŠธ: VARCHAR(๊ธธ์ด ์ œํ•œ) ๋˜๋Š” TEXT
โ€ข Boolean: BOOLEAN ๋˜๋Š” TINYINT(1)

โœ… ์ œ์•ฝ ์กฐ๊ฑด
โ€ข PRIMARY KEY: ๋ชจ๋“  ํ…Œ์ด๋ธ”์— ํ•„์ˆ˜
โ€ข FOREIGN KEY: ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ ๋ณด์žฅ
โ€ข UNIQUE: ์ค‘๋ณต ๋ฐฉ์ง€ (์ด๋ฉ”์ผ, ์ฃผ๋ฌธ๋ฒˆํ˜ธ ๋“ฑ)
โ€ข NOT NULL: ํ•„์ˆ˜ ๊ฐ’ ์ง€์ •
โ€ข DEFAULT: ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •

โœ… ์ธ๋ฑ์Šค
โ€ข WHERE ์ ˆ์— ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์ปฌ๋Ÿผ
โ€ข JOIN ์กฐ๊ฑด ์ปฌ๋Ÿผ
โ€ข ORDER BY ์ปฌ๋Ÿผ
โ€ข ๋ณตํ•ฉ ์ธ๋ฑ์Šค๋Š” ์„ ํƒ๋„๊ฐ€ ๋†’์€ ์ปฌ๋Ÿผ์„ ์•ž์—

โœ… ๋ณด์•ˆ
โ€ข ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ํ•ด์‹œ ์ €์žฅ (bcrypt, Argon2)
โ€ข ๊ฐœ์ธ์ •๋ณด๋Š” ์•”ํ˜ธํ™” ๊ณ ๋ ค
โ€ข SQL Injection ๋ฐฉ์ง€ (Prepared Statement)
โ€ข ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ (DB ์‚ฌ์šฉ์ž ๊ถŒํ•œ ์ œํ•œ)
๐Ÿ’ก ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ

โ€ข SELECT * ์‚ฌ์šฉ ์ž์ œ, ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ์กฐํšŒ
โ€ข N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ ํ•ด๊ฒฐ (JOIN ๋˜๋Š” IN ์‚ฌ์šฉ)
โ€ข ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ํ•„์ˆ˜ (LIMIT, OFFSET)
โ€ข ์Šฌ๋กœ์šฐ ์ฟผ๋ฆฌ ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง
โ€ข EXPLAIN์œผ๋กœ ์‹คํ–‰ ๊ณ„ํš ํ™•์ธ
โ€ข ์ ์ ˆํ•œ ์ธ๋ฑ์Šค ์ƒ์„ฑ
โ€ข ์บ์‹ฑ ํ™œ์šฉ
โ€ข ์ปค๋„ฅ์…˜ ํ’€ ์„ค์ •
๐ŸŽฏ ์ž์ฃผ ํ•˜๋Š” ์‹ค์ˆ˜๋“ค

โŒ ์‹ค์ˆ˜ 1: ๊ณผ๋„ํ•œ ์ •๊ทœํ™”
โ†’ ์กฐํšŒ ์„ฑ๋Šฅ ์ €ํ•˜, ๋ณต์žกํ•œ JOIN
โ†’ ํ•ด๊ฒฐ: ์ ์ ˆํ•œ ์—ญ์ •๊ทœํ™”๋กœ ๊ท ํ˜• ์œ ์ง€

โŒ ์‹ค์ˆ˜ 2: ์ธ๋ฑ์Šค ๋ถ€์กฑ ๋˜๋Š” ๊ณผ๋‹ค
โ†’ ์กฐํšŒ ๋А๋ฆผ ๋˜๋Š” ์“ฐ๊ธฐ ๋А๋ฆผ
โ†’ ํ•ด๊ฒฐ: ์‹ค์ œ ์ฟผ๋ฆฌ ํŒจํ„ด ๋ถ„์„ ํ›„ ์ตœ์ ํ™”

โŒ ์‹ค์ˆ˜ 3: ํŠธ๋žœ์žญ์…˜ ๋ฏธ์‚ฌ์šฉ
โ†’ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๋ฌธ์ œ
โ†’ ํ•ด๊ฒฐ: ๊ด€๋ จ ์ž‘์—…์€ ๋ฐ˜๋“œ์‹œ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ๊ธฐ

โŒ ์‹ค์ˆ˜ 4: ๋™์‹œ์„ฑ ์ œ์–ด ๋ฏธํก
โ†’ ์žฌ๊ณ  ๋งˆ์ด๋„ˆ์Šค, ์ค‘๋ณต ๊ฒฐ์ œ ๋“ฑ
โ†’ ํ•ด๊ฒฐ: ๋ฝ(Lock) ๋˜๋Š” ์›์ž์  ์—…๋ฐ์ดํŠธ ์‚ฌ์šฉ

โŒ ์‹ค์ˆ˜ 5: ๋ฐฑ์—… ์ „๋žต ๋ถ€์žฌ
โ†’ ๋ฐ์ดํ„ฐ ์†์‹ค ์‹œ ๋ณต๊ตฌ ๋ถˆ๊ฐ€
โ†’ ํ•ด๊ฒฐ: ์ •๊ธฐ ๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ ํ…Œ์ŠคํŠธ

๐ŸŽ“ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ๋А๋ผ ์ˆ˜๊ณ ํ–ˆ์–ด! ๐Ÿ˜Š

์‡ผํ•‘๋ชฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋Š” ์ •๋ง ๋ณต์žกํ•˜๊ณ  ๊ณ ๋ คํ•  ๊ฒŒ ๋งŽ์ง€? ํ•˜์ง€๋งŒ ํ•œ ๋ฒˆ ์ œ๋Œ€๋กœ ์„ค๊ณ„ํ•ด๋‘๋ฉด ๋‚˜์ค‘์— ์ •๋ง ํŽธํ•ด. ๋ฐ˜๋Œ€๋กœ ์ฒ˜์Œ์— ๋Œ€์ถฉ ๋งŒ๋“ค๋ฉด ๋‚˜์ค‘์— ์—„์ฒญ๋‚œ ๊ณ ์ƒ์„ ํ•˜๊ฒŒ ๋ผ... (๊ฒฝํ—˜๋‹ด ๐Ÿ˜…)

์˜ค๋Š˜ ๋‹ค๋ฃฌ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด:

1. ๊ธฐ๋ณธ ์›์น™
โ€ข ์ •๊ทœํ™”์™€ ์—ญ์ •๊ทœํ™”์˜ ๊ท ํ˜•
โ€ข ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ ์„ค๊ณ„
โ€ข ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๋ณด์žฅ

2. ํ•ต์‹ฌ ํ…Œ์ด๋ธ”
โ€ข ํšŒ์› ๊ด€๋ฆฌ (members, member_addresses)
โ€ข ์ƒํ’ˆ ๊ด€๋ฆฌ (products, categories, product_options)
โ€ข ์ฃผ๋ฌธ ๊ด€๋ฆฌ (orders, order_items, payments)

3. ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ
โ€ข ์ฟ ํฐ, ๋ฆฌ๋ทฐ, ์œ„์‹œ๋ฆฌ์ŠคํŠธ, ํฌ์ธํŠธ
โ€ข ๊ฐ ๊ธฐ๋Šฅ๋ณ„ ์ตœ์ ํ™”๋œ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ

4. ์„ฑ๋Šฅ ์ตœ์ ํ™”
โ€ข ์ธ๋ฑ์Šค ์ „๋žต
โ€ข ์ฟผ๋ฆฌ ์ตœ์ ํ™”
โ€ข ๋™์‹œ์„ฑ ์ œ์–ด
โ€ข ์บ์‹ฑ ํ™œ์šฉ

5. ํ™•์žฅ์„ฑ
โ€ข ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ถ„๋ฆฌ
โ€ข ์ƒค๋”ฉ
โ€ข ๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ

๐ŸŒŸ ์žฌ๋Šฅ๋„ท์—์„œ ๋” ๋ฐฐ์šฐ๊ธฐ

์‡ผํ•‘๋ชฐ ๊ฐœ๋ฐœ์ด ์ฒ˜์Œ์ด๋ผ ๋ง‰๋ง‰ํ•˜๋‹ค๋ฉด? ์žฌ๋Šฅ๋„ท์—์„œ ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ 1:1 ๋ฉ˜ํ† ๋ง์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ!

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋ถ€ํ„ฐ ์‹ค์ œ ๊ตฌํ˜„๊นŒ์ง€, ํ”„๋กœ์ ํŠธ ์ „๋ฐ˜์— ๊ฑธ์นœ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ํ˜ผ์ž ๊ณ ๋ฏผํ•˜์ง€ ๋ง๊ณ  ์žฌ๋Šฅ๋„ท์˜ ์ „๋ฌธ๊ฐ€๋“ค๊ณผ ํ•จ๊ป˜ ์„ฑ์žฅํ•ด๋ณด์ž! ๐Ÿ’ช

์‹ค๋ฌด์—์„œ๋Š” ์ด๋ก ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•ด. ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด๊ณ , ์‹ค์ˆ˜ํ•˜๊ณ , ๊ฐœ์„ ํ•˜๋ฉด์„œ ๋ฐฐ์šฐ๋Š” ๊ฒŒ ๊ฐ€์žฅ ๋น ๋ฅธ ๊ธธ์ด์•ผ.

์ฒ˜์Œ์—” ์™„๋ฒฝํ•˜์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ์•„! ์ž‘๊ฒŒ ์‹œ์ž‘ํ•ด์„œ ์ ์ง„์ ์œผ๋กœ ๊ฐœ์„ ํ•ด๋‚˜๊ฐ€๋ฉด ๋ผ. ์ค‘์š”ํ•œ ๊ฑด ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฑฐ์•ผ. ๊ทธ๋ž˜์•ผ ๋‚˜์ค‘์— ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ๋•Œ ์ „์ฒด๋ฅผ ๋’ค์—Ž์ง€ ์•Š์•„๋„ ๋˜๊ฑฐ๋“ ! ๐ŸŽฏ

๊ทธ๋ฆฌ๊ณ  ํ•ญ์ƒ ๊ธฐ์–ตํ•ด! ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๋ˆ๊ณผ ์ง๊ฒฐ๋˜๋Š” ๋ถ€๋ถ„์ด์•ผ. ์ฃผ๋ฌธ, ๊ฒฐ์ œ, ์žฌ๊ณ ... ์ด๋Ÿฐ ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋“ค์ด ์ €์žฅ๋˜๋Š” ๊ณณ์ด๋‹ˆ๊นŒ ๋ณด์•ˆ๊ณผ ์ •ํ•ฉ์„ฑ์— ํŠนํžˆ ์‹ ๊ฒฝ ์จ์•ผ ํ•ด! ๐Ÿ”’

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๊ณ , ์ž์‹ ๋งŒ์˜ ๋…ธํ•˜์šฐ๋ฅผ ์Œ“์•„๊ฐ€๊ธธ ๋ฐ”๋ผ! ๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ์–ธ์ œ๋“  ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๋ฌผ์–ด๋ด. ์šฐ๋ฆฌ ๋ชจ๋‘ ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฑฐ๋‹ˆ๊นŒ! ๐Ÿš€

ํ™”์ดํŒ…! ๋ฉ‹์ง„ ์‡ผํ•‘๋ชฐ ๋งŒ๋“ค์–ด๋ณด์ž! ๐Ÿ’ชโœจ

Happy Coding! ์„ฑ๊ณต์ ์ธ ์‡ผํ•‘๋ชฐ ๊ฐœ๋ฐœ์„ ์‘์›ํ•ฉ๋‹ˆ๋‹ค Database Design Master
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.