본문 바로가기
Python/Django

관계형 데이터베이스 보충

by 좌우지간에 2025. 1. 9.

관계형 데이터베이스 모델링 핵심 정리: 1:1, 1:N, N:M 관계(feat. Django ORM)

 

데이터베이스는 현대 웹 서비스의 핵심 요소이며, 효율적인 데이터 관리를 위해서는 테이블 간의 관계를 올바르게 설계하는 것이 매우 중요합니다. 오늘은 관계형 데이터베이스에서 흔히 사용되는 1:1, 1:N, N:M 관계를 살펴보고, 이러한 관계를 Django ORM에서 어떻게 정의하고 활용하는지 자세히 알아보겠습니다. 특히, OneToOneField, ForeignKey, ManyToManyField와 같은 Django ORM 필드의 사용법과 주의사항을 중점적으로 다루겠습니다.

 


 

1. 관계형 데이터베이스 모델링 기본

관계형 데이터베이스에서 테이블 간의 관계는 데이터를 구조화하고 데이터 무결성을 유지하는 데 중요한 역할을 합니다. 세 가지 기본적인 관계는 다음과 같습니다.

  • 1:1 관계 (One-to-One):
    • 하나의 레코드가 다른 테이블의 단 하나의 레코드와 연결되는 관계입니다.
    • 예시: 유저와 프로필 (각 유저는 하나의 프로필만 가짐), 사람과 주민등록번호 (각 사람은 하나의 주민등록번호만 가짐)
  • 1:N 관계 (One-to-Many):
    • 하나의 레코드가 다른 테이블의 여러 레코드와 연결되는 관계입니다.
    • 예시: 게시글과 댓글 (하나의 게시글에 여러 개의 댓글이 달릴 수 있음), 작가와 책 (한 명의 작가는 여러 권의 책을 쓸 수 있음)
  • N:M 관계 (Many-to-Many):
    • 여러 개의 레코드가 다른 테이블의 여러 개의 레코드와 연결되는 관계입니다.
    • 예시: 태그와 포스트 (하나의 포스트는 여러 개의 태그를 가질 수 있고, 하나의 태그는 여러 개의 포스트에 사용될 수 있음), 학생과 수업 (한 명의 학생은 여러 개의 수업을 들을 수 있고, 하나의 수업은 여러 명의 학생이 들을 수 있음)

 

 

2. Django ORM과 모델 필드

Django ORM(Object-Relational Mapper)은 파이썬 코드를 사용하여 데이터베이스를 조작할 수 있도록 도와주는 편리한 도구입니다. Django ORM에서 테이블 간의 관계는 모델의 필드를 통해 정의됩니다.

  • OneToOneField: 다른 모델과의 1:1 관계를 정의합니다. 참조되는 모델(예: User)은 오직 하나의 레코드만 참조할 수 있습니다.
    • on_delete 옵션: 참조하는 모델 객체가 삭제될 때 어떻게 할지를 결정합니다. (예: models.CASCADE, models.SET_NULL, models.PROTECT, models.SET_DEFAULT 등)
    • related_name: 참조 모델에서 역으로 참조할 때 사용할 이름을 설정합니다.
  • ForeignKey: 다른 모델과의 1:N 관계를 정의합니다. 참조하는 모델(예: Post)에 대한 외래 키를 다른 모델(예: Comment)에 설정합니다.
    • on_delete 옵션: 참조하는 모델 객체가 삭제될 때 어떻게 할지를 결정합니다.
    • related_name: 참조 모델에서 역으로 참조할 때 사용할 이름을 설정합니다.
  • ManyToManyField: 다른 모델과의 N:M 관계를 정의합니다. 내부적으로 중간 테이블을 생성하여 관계를 관리합니다.
    • through 옵션: 중간 테이블을 직접 커스터마이징할 때 사용합니다.
    • blank=True: 필드를 필수로 입력하지 않아도 되도록 설정합니다.

 

모델 필드의 주요 옵션:

  • max_length: CharField의 최대 길이를 설정합니다.
  • auto_now: 객체가 저장될 때마다 현재 시간으로 자동 업데이트합니다.
  • auto_now_add: 객체가 생성될 때 자동으로 현재 시간을 저장합니다.
  • blank: 필드를 필수로 입력하지 않아도 되도록 설정합니다.
  • null: 데이터베이스에 NULL 값을 허용합니다.
  • unique: 해당 필드의 값이 테이블에서 유일해야 합니다.

 

 

3. 실전 예시를 통한 관계 설정

이제 실제 Django 모델 코드를 통해 각 관계를 어떻게 설정하는지 알아보겠습니다.

 

1. User와 Profile (1:1 관계)


from django.conf import settings
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    bio = models.TextField()

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile")
    address = models.CharField(max_length=50)
    zipcode = models.CharField(max_length=6)
        

User 모델은 사용자 정보를 저장하며, AbstractUser를 상속받아 사용자 인증 기능을 제공합니다. bio 필드는 추가적인 사용자 정보를 저장합니다. Profile 모델은 사용자 프로필 정보를 저장하며, OneToOneField를 사용하여 User 모델과 1:1 관계를 설정합니다. 각 사용자는 하나의 프로필만 가질 수 있습니다.

 

핵심: OneToOneField를 사용하여 각 User 인스턴스는 오직 하나의 Profile 인스턴스에 연결됩니다.

 

2. Post와 Comment (1:N 관계)


from django.conf import settings
from django.db import models

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    message = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
    content = models.TextField()
        

Post 모델은 게시글 정보를 저장합니다. author 필드는 ForeignKey를 사용하여 사용자 모델과 1:N 관계를 설정하고, 게시글을 작성한 사용자를 나타냅니다. Comment 모델은 댓글 정보를 저장하며, ForeignKey를 사용하여 Post 모델과 1:N 관계를 설정합니다. 하나의 게시글에는 여러 개의 댓글이 달릴 수 있습니다.

 

핵심: ForeignKey를 사용하여 여러 개의 Comment 인스턴스가 하나의 Post 인스턴스를 참조할 수 있습니다.

 

 

3. Post와 Tag (N:M 관계)


from django.conf import settings
from django.db import models

class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    message = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    tag_set = models.ManyToManyField('Tag', blank=True)

    def __str__(self):
        return self.message

class Tag(models.Model):
    name = models.CharField(max_length=44)

    def __str__(self):
       return self.name
        

Post 모델은 게시글 정보를 저장하며, ManyToManyField를 사용하여 Tag 모델과 N:M 관계를 설정합니다. 하나의 게시글에는 여러 개의 태그를 달 수 있고, 하나의 태그는 여러 게시글에 사용될 수 있습니다. Tag 모델은 태그 이름을 저장합니다.

 

핵심: ManyToManyField는 내부적으로 중간 테이블을 생성하여 N:M 관계를 관리합니다.

 

settings.AUTH_USER_MODEL:

settings.AUTH_USER_MODEL은 Django 프로젝트의 settings.py 파일에 설정된 사용자 모델을 참조하는 설정 값입니다. 기본적으로 Django는 django.contrib.auth.models.User 모델을 사용합니다. 만약 커스텀 사용자 모델을 사용하고 있다면, settings.py 파일에서 AUTH_USER_MODEL을 커스텀 모델로 지정해야 합니다.

 

모델의 __str__ 메서드:

__str__ 메서드는 모델 객체를 문자열로 표현하는 방법을 정의합니다. Django Admin, 쉘 등에서 모델 객체를 출력할 때 __str__ 메서드가 호출됩니다.

 

 

4. ManyToManyField 사용 시 주의사항

  • 모델 선언 순서: ManyToManyField를 사용할 때 모델 간의 의존성에 따라 오류가 발생할 수 있습니다. 예를 들어, Tag 모델을 먼저 선언하고, Post 모델에서 ManyToManyFieldTag 모델을 참조해야 합니다.
  • 문자열 참조: 모델 선언 순서 문제를 해결하기 위해 ManyToManyField에서 모델을 참조할 때 클래스 이름 대신 문자열을 사용할 수 있습니다. ('Tag')
  • through 옵션: 중간 테이블을 직접 커스터마이징해야 하는 경우에 through 옵션을 사용할 수 있습니다.

 


결론

오늘은 관계형 데이터베이스에서 자주 사용되는 1:1, 1:N, N:M 관계와 Django ORM에서 이를 구현하는 방법을 알아보았습니다. OneToOneField, ForeignKey, ManyToManyField와 같은 Django ORM 필드를 이해하고 활용하면 데이터베이스를 효율적으로 설계하고 관리할 수 있습니다. ManyToManyField 사용 시 모델 선언 순서에 주의하고, settings.AUTH_USER_MODEL, on_delete 옵션, 모델 필드의 다양한 옵션들을 적절히 활용해야 합니다.