본문 바로가기
Python/Django

[TIL] ForeignKey 보충 (코드예시)

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

📌 ForeignKey 보충설명 🔗

관계형 데이터베이스(RDBMS)에서 테이블 간의 관계를 정의하고, 데이터 무결성을 유지하는 데 필수적인 도구인 ForeignKey를 좀 더 쉽게 이해할 수 있도록 내용을 보충하여 다시 정리했습니다.


1. ForeignKey란? 왜 중요한가? 🤔

  • 데이터베이스 관계의 핵심 연결 고리: ForeignKey하나의 테이블(자식 테이블)의 레코드가 다른 테이블(부모 테이블)의 특정 레코드를 참조하도록 연결하는 역할을 합니다. 마치 레고 블록을 연결하여 더 큰 구조물을 만드는 것처럼, ForeignKey는 테이블들을 연결하여 더 복잡하고 의미 있는 데이터 관계를 만들 수 있도록 해줍니다.
  • 데이터 무결성의 수호자: 데이터베이스에서 ForeignKey는 자식 테이블에 저장되는 값이 반드시 부모 테이블에 존재하는 값이어야 한다는 규칙을 강제합니다. 이는 데이터가 엉뚱하게 연결되거나, 존재하지 않는 데이터를 참조하는 것을 방지하여 데이터의 정확성과 신뢰성을 높여줍니다.
  • 데이터 일관성 유지: ForeignKey는 데이터베이스 내에서 테이블 간의 관계를 정의하고 일관성을 유지하는 데 중요한 역할을 합니다. 만약 ForeignKey가 없다면 테이블들이 제멋대로 연결될 수 있고, 데이터의 일관성이 깨질 수 있습니다.
  • JOIN 연산의 핵심 기반: ForeignKeyJOIN 연산의 핵심적인 기반이 됩니다. JOIN 연산을 통해 관련된 여러 테이블의 데이터를 효율적으로 검색하고 조합할 수 있으며, 이는 데이터 분석이나 복잡한 정보를 검색할 때 필수적입니다.

2. ForeignKey, 어떻게 활용하는가? 다양한 관계 표현 🎯

ForeignKey를 사용하여 데이터베이스에서 다양한 관계를 어떻게 표현하는지 예시를 통해 좀 더 쉽게 이해해 보겠습니다.

  • 1대다(1:N) 관계: 작성자와 게시글

    • 개념: 한 명의 작성자(Author)가 여러 개의 게시글(Article)을 작성할 수 있는 관계를 의미합니다. 마치 한 명의 선생님이 여러 학생들을 가르치는 것과 비슷합니다.

    • 구현: Article 테이블에 author 필드를 ForeignKey로 설정하여 Author 테이블을 참조합니다.

      from django.db import models
      
      class Author(models.Model):
          name = models.CharField(max_length=100)
      
          def __str__(self):
              return self.name
      
      class Article(models.Model):
          title = models.CharField(max_length=200)
          content = models.TextField()
          # Article 모델의 author 필드는 Author 모델을 참조하는 외래 키
          author = models.ForeignKey(
              Author, on_delete=models.CASCADE, related_name="articles"
          )
          def __str__(self):
              return self.title

      * **설명:** `Article` 모델의 `author` 필드는 `Author` 모델의 `id` 값을 참조하여 작성자와 게시글을 연결합니다. 마치 게시글에 "이 게시글은 누구누구 선생님이 작성했다"라고 표시하는 것과 같습니다.
  • 1대1(1:1) 관계: 사용자와 프로필

    • 개념: 한 명의 사용자(User)는 단 하나의 프로필(Profile) 정보만을 가지는 관계를 의미합니다. 마치 한 사람이 주민등록증을 하나만 가지고 있는 것과 같습니다.

    • 구현: Profile 테이블에 user 필드를 OneToOneField로 설정하여 User 테이블을 참조합니다.

      from django.db import models
      
      class User(models.Model):
          username = models.CharField(max_length=100, unique=True)
          email = models.EmailField(unique=True)
      
      class Profile(models.Model):
          # Profile 모델의 user 필드는 User 모델을 참조하는 OneToOneField
          user = models.OneToOneField(
              User, on_delete=models.CASCADE, related_name="profile"
          )
          phone_number = models.CharField(max_length=20, blank=True)
          address = models.CharField(max_length=200, blank=True)

      * **설명:** `Profile` 모델의 `user` 필드는 `User` 모델의 `id` 값을 참조하여 사용자와 프로필을 연결합니다. `OneToOneField`는 `ForeignKey`와 유사하지만, 하나의 사용자가 하나의 프로필만 가질 수 있도록 데이터베이스 레벨에서 보장합니다.
  • 자체 참조(Self-Referential) 관계: 댓글 스레드

    • 개념: 댓글(Comment)이 다른 댓글에 답글을 다는 것처럼, 하나의 테이블에서 자기 자신을 참조하는 관계를 의미합니다. 마치 거울 속의 거울처럼, 무한히 이어질 수 있는 구조입니다.
    • 구현: Comment 테이블에 parent 필드를 ForeignKey로 설정하여 자기 자신을 참조합니다.
    ```python
    from django.db import models

    class Comment(models.Model):
        content = models.TextField()
        # Comment 모델의 parent 필드는 자기 자신을 참조하는 외래 키
        parent = models.ForeignKey(
            'self', on_delete=models.CASCADE, null=True, blank=True, related_name="replies"
        )
    ```
    <br>
    *   **설명:** `Comment` 모델의 `parent` 필드는 자기 자신(`Comment` 모델)을 참조하여 댓글 간의 계층 구조를 형성합니다. 댓글이 댓글에 답글을 달고, 그 답글에 또 답글을 다는 댓글 스레드를 만들 때 유용합니다.
  • 다대다(N:M) 관계: 게시글과 태그 (ManyToMany 활용)

    • 개념: 하나의 게시글(Article)이 여러 개의 태그(Tag)를 가질 수 있고, 하나의 태그도 여러 게시글에 사용될 수 있는 관계를 의미합니다. 마치 여러 학생이 여러 개의 과목을 수강할 수 있는 것과 같습니다.

    • 구현: Article 모델에 tags 필드를 ManyToManyField로 설정하여 Tag 모델과 다대다 관계를 설정합니다.

      from django.db import models
      
      class Article(models.Model):
          title = models.CharField(max_length=200)
          content = models.TextField()
          # Article 모델의 tags 필드는 Tag 모델과 다대다 관계를 설정하는 ManyToManyField
          tags = models.ManyToManyField(
               'Tag', related_name="articles"
         )
      
      class Tag(models.Model):
          name = models.CharField(max_length=100, unique=True)

      * **설명:** `Article` 모델의 `tags` 필드는 `Tag` 모델과 다대다 관계를 설정합니다. `ManyToManyField`를 사용하면 Django는 자동으로 중간 테이블을 생성하고 관리하여 다대다 관계를 편리하게 표현할 수 있도록 도와줍니다.

3. ForeignKey 옵션: 데이터 무결성을 지키는 방법 🛡️

ForeignKey를 사용할 때 중요한 것은 데이터 무결성을 유지하는 것입니다. on_delete 옵션을 통해 참조하는 데이터가 삭제되었을 때, 어떤 동작을 수행할지 미리 설정할 수 있습니다.

  • on_delete=models.CASCADE: 참조하는 부모 데이터가 삭제되면, 해당 부모 데이터를 참조하는 자식 데이터도 함께 삭제합니다. 마치 건물을 폭파시킬 때, 연결된 파이프라인도 함께 부숴버리는 것과 같습니다.
  • on_delete=models.PROTECT: 참조하는 부모 데이터가 삭제되는 것을 막습니다. 마치 중요한 건물을 폭파하려는 사람을 막는 것처럼, 데이터 삭제를 보호합니다.
  • on_delete=models.SET_NULL: 참조하는 부모 데이터가 삭제되면, 해당 자식 데이터의 ForeignKey 값을 NULL로 설정합니다. 마치 건물이 폭파되면 연결된 파이프라인은 그대로 두지만, 더 이상 연결되어 있지 않게 만드는 것과 같습니다. (null=True 옵션이 필요합니다.)
  • on_delete=models.SET_DEFAULT: 참조하는 부모 데이터가 삭제되면, 해당 자식 데이터의 ForeignKey 값을 미리 정해둔 기본값으로 설정합니다. 마치 건물이 폭파되면 연결된 파이프라인을 새로운 연결 장소로 이어주는 것과 같습니다. (default 옵션이 필요합니다.)
  • on_delete=models.DO_NOTHING: 아무런 동작도 하지 않습니다. 데이터 불일치를 유발할 수 있으므로 권장하지 않습니다.
  • related_name: 부모 모델에서 자식 모델을 역으로 참조할 때 사용할 이름을 지정하여, 데이터 접근성을 높여줍니다.

4. ForeignKey 사용 시 주의사항: 데이터베이스 성능과 설계 🚩

  • 참조 무결성: ForeignKey는 데이터 무결성을 유지하는 강력한 도구이지만, on_delete 옵션을 잘못 설정하면 데이터 불일치를 유발할 수 있으므로 신중하게 선택해야 합니다.
  • 데이터베이스 성능: ForeignKey를 과도하게 사용하면 데이터베이스 쿼리 성능이 저하될 수 있습니다. 특히 JOIN 연산을 많이 사용하는 경우에는 데이터베이스 인덱스 설정 및 쿼리 최적화가 필수입니다.
  • 테이블 설계: 데이터 모델링 시 테이블 간의 관계를 명확하게 정의하고, ForeignKey를 적절하게 활용해야 합니다. 테이블 구조를 설계할 때는 데이터의 성격과 관계를 정확하게 파악하고, 각 필드의 역할과 제약 조건을 고려해야 합니다.
  • 단일 테이블에서의 사용: ForeignKey는 테이블 간의 관계를 표현하는 것이 목적이지만, 특정 상황 (자체 참조 관계 등)에서는 단일 테이블에서 사용될 수 있습니다. 하지만 이 경우에는 데이터 구조가 복잡해질 수 있으므로 신중하게 설계해야 합니다.

5. 결론: 데이터베이스 설계의 핵심, ForeignKey 🔑

ForeignKey는 관계형 데이터베이스에서 데이터를 효율적으로 관리하고, 데이터의 무결성을 유지하는 데 매우 중요한 도구입니다. 다양한 데이터베이스 관계를 표현할 수 있으며, 데이터베이스 설계의 핵심적인 요소입니다.