데이터베이스 설계를 하기 위해서

2024. 11. 14. 15:01django/Model

1번
사용자아이디, 작업일자, 핸드폰번호
사용자아이디, 작업일자, 핸드폰번호
사용자아이디, 작업일자, 핸드폰번호
.....
이렇게 하는 방법과
2번
사용자아이디, 작업일자, [핸드폰번호, 핸드폰번호...]

아니면 어떤방법이 좋을까요

데이터베이스 설계에 대해 세 가지 접근 방식을 제안하고, 각각의 장단점을 설명하겠습니다:

  1. 일반 테이블 방식 (1번 방식)

    class PhoneNumberLog(models.Model):
     user_id = models.CharField(max_length=50)
     work_date = models.DateField()
     phone_number = models.CharField(max_length=20)
     created_at = models.DateTimeField(auto_now_add=True)
    
     class Meta:
         indexes = [
             models.Index(fields=['user_id', 'work_date']),
         ]

    장점:

  • 단순하고 직관적인 구조
  • 쉬운 쿼리 작성
  • 개별 레코드 관리 용이
    단점:
  • 데이터 중복 가능성
  • 저장 공간 더 필요
  • 대량 데이터 시 성능 영향
  1. JSON 필드 사용 (2번 방식)

    class PhoneNumberLogJson(models.Model):
     user_id = models.CharField(max_length=50)
     work_date = models.DateField()
     phone_numbers = models.JSONField()  # 배열 형태로 저장
     created_at = models.DateTimeField(auto_now_add=True)
    
     class Meta:
         indexes = [
             models.Index(fields=['user_id', 'work_date']),
         ]

    장점:

  • 저장 공간 효율적
  • 유연한 데이터 구조
  • 한 번의 쿼리로 모든 번호 조회
    단점:
  • 특정 번호 검색이 어려움
  • 데이터 무결성 검증 어려움
  • 인덱싱 제한적
  1. 관계형 테이블 사용 (추천 방식)

    class PhoneNumberWork(models.Model):
     user_id = models.CharField(max_length=50)
     work_date = models.DateField()
     created_at = models.DateTimeField(auto_now_add=True)
    
     class Meta:
         unique_together = ['user_id', 'work_date']
    

class PhoneNumber(models.Model):
work = models.ForeignKey(PhoneNumberWork, on_delete=models.CASCADE, related_name='phone_numbers')
phone_number = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
    indexes = [
        models.Index(fields=['phone_number']),
    ]
장점:
- 데이터 무결성 보장
- 효율적인 검색과 필터링
- 확장성이 좋음
단점:
- 구조가 더 복잡
- 조인 필요
- 초기 설정 시간 필요

사용 예시:
```python
# 데이터 저장
work = PhoneNumberWork.objects.create(
    user_id='user1',
    work_date='2024-03-14'
)
PhoneNumber.objects.bulk_create([
    PhoneNumber(work=work, phone_number='010-1234-5678'),
    PhoneNumber(work=work, phone_number='010-2345-6789')
])

# 조회
work = PhoneNumberWork.objects.prefetch_related('phone_numbers').get(
    user_id='user1',
    work_date='2024-03-14'
)
phone_numbers = [pn.phone_number for pn in work.phone_numbers.all()]

추천:
데이터의 특성과 사용 패턴에 따라 다르지만, 일반적으로는 세 번째 방식(관계형 테이블)을 추천합니다.

그 이유는:

  1. 데이터 무결성 보장
  2. 효율적인 검색과 필터링
  3. 향후 확장성 (예: 번호별 추가 정보 저장)
  4. 표준 데이터베이스 기능 활용
  5. 백업과 복구 용이

단, 다음과 같은 경우에는 다른 방식을 고려할 수 있습니다:

  • 매우 단순한 로깅 목적: 1번 방식
  • 유연한 구조가 필요하고 검색이 적은 경우: 2번 방식
  • 데이터 양이 매우 적은 경우: 1번 방식

실제 구현 시에는 예상 데이터 양과 주요 사용 패턴을 고려하여 선택하시면 좋겠습니다.