한 주간의 부트캠프가 마지막 날을 맞이했다. 월요일에 **“어떻게 생각할 것인가”**를 배웠다면, 오늘은 **“실제로 설계하고 구현할 수 있는가”**를 시험하는 날이었다. 가장 실전적이고 가장 배우는 점이 많은 하루였다.

오늘 다룬 범위

  • 실제 비즈니스 요구사항 분석
  • 테이블 설계 및 정규화
  • Primary Key, Foreign Key 설정
  • CREATE TABLE 쿼리 작성
  • 샘플 데이터 삽입 및 쿼리 작성
  • 설계 검토 및 개선

핵심 개념 정리

데이터베이스 설계의 단계

1단계: 요구사항 분석

“어떤 데이터를 저장해야 하나?”

예를 들어, “온라인 쇼핑몰 시스템”을 만든다면:

  • 회원 정보: 이름, 이메일, 전화번호, 주소
  • 상품 정보: 상품명, 가격, 설명, 재고
  • 주문 정보: 누가, 언제, 어떤 상품을, 몇 개
  • 리뷰 정보: 누가, 어떤 상품을, 별점, 내용

2단계: 엔티티와 관계 파악

회원 (Users)
  ↓ 1:N (한 회원이 여러 주문)
주문 (Orders)
  ↓ 1:N (한 주문에 여러 상품)
주문상세 (OrderDetails)
  ↓ N:1 (여러 주문상세가 한 상품)
상품 (Products)

3단계: 테이블 설계

-- 회원 테이블
CREATE TABLE Users (
    UserID INT PRIMARY KEY AUTO_INCREMENT,
    Email VARCHAR(100) NOT NULL UNIQUE,
    Name VARCHAR(50) NOT NULL,
    Phone VARCHAR(20),
    Address VARCHAR(200),
    CreatedAt DATETIME DEFAULT NOW()
);

-- 상품 테이블
CREATE TABLE Products (
    ProductID INT PRIMARY KEY AUTO_INCREMENT,
    ProductName VARCHAR(100) NOT NULL,
    Price DECIMAL(10, 2) NOT NULL,
    Description TEXT,
    Stock INT DEFAULT 0
);

-- 주문 테이블
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY AUTO_INCREMENT,
    UserID INT NOT NULL,
    OrderDate DATETIME DEFAULT NOW(),
    TotalAmount DECIMAL(10, 2),
    Status VARCHAR(20) DEFAULT 'Pending',
    FOREIGN KEY (UserID) REFERENCES Users(UserID)
);

-- 주문상세 테이블
CREATE TABLE OrderDetails (
    OrderDetailID INT PRIMARY KEY AUTO_INCREMENT,
    OrderID INT NOT NULL,
    ProductID INT NOT NULL,
    Quantity INT NOT NULL,
    UnitPrice DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (OrderID) REFERENCES Orders(OrderID),
    FOREIGN KEY (ProductID) REFERENCES Products(ProductID)
);

4단계: 쿼리 작성 및 테스트

-- 1. 2025년 1월의 모든 주문
SELECT o.OrderID, u.Name, o.OrderDate, o.TotalAmount
FROM Orders o
JOIN Users u ON o.UserID = u.UserID
WHERE MONTH(o.OrderDate) = 1 AND YEAR(o.OrderDate) = 2025;

-- 2. 각 회원별 총 주문 금액
SELECT u.Name, COUNT(o.OrderID) as order_count, SUM(o.TotalAmount) as total_spent
FROM Users u
LEFT JOIN Orders o ON u.UserID = o.UserID
GROUP BY u.UserID, u.Name
ORDER BY total_spent DESC;

-- 3. 가장 인기 있는 상품 TOP 5
SELECT p.ProductName, COUNT(od.OrderDetailID) as times_ordered
FROM Products p
JOIN OrderDetails od ON p.ProductID = od.ProductID
GROUP BY p.ProductID, p.ProductName
ORDER BY times_ordered DESC
LIMIT 5;

헷갈렸던 점 / 실수 포인트

”정말 이 테이블 구조가 맞나?”

설계를 마친 후, 강사님이 한 질문이 정말 좋았다: “이 구조로 모든 필요한 쿼리를 짤 수 있나?”

내가 처음 설계할 때:

Users - Orders - Products (1:N:1)

문제: 한 주문에 여러 상품이 있을 때, Orders 테이블에 ProductID를 여러 개 넣을 수 없다.

해결:

Users - Orders - OrderDetails - Products

중간에 OrderDetails(주문상세) 테이블을 만들어서 해결했다.

교훈: “나중에 쿼리를 짤 때 어떤 조인이 필요할지를 먼저 생각하고 테이블을 설계해야 한다."

"DECIMAL vs INT vs VARCHAR, 어떤 타입을 써야 하나?”

각 데이터의 특성에 맞는 타입을 선택하는 게 중요하다.

-- 가격: DECIMAL (실수, 정밀도 중요)
Price DECIMAL(10, 2)  -- 최대 8자리 + 소수점 2자리

-- 나이: INT
Age INT

-- 이름: VARCHAR (가변 길이 문자열)
Name VARCHAR(50)

-- 설명: TEXT (긴 문자열)
Description TEXT

-- 날짜: DATETIME
CreatedAt DATETIME

-- 상태: VARCHAR (정해진 값만)
Status VARCHAR(20)  -- 'Pending', 'Shipped', 'Delivered'

타입 선택의 영향:

  • 저장 공간 효율성
  • 쿼리 성능
  • 데이터 무결성

”NOT NULL과 DEFAULT는 언제 쓰나?”

-- NOT NULL: 반드시 값이 있어야 함 (필수 항목)
Email VARCHAR(100) NOT NULL

-- DEFAULT: 값을 넣지 않으면 기본값 사용
CreatedAt DATETIME DEFAULT NOW()
Status VARCHAR(20) DEFAULT 'Pending'

-- NULL 허용: 값이 있을 수도, 없을 수도
Phone VARCHAR(20)  -- 전화번호가 없는 사용자도 있을 수 있음

원칙:

  • “반드시 있어야 한다” → NOT NULL
  • “없을 수도 있다” → NULL 허용
  • “보통 이 값을 사용한다” → DEFAULT 설정

복습 Q&A

Q. 한 테이블에 몇 개의 Foreign Key를 가질 수 있나?

A. 제한이 없다. 필요한 만큼 가질 수 있다.

예를 들어, OrderDetails 테이블:

CREATE TABLE OrderDetails (
    OrderDetailID INT PRIMARY KEY,
    OrderID INT NOT NULL,           -- FK 1: Orders 참조
    ProductID INT NOT NULL,         -- FK 2: Products 참조
    FOREIGN KEY (OrderID) REFERENCES Orders(OrderID),
    FOREIGN KEY (ProductID) REFERENCES Products(ProductID)
);

다만, FK가 너무 많으면 테이블이 복잡해지므로 설계를 다시 검토해야 한다.

Q. 같은 이메일을 가진 회원이 두 명 등록되면?

A. UNIQUE 제약조건으로 막는다.

CREATE TABLE Users (
    UserID INT PRIMARY KEY,
    Email VARCHAR(100) NOT NULL UNIQUE,  -- 중복 불가
    Name VARCHAR(50)
);

이 구조로 같은 이메일을 가진 회원을 두 번 등록하려면 에러가 난다.

Q. LEFT JOIN과 INNER JOIN은 언제 쓰는 차이?

A.

-- INNER JOIN: 양쪽 테이블 모두에 데이터가 있어야 함
SELECT u.Name, o.OrderID
FROM Users u
INNER JOIN Orders o ON u.UserID = o.UserID;
-- 주문이 있는 회원만 나옴

-- LEFT JOIN: 왼쪽 테이블의 모든 행을 보임 (오른쪽이 없으면 NULL)
SELECT u.Name, o.OrderID
FROM Users u
LEFT JOIN Orders o ON u.UserID = o.UserID;
-- 주문이 없는 회원도 나옴 (OrderID는 NULL)

“회원별로 주문 수를 집계할 때” → LEFT JOIN “주문과 회원의 정보를 함께 보일 때” → INNER JOIN

한 줄 정리

데이터베이스 설계는 프로그래밍의 기초 위에 세워지는 건축물이다. 처음 설계를 잘못하면 나중에 고치기가 매우 어렵다. 따라서 “나중에 이 데이터를 어떻게 쿼리할 것인가”를 먼저 생각하고 설계해야 한다.


한 주를 정리하며

월요일부터 오늘까지, 정말 밀도 있는 한 주였다.

  • 월요일: Computational Thinking (어떻게 생각할 것인가)
  • 화요일: 자료구조 (데이터를 어떻게 담을 것인가)
  • 수요일: 알고리즘 (효율적으로 어떻게 처리할 것인가)
  • 목요일: 개발 도구 (실전에서 어떻게 협업할 것인가)
  • 금요일: DB 개념 (데이터를 어떻게 영구보관할 것인가)
  • 토요일: SQL (데이터를 어떻게 조작할 것인가)
  • 일요일: NoSQL (다른 방식의 접근)
  • 월요일: DB 설계 (모든 개념을 종합해서 설계하기)

이제 느껴진다. 처음 월요일에 배웠던 Computational Thinking이 모든 주차의 기초였다는 걸.

“문제를 어떻게 분석할 것인가”부터 시작해서 “실제로 어떻게 구현하고 협업할 것인가”까지, 모든 게 연결되어 있다.

다음 주부터는 프로젝트 실습이 시작된다고 했다. 이번 주 배운 내용을 실제로 써먹을 때가 온 것 같다.

벌써 부트캠프 2주차를 손꼽아 기다리고 있다.

Community

Comments

0 comments

Comments appear immediately. Use report if something needs review.

No comments yet.