DB 설계
데이터베이스(DB) 설계는 서비스의 데이터를 어떻게 저장하고 관리할지 정하는 과정입니다. 잘 설계된 데이터베이스는 데이터가 꼬이지 않게 지켜 주고, 검색 속도를 높이며, 확장하기 쉽습니다. URL 설계와 마찬가지로 이 책에서 DB 설계는 Claude에게 시켜서 받아 보고, 표와 그림으로 검토하는 대상입니다.
1. 데이터베이스란
데이터베이스는 데이터를 체계적으로 저장하는 공간입니다. 엑셀 시트를 떠올리면 이해하기 쉽습니다.
| id | 제목 | 내용 | 작성자 | 작성일 |
|---|---|---|---|---|
| 1 | 첫 번째 글 | 안녕하세요 | 홍길동 | 2025-01-01 |
| 2 | 두 번째 글 | 반갑습니다 | 김철수 | 2025-01-02 |
위와 같은 표 하나를 테이블(Table) 이라고 합니다. 데이터베이스는 이런 테이블들의 모음입니다. 엑셀 파일 하나에 시트가 여러 장 있는 것과 같습니다.
- 테이블(Table): 데이터를 저장하는 표
- 행(Row): 하나의 데이터 레코드 (예: 하나의 게시글)
- 열(Column): 데이터의 속성 (예: 제목, 내용, 작성자)
- 기본키(Primary Key): 각 행을 고유하게 식별하는 값 (예: id)
2. 테이블 간의 관계
실제 서비스에서는 여러 테이블이 서로 연결되어 있습니다. 이 연결 관계를 이해하는 것이 DB 설계의 핵심입니다. 엑셀에서 '주문' 시트의 고객 이름이 '고객' 시트의 이름과 맞아야 하는 것과 같은 이야기입니다.
2.1 일대다 관계
일대다 관계 1:N는 가장 흔한 관계입니다. 하나의 레코드가 여러 개의 레코드와 연결됩니다.
- 하나의 사용자가 여러 개의 게시글을 작성할 수 있음
- 하나의 게시글에 여러 개의 댓글이 달릴 수 있음
- 하나의 카테고리에 여러 개의 상품이 속할 수 있음
2.2 다대다 관계
다대다 관계 N:M는 양쪽 모두 여러 개와 연결될 수 있는 관계입니다. 중간 테이블이 필요합니다.
- 한 학생이 여러 강좌를 수강하고, 한 강좌에 여러 학생이 등록
- 한 게시글에 여러 태그, 한 태그가 여러 게시글에 사용
2.3 일대일 관계
일대일 관계 1:1는 하나의 레코드가 정확히 하나의 레코드와 연결됩니다.
- 한 사용자는 하나의 프로필만 가짐
3. Claude에게 DB 설계 시키기
AI를 활용하면 복잡한 DB 설계도 빠르게 시작할 수 있습니다.
3.1 기본 프롬프트
앞 절에서 만든 설계.md 파일이 폴더에 있다면 @로 지목해서 이어 붙이면 됩니다.
@설계.md 의 URL 설계를 바탕으로 데이터베이스를 설계해줘.
Django 모델 기준으로 각 테이블의 필드와 관계를 표로 설명해주고,
ERD(Entity Relationship Diagram)도 Mermaid로 그려줘.
아직 코드는 만들지 말고, 결과는 설계.md 파일 아래에 이어서 저장해줘.
3.2 블로그 서비스 예시
설계.md가 없다면 아래처럼 기능을 다시 적어 주면 됩니다.
나는 개인 블로그를 만들려고 해.
[주요 기능]
- 사용자 회원가입, 로그인
- 게시글 작성, 수정, 삭제
- 카테고리별 게시글 분류
- 댓글 작성, 삭제
Django 모델 기준으로 데이터베이스를 설계해줘.
각 테이블의 필드와 관계를 표로 설명해주고,
ERD도 Mermaid로 그려줘. 아직 코드는 만들지 마.
AI는 다음과 같은 설계를 제안할 것입니다.
User (사용자)
Django의 기본 User 모델을 사용하거나 확장합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
| id | Integer | 기본키, 자동 증가 |
| username | CharField | 사용자 아이디 |
| EmailField | 이메일 주소 | |
| password | CharField | 비밀번호 (암호화) |
| created_at | DateTimeField | 가입일 |
Category (카테고리)
| 필드 | 타입 | 설명 |
|---|---|---|
| id | Integer | 기본키, 자동 증가 |
| name | CharField | 카테고리 이름 |
| slug | SlugField | URL용 식별자 |
Post (게시글)
| 필드 | 타입 | 설명 |
|---|---|---|
| id | Integer | 기본키, 자동 증가 |
| title | CharField | 제목 |
| content | TextField | 내용 |
| author | ForeignKey(User) | 작성자 (1:N) |
| category | ForeignKey(Category) | 카테고리 (1:N) |
| created_at | DateTimeField | 작성일 |
| updated_at | DateTimeField | 수정일 |
Comment (댓글)
| 필드 | 타입 | 설명 |
|---|---|---|
| id | Integer | 기본키, 자동 증가 |
| content | TextField | 댓글 내용 |
| author | ForeignKey(User) | 작성자 (1:N) |
| post | ForeignKey(Post) | 게시글 (1:N) |
| created_at | DateTimeField | 작성일 |
3.3 무엇을 확인하나
여기서도 여러분이 볼 것은 타입이 아니라 '칸'입니다. 엑셀 시트를 검토하듯 보시면 됩니다.
- 칸이 빠지지 않았는가: 게시글에 '조회수'가 필요한데 없다면 "Post에 조회수 필드를 추가해줘"라고 합니다. 나중에 추가해도 되지만 처음에 넣는 편이 데이터가 꼬이지 않습니다.
- 관계가 내 생각과 같은가: 댓글이 게시글에 달리는 것이 맞는지, 한 게시글이 카테고리 여러 개에 속할 수 있어야 하는지 같은 것입니다. 후자라면 일대다가 아니라 다대다여야 합니다. "게시글 하나가 카테고리 여러 개에 들어갈 수 있게 해줘"라고 말하면 Claude가 관계를 바꿔 줍니다.
- 모르는 테이블이 있는가: 요청하지 않은 테이블이 생겼다면 왜 필요한지 물어보세요. 설명을 듣고 납득되면 두고, 아니면 지웁니다.
4. DB 설계 시각화 (ERD)
ERD(Entity Relationship Diagram)는 테이블 간의 관계를 시각적으로 표현합니다. Mermaid를 사용하면 쉽게 그릴 수 있습니다. 여러분은 AI가 생성한 ERD를 참고하여 데이터베이스 구조를 한눈에 파악하고 추가할 것을 고민하고, 필요하면 수정 요청을 하여 완성도를 높여갈 수 있습니다. 예를 들어, '유저에는 프로필 사진이 있으면 좋겠다'라든지 '게시글에 태그 기능이 있으면 좋겠다' 같은 추가 요구사항을 반영할 수 있습니다. 중요한 포인트는 int나 string 같은 필드 타입을 이해하는 것이 아니라, 테이블에 있는 필드의 역할, 그리고 테이블 간의 관계를 이해하는 것입니다.
4.1 블로그 ERD
5. Django 모델로 구현된 모습
설계가 끝나고 "이제 이 설계대로 만들어줘"라고 하면 Claude가 코드를 씁니다. 그중 데이터베이스에 해당하는 코드가 models.py라는 파일에 들어갑니다. 여러분이 쓸 일은 없지만, 폴더에서 이 파일을 열어 봤을 때 앞의 표와 어떻게 대응되는지는 알아 두면 좋습니다. 표의 한 줄이 코드 한 줄입니다.
5.1 블로그 모델 예시
# models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.author}의 댓글'
5.2 주요 필드 타입
| 필드 타입 | 설명 | 예시 |
|---|---|---|
CharField | 짧은 문자열 | 제목, 이름 |
TextField | 긴 문자열 | 본문, 설명 |
IntegerField | 정수 | 가격, 수량 |
DecimalField | 소수점 숫자 | 정확한 가격 |
BooleanField | 참/거짓 | 공개 여부 |
DateTimeField | 날짜와 시간 | 작성일, 수정일 |
EmailField | 이메일 | 사용자 이메일 |
ImageField | 이미지 파일 | 프로필 사진, 상품 이미지 |
ForeignKey | 일대다 관계 | 게시글의 작성자 |
ManyToManyField | 다대다 관계 | 게시글의 태그들 |
5.3 관계 설정 옵션
on_delete라는 옵션 하나는 알아 두면 좋습니다. 사용자를 지웠을 때 그 사람이 쓴 글을 어떻게 할지 정하는 옵션입니다. 이것은 기술 판단이 아니라 서비스 판단이라 Claude가 아니라 여러분이 정해야 합니다. "회원이 탈퇴해도 글은 남겨줘"라고 말하면 Claude가 아래 옵션을 바꿔 줍니다.
# 일대다 관계 (ForeignKey)
author = models.ForeignKey(
User,
on_delete=models.CASCADE, # 사용자 삭제 시 게시글도 삭제
related_name='posts' # 역참조 이름: user.posts.all()
)
# on_delete 옵션
# CASCADE: 함께 삭제
# SET_NULL: NULL로 설정 (null=True 필요)
# SET_DEFAULT: 기본값으로 설정 (default 필요)
# PROTECT: 삭제 방지 (에러 발생)
데이터베이스는 코드와 달리 되돌리기가 어렵습니다. 코드는 /rewind로 돌아갈 수 있지만, 이미 들어간 데이터가 지워지면 그만입니다. 실제 사용자가 생긴 뒤에 테이블 구조를 바꿀 때는 Code 탭에 "바꾸기 전에 db.sqlite3를 백업해줘"라고 먼저 말하는 습관을 들이세요.