DB 설계 규칙 6가지
1. 한 칸에는 한 가지 정보만 들어가도록 만들어라.
데이터베이스 테이블을 설계할 때 ‘한 칸에는 한 가지 정보만 들어가야 한다.’라는 규칙을 지켜야 한다. 이 규칙이 뭔지 알아보기 위해 이 규칙을 안 지킨 사례를 먼저 살펴보자.
[사례 1]
students (학생)
id (PK) | 이름 | 연락처 |
---|---|---|
1 | 김철수 | 010-1234-5678, 010-8765-4321 |
2 | 이영희 | 010-1111-2222, 010-3333-4444 |
위 사례를 살펴보면 한 칸에 2가지 이상의 정보가 들어가 있는 걸 확인할 수 있다.
“왜 한 칸에 2가지 이상의 정보가 들어가면 안 되는 걸까?”
데이터를 삽입하거나 수정할 때 로직이 복잡해지고, 데이터 중복 및 무결성 문제가 발생할 가능성이 높아진다. 따라서 한 칸에는 한 가지 정보만 넣어야 한다.
한 칸에 2개 이상의 정보가 들어가 있을 땐?
한 칸에 2가지 이상의 정보가 들어가 있을 때는 테이블을 분리해야 한다. 테이블을 분리함으로써 한 칸에 한 가지 정보만 들어가도록 해야 한다.
[사례 1 - 테이블 분리]
students (학생)
id (PK) | 이름 |
---|---|
1 | 김철수 |
2 | 이영희 |
contacts (연락처)
id (PK) | 연락처 | 학생 id (FK) |
---|---|---|
1 | 010-1234-5678 | 1 |
2 | 010-8765-4321 | 1 |
3 | 010-1111-2222 | 2 |
4 | 010-3333-4444 | 2 |
위와 같이 테이블을 분리함으로써 한 칸에 한 가지 정보만 들어가게 만들었다.
✅ 또 다른 사례
[사례 2]
orders (주문)
id (PK) | 주문자 이름 | 주문 상품 |
---|---|---|
1 | 박지훈 | 노트북, 무선 마우스 |
2 | 김나연 | 스마트폰, 충전기, 이어폰 |
[사례 2 - 테이블 분리]
orders (주문)
id (PK) | 주문자 이름 |
---|---|
1 | 박지훈 |
2 | 김나연 |
products (상품)
id (PK) | 상품명 | 주문 id (FK) |
---|---|---|
1 | 노트북 | 1 |
2 | 무선 마우스 | 1 |
3 | 스마트폰 | 2 |
4 | 충전기 | 2 |
5 | 이어폰 | 2 |
이 과정을 데이터베이스 이론에서는 제1정규형(1NF)이라고 부른다. 학문적인 용어를 외우는 것보다, 테이블을 설계하고 데이터를 효율적으로 관리하는 능력을 기르는 것이 더 중요하다.
'한 가지 정보'라는게 관점에 따라 달라질 수 있다.
‘한 가지 정보’라는 기준은 절대적이지 않다. 데이터의 활용 목적이나 서비스의 요구사항에 따라 ‘한 가지 정보’로 볼 수 있는 관점이 달라질 수 있다.
[예시 1: 주소 정보]
address (주소)
id (PK) | 주소 |
---|---|
1 | 서울특별시 강남구 테헤란로 12 |
2 | 부산광역시 해운대구 우동 123 |
- 문제점: 시/도, 구/군, 상세주소가 하나의 칸에 통합되어 있다.
- 해결: 분리된 형태로 저장하여 검색 및 수정 편의성을 높인다.
[분리 후]
addresses (주소)
id (PK) | 시/도 | 구/군 | 상세주소 |
---|---|---|---|
1 | 서울특별시 | 강남구 | 테헤란로 12 |
2 | 부산광역시 | 해운대구 | 우동 123 |
[예시 2: 복합 키워드]
products (상품)
id (PK) | 상품명 | 키워드 |
---|---|---|
1 | 노트북 | 전자제품, 휴대용, 고성능 |
2 | 의자 | 가구, 인테리어, 편안함 |
- 문제점: 키워드가 한 칸에 여러 값으로 들어가 있어 필터링이나 검색이 어려움.
- 해결: 키워드를 별도 테이블로 분리하여 다대다 관계로 저장한다.
[분리 후]
products (상품)
id (PK) | 상품명 |
---|---|
1 | 노트북 |
2 | 의자 |
product_keywords (상품 키워드)
id (PK) | 상품 id (FK) | 키워드 |
---|---|---|
1 | 1 | 전자제품 |
2 | 1 | 휴대용 |
3 | 1 | 고성능 |
4 | 2 | 가구 |
5 | 2 | 인테리어 |
6 | 2 | 편안함 |
[예시 3: 다국어 지원]
menus (메뉴)
id (PK) | 메뉴명 |
---|---|
1 | 커피 (Coffee) |
2 | 차 (Tea) |
- 문제점: 다국어 데이터가 한 칸에 혼합되어 있다.
- 해결: 언어별로 메뉴명을 분리하여 저장한다.
[분리 후]
menus (메뉴)
id (PK) | 언어 | 메뉴명 |
---|---|---|
1 | ko | 커피 |
2 | en | Coffee |
3 | ko | 차 |
4 | en | Tea |
요약
- 한 칸에는 한 가지 정보만 들어가야 한다.
- 한 칸에 두 가지 이상의 정보가 들어가있을 땐, 테이블을 분리해서 FK를 활용하면 된다.
- 특정 테이블에 FK를 도입했을 때 규칙1이 안 지켜진다면, 다른 테이블로 FK를 옮겨본다.
- '한 가지 정보'의 기준은 절대적이지 않다. 따라서 서비스에 맞게 판단해야 한다.
DB 설계 규칙 6가지
2. 어떤 테이블에 FK를 넣어도 '규칙 1'을 못 지킬 때는 중간 매핑테이블을 하나 더 만들어라.
FK(외래 키)를 활용할 때도 한 칸에 한 가지 정보만 넣는 규칙(규칙 1)을 지키는 것이 중요하다. 하지만 어떤 상황에서는 특정 테이블에 FK를 넣어도 여전히 한 칸에 여러 정보가 들어가게 된다. 이 경우에는 중간 매핑 테이블을 추가하여 해결할 수 있다.
예시: 학생과 수강 과목
학생이 여러 과목을 수강할 수 있고, 한 과목에 여러 학생이 등록할 수 있는 다대다(M:N) 관계를 설계한다고 가정하자.
[잘못된 설계]
students (학생)
id (PK) | 이름 | 수강 과목 id (FK) |
---|---|---|
1 | 김철수 | 1, 2 |
2 | 이영희 | 2, 3 |
subjects (과목)
id (PK) | 과목명 |
---|---|
1 | 수학 |
2 | 과학 |
3 | 영어 |
- 문제점:
students
테이블의수강 과목 id
컬럼에 여러 값이 들어가 있어 규칙 1이 위배된다. - 해결 방법: 중간 매핑 테이블을 생성하여 학생과 과목의 관계를 별도로 관리한다.
[중간 매핑 테이블을 사용한 설계]
students (학생)
id (PK) | 이름 |
---|---|
1 | 김철수 |
2 | 이영희 |
subjects (과목)
id (PK) | 과목명 |
---|---|
1 | 수학 |
2 | 과학 |
3 | 영어 |
student_subjects (학생-과목 매핑)
id (PK) | 학생 id (FK) | 과목 id (FK) |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 2 |
4 | 2 | 3 |
- 설명:
student_subjects
라는 중간 매핑 테이블을 생성하여 학생과 과목 간의 관계를 관리한다. - 장점:
- 규칙 1을 지키며 다대다 관계를 효과적으로 표현할 수 있다.
- 추가적인 속성(예: 수강 신청 날짜, 점수 등)을 중간 매핑 테이블에 쉽게 추가할 수 있다.
예시: 주문과 제품
한 주문에 여러 제품이 포함될 수 있고, 한 제품이 여러 주문에 포함될 수 있는 다대다(M:N) 관계를 설계한다고 가정하자.
[잘못된 설계]
orders (주문)
id (PK) | 주문자 | 제품 id (FK) |
---|---|---|
1 | 박지훈 | 1, 2 |
2 | 김나연 | 2, 3 |
products (제품)
id (PK) | 제품명 |
---|---|
1 | 노트북 |
2 | 스마트폰 |
3 | 충전기 |
- 문제점:
orders
테이블의제품 id
컬럼에 여러 값이 들어가 있어 규칙 1이 위배된다. - 해결 방법: 중간 매핑 테이블을 생성하여 주문과 제품의 관계를 별도로 관리한다.
[중간 매핑 테이블을 사용한 설계]
orders (주문)
id (PK) | 주문자 |
---|---|
1 | 박지훈 |
2 | 김나연 |
products (제품)
id (PK) | 제품명 |
---|---|
1 | 노트북 |
2 | 스마트폰 |
3 | 충전기 |
order_products (주문-제품 매핑)
id (PK) | 주문 id (FK) | 제품 id (FK) |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 2 |
4 | 2 | 3 |
- 설명:
order_products
라는 중간 매핑 테이블을 생성하여 주문과 제품 간의 관계를 관리한다. - 장점:
- 다대다 관계를 효과적으로 분리하고, 규칙 1을 지킨다.
- 주문별 제품 수량, 할인율 등 추가 정보를 매핑 테이블에 쉽게 저장할 수 있다.
중간 매핑 테이블을 사용하는 것은 다대다 관계를 표현할 때뿐만 아니라, 복잡한 데이터 관계를 단순화하고 데이터 무결성을 유지하는 데 매우 유용하다.
DB 설계 규칙 6가지
3. 헷갈릴 땐 관계(1:1, 1:N, N:M)를 파악해봐라
데이터를 설계할 때 가장 중요한 것은 테이블 간의 관계를 파악하는 것이다. 관계를 명확히 파악하면 테이블을 어떻게 설계해야 할지 쉽게 결정할 수 있다. 관계는 크게 1:1 관계, 1:N 관계, N:M 관계로 나눌 수 있다.
1:1 관계
1:1 관계는 한 엔티티가 다른 엔티티와 정확히 하나의 데이터와 연결될 때 발생한다.
[예시: 사용자와 사용자 프로필]
- 사용자 정보는
users
테이블에, 프로필 정보는profiles
테이블에 저장된다. - 각 사용자는 하나의 프로필만 가질 수 있다.
[테이블 설계]
users (사용자)
id (PK) | 이름 |
---|---|
1 | 김철수 |
2 | 이영희 |
profiles (프로필)
id (PK) | 사용자 id (FK) | 프로필 이미지 | 자기소개 |
---|---|---|---|
1 | 1 | profile1.jpg | 안녕하세요, 김철수입니다. |
2 | 2 | profile2.jpg | 안녕하세요, 이영희입니다. |
- 설명:
profiles
테이블의사용자 id
는users
테이블의id
와 1:1로 매칭된다. - 활용: 테이블 분리를 통해 데이터 접근 속도를 높이고, 각 테이블의 역할을 명확히 할 수 있다.
1:N 관계
1:N 관계는 하나의 엔티티가 여러 개의 다른 엔티티와 연결될 때 발생한다.
[예시: 카테고리와 제품]
- 하나의 카테고리는 여러 제품을 포함할 수 있다.
- 각 제품은 하나의 카테고리에만 속한다.
[테이블 설계]
categories (카테고리)
id (PK) | 카테고리명 |
---|---|
1 | 전자제품 |
2 | 가구 |
products (제품)
id (PK) | 제품명 | 카테고리 id (FK) |
---|---|---|
1 | 노트북 | 1 |
2 | 스마트폰 | 1 |
3 | 의자 | 2 |
4 | 테이블 | 2 |
- 설명:
products
테이블의카테고리 id
는categories
테이블의id
를 참조하여 여러 제품이 하나의 카테고리에 연결된다. - 활용: 각 카테고리별로 제품을 쉽게 조회할 수 있다.
N:M 관계
N:M 관계는 여러 엔티티가 여러 엔티티와 연결될 때 발생한다. 이를 표현하기 위해 중간 매핑 테이블이 필요하다.
[예시: 학생과 과목]
- 학생은 여러 과목을 수강할 수 있고, 한 과목에는 여러 학생이 등록될 수 있다.
[테이블 설계]
students (학생)
id (PK) | 이름 |
---|---|
1 | 김철수 |
2 | 이영희 |
subjects (과목)
id (PK) | 과목명 |
---|---|
1 | 수학 |
2 | 과학 |
3 | 영어 |
student_subjects (학생-과목 매핑)
id (PK) | 학생 id (FK) | 과목 id (FK) |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 2 |
4 | 2 | 3 |
- 설명:
student_subjects
테이블은 학생과 과목 간의 N:M 관계를 중간 매핑 테이블로 관리한다. - 활용: N:M 관계를 유연하게 처리하고 추가 정보를 저장할 수 있다.
엔티티 관계 파악 방법
1. 엔티티 간에 어울리는 동사 찾기
관계를 파악할 때는 두 엔티티 간의 의미를 동사로 표현해보면 명확해진다.
A(주어)가 B를 _____.
(A가 주어)- 예: 학생이 과목을 수강한다.
- 결과: 학생-과목은 N:M 관계.
B(주어)가 A에 의해 _____.
(B가 주어)- 예: 과목이 학생에 의해 수강된다.
- 결과: 과목-학생은 N:M 관계.
2. 관계를 기준으로 테이블 분리 여부 결정
- 1:1 관계: 엔티티를 분리하고 FK로 연결.
- 1:N 관계: N 테이블에 FK 추가.
- N:M 관계: 중간 매핑 테이블을 생성.
DB 설계 규칙 6가지
4. 데이터 중복이 발생하는 컬럼이 있는지 확인해라
데이터베이스 설계 시 데이터 중복이 발생하면 저장 공간 낭비, 데이터 무결성 문제, 유지보수 비용 증가 등의 문제가 발생할 수 있다. 따라서, 데이터 중복 가능성을 미리 시뮬레이션하여 중복이 발생하는 컬럼을 찾아야 한다.
데이터 중복이란?
- 동일한 데이터가 여러 행에 반복적으로 저장되는 경우를 의미한다.
- 예를 들어, 고객의 도시 정보가 여러 고객 레코드에서 반복된다면 이는 데이터 중복의 한 예이다.
데이터 중복 사례
[예시: 고객과 도시 정보]
[잘못된 설계]
customers (고객)
id (PK) | 이름 | 도시 |
---|---|---|
1 | 김철수 | 서울특별시 강남구 |
2 | 이영희 | 서울특별시 강남구 |
3 | 박지훈 | 부산광역시 해운대구 |
4 | 최민수 | 서울특별시 강남구 |
- 문제점:
- 도시 정보가 여러 행에서 반복적으로 저장된다.
- 저장 공간 낭비 및 데이터 변경 시 모든 행을 수정해야 하는 비효율 발생.
[해결 방법: 테이블 분리]
- 도시 정보를 별도의 테이블로 분리하고, FK로 참조하도록 설계한다.
[분리 후 설계]
customers (고객)
id (PK) | 이름 | 도시 id (FK) |
---|---|---|
1 | 김철수 | 1 |
2 | 이영희 | 1 |
3 | 박지훈 | 2 |
4 | 최민수 | 1 |
cities (도시)
id (PK) | 도시 이름 |
---|---|
1 | 서울특별시 강남구 |
2 | 부산광역시 해운대구 |
- 장점:
- 도시 정보는
cities
테이블에 한 번만 저장된다. - 중복된 도시 정보를 제거하여 저장 공간을 절약하고, 도시 데이터 수정 시 단일 위치에서 처리 가능.
- 도시 정보는
데이터 중복 시뮬레이션
데이터 중복 여부를 확인하려면 기존 데이터를 분석하거나 샘플 데이터를 시뮬레이션하여 중복된 값의 빈도를 확인한다.
시뮬레이션 절차
- 테이블에서 특정 컬럼을 기준으로 그룹화하고 중복된 값의 빈도를 확인.
SELECT 도시, COUNT(*) FROM customers GROUP BY 도시 HAVING COUNT(*) > 1;
DB 설계 규칙 6가지
5. 가짜 중복과 진짜 중복을 구별해라
데이터베이스 설계 시 데이터를 중복 제거해야 하지만, 중복으로 보이는 데이터가 항상 잘못된 것은 아니다. 따라서, 데이터가 가짜 중복인지, 진짜 중복인지 구별하는 것이 중요하다.
가짜 중복과 진짜 중복의 차이
- 가짜 중복: 데이터가 반복적으로 나타나지만, 서비스 상에서 별개의 데이터로 취급되어야 한다.
- 진짜 중복: 동일한 데이터가 반복적으로 저장되어 수정 시 다른 데이터도 영향을 받아야 한다.
가짜 중복인지 진짜 중복인지 판단하려면 이렇게 질문해보면 된다.
“실제 서비스에서 A 데이터의 값을 수정하면, B 데이터의 값도 같이 수정되어야 하는가?”
예시 1: 고객의 주문 내역
[잘못된 설계: 모든 주문에서 고객 이름을 저장]
orders (주문)
id (PK) | 고객 이름 | 주문 상품 | 주문 날짜 |
---|---|---|---|
1 | 김철수 | 노트북 | 2024-01-01 |
2 | 김철수 | 마우스 | 2024-01-02 |
3 | 이영희 | 스마트폰 | 2024-01-03 |
- 문제점: 고객 이름이 여러 주문에서 반복적으로 저장됨.
- 진짜 중복인지 확인: 만약 김철수의 이름을 "김영수"로 수정한다면, 모든 주문 내역에서도 수정되어야 함.
- 결과: 진짜 중복 → 고객 정보를 별도 테이블로 분리해야 함.
[분리 후 설계]
customers (고객)
id (PK) | 이름 |
---|---|
1 | 김철수 |
2 | 이영희 |
orders (주문)
id (PK) | 고객 id (FK) | 주문 상품 | 주문 날짜 |
---|---|---|---|
1 | 1 | 노트북 | 2024-01-01 |
2 | 1 | 마우스 | 2024-01-02 |
3 | 2 | 스마트폰 | 2024-01-03 |
- 해결: 고객 이름을 수정하면 모든 주문 내역에서 변경 사항이 반영됨.
예시 2: 제품의 주문 기록
[잘못된 설계: 제품 이름이 중복 저장]
orders (주문)
id (PK) | 제품명 | 수량 | 주문 날짜 |
---|---|---|---|
1 | 노트북 | 1 | 2024-01-01 |
2 | 노트북 | 2 | 2024-01-02 |
3 | 스마트폰 | 1 | 2024-01-03 |
- 문제점: 제품명이 여러 주문에서 반복적으로 저장됨.
- 진짜 중복인지 확인: 만약 "노트북" 이름을 "울트라 노트북"으로 변경하면 모든 주문 기록에서 변경되어야 함.
- 결과: 진짜 중복 → 제품 정보를 별도 테이블로 분리해야 함.
[분리 후 설계]
products (제품)
id (PK) | 제품명 |
---|---|
1 | 노트북 |
2 | 스마트폰 |
orders (주문)
id (PK) | 제품 id (FK) | 수량 | 주문 날짜 |
---|---|---|---|
1 | 1 | 1 | 2024-01-01 |
2 | 1 | 2 | 2024-01-02 |
3 | 2 | 1 | 2024-01-03 |
- 해결: 제품 이름을 변경하면 모든 주문 내역에 자동으로 반영됨.
예시 3: 주문 내역에서 할인 쿠폰
[잘못된 설계: 쿠폰 정보 중복 저장]
orders (주문)
id (PK) | 주문자 | 쿠폰 코드 | 할인 금액 |
---|---|---|---|
1 | 김철수 | SAVE10 | 10,000 |
2 | 이영희 | SAVE10 | 10,000 |
3 | 박지훈 | SAVE20 | 20,000 |
- 문제점: 쿠폰 정보가 여러 주문에서 반복적으로 저장됨.
- 진짜 중복인지 확인: "SAVE10" 쿠폰의 할인 금액이 변경되면 모든 주문 내역에 반영되어야 함.
- 결과: 진짜 중복 → 쿠폰 정보를 별도 테이블로 분리해야 함.
[분리 후 설계]
coupons (쿠폰)
id (PK) | 쿠폰 코드 | 할인 금액 |
---|---|---|
1 | SAVE10 | 10,000 |
2 | SAVE20 | 20,000 |
orders (주문)
id (PK) | 주문자 | 쿠폰 id (FK) |
---|---|---|
1 | 김철수 | 1 |
2 | 이영희 | 1 |
3 | 박지훈 | 2 |
- 해결: 쿠폰 할인 금액을 변경하면 모든 주문 내역에서 자동으로 반영됨.
정리
가짜 중복인지 진짜 중복인지 판단 질문
- 데이터 A와 B가 동일하다면, 데이터 A를 변경했을 때 B도 변경되어야 하는가?
- YES: 진짜 중복 → 테이블 분리.
- NO: 가짜 중복 → 분리 필요 없음.
이 과정을 통해 데이터 중복을 제대로 관리하면 저장 공간을 절약하고, 데이터 무결성을 유지하며, 유지보수 비용을 줄일 수 있다.
DB 설계 규칙 6가지
6. 숨어있는 중복을 찾아라
데이터베이스 설계 시 숨어있는 중복은 명시적으로 드러나지 않지만, 설계의 비효율로 인해 데이터 무결성과 관리에 문제를 야기할 수 있다. 특히, 관련된 데이터를 별도의 테이블로 관리할 경우 숨은 중복이 발생할 가능성이 높다.
예시: 조회수 관리에서의 숨은 중복
[잘못된 설계: 게시물 테이블에서 조회수를 관리]
posts (게시물)
id (PK) | 제목 | 내용 | 조회수 |
---|---|---|---|
1 | 게시물 1 | 내용 1 | 100 |
2 | 게시물 2 | 내용 2 | 50 |
- 문제점:
- 조회수는 동적인 데이터로, 게시물 테이블의 다른 정적인 데이터(제목, 내용)와 성격이 다르다.
- 조회수 업데이트가 자주 발생하면 게시물 테이블에 불필요한 수정 작업이 자주 일어나 성능에 영향을 미친다.
[해결 방법1: 조회수를 별도의 테이블로 분리]
posts (게시물)
id (PK) | 제목 | 내용 |
---|---|---|
1 | 게시물 1 | 내용 1 |
2 | 게시물 2 | 내용 2 |
post_views (조회수)
id (PK) | 게시물 id (FK) | 조회수 |
---|---|---|
1 | 1 | 100 |
2 | 2 | 50 |
- 장점:
- 조회수 업데이트는
post_views
테이블만 수정하므로,posts
테이블의 성능에 영향을 주지 않음. - 조회수와 게시물 데이터를 독립적으로 관리할 수 있음.
- 조회수 업데이트는
[해결 방법: 조회수를 동적으로 계산]
- 조회수를 별도로 저장하지 않고, 조회 기록 테이블을 기반으로 조회수를 계산하도록 설계.
posts (게시물)
id (PK) | 제목 | 내용 |
---|---|---|
1 | 게시물 1 | 내용 1 |
2 | 게시물 2 | 내용 2 |
post_views (조회 기록)
id (PK) | 게시물 id (FK) | 사용자 id | 조회 일시 |
---|---|---|---|
1 | 1 | 101 | 2024-01-01 10:00 |
2 | 1 | 102 | 2024-01-01 10:05 |
3 | 2 | 103 | 2024-01-01 11:00 |
- 조회수 계산 쿼리 예시
SELECT 게시물.id, 제목, 내용, COUNT(post_views.id) AS 조회수 FROM posts LEFT JOIN post_views ON posts.id = post_views.게시물_id GROUP BY posts.id, 제목, 내용;
'강의 정리 > 비전공자도 이해할 수 있는 DB 설계 입문_실전' 카테고리의 다른 글
DB 설계의 핵심원칙 및 전체 과정 (1) | 2024.12.26 |
---|---|
DB 설계 전 필수로 알아야 하는 개념 (1) | 2024.12.26 |