기술블로그-Django편

📌 Django에서의 팔로우 기능 모델링 완전 정복

Chansman 2025. 5. 16. 11:47

📌 Django에서의 팔로우 기능 모델링 완전 정복

✅ 1. 핵심 개념 정리

용어 설명

팔로잉 내가 누군가를 따라가는 것 (내가 팔로우하는 사람들)
팔로워 나를 따라오는 사람들 (나를 팔로우하는 사람들)
ManyToManyField N:N 관계 (예: 유저끼리 서로 팔로우할 수 있음)
through 모델 중간에 관계를 직접 조절할 수 있는 연결 테이블
symmetrical=False “a가 b를 팔로우”는 “b가 a를 팔로우”를 의미하지 않음 (단방향 관계)

🧱 2. 전체 구조 및 모델 설명

👤 User 모델 분석

class User(AbstractBaseUser):
    email = models.EmailField(verbose_name='email', unique=True)
    is_active = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)
    nickname = models.CharField('nickname', max_length=20, unique=True)

    following = models.ManyToManyField(
        'self',
        symmetrical=False,
        related_name='followers',
        through='UserFollowing',
        through_fields=('from_user', 'to_user')
    )

✨ 필드 설명:

  • 'self': 같은 모델(User) 간 연결
  • symmetrical=False: 단방향 팔로우
  • related_name='followers': 나를 팔로우하는 유저를 조회하는 역참조 필드
  • through='UserFollowing': 중간 모델을 명시적으로 사용
  • through_fields: 어떤 필드가 어떤 방향인지 명시

🧩 UserFollowing 중간 모델

class UserFollowing(TimestampModel):
    to_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_followers')
    from_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_following')

    class Meta:
        unique_together = ('to_user', 'from_user')

✔️ 핵심 개념 정리:

  • to_user: 팔로우 당하는 대상
  • from_user: 팔로우 하는 사람
  • unique_together: 같은 유저에게 중복 팔로우 방지

🧪 3. 예제 흐름 시뮬레이션

admin = User.objects.get(email='admin@admin.com')
student = User.objects.first()
UserFollowing.objects.create(to_user=admin, from_user=student)

➡️ 결과: student가 admin을 팔로우함

🧪 4. ORM 명령어 예시로 보는 실제 활용

목적 ORM 코드 설명

admin이 팔로우한 사람들 admin.following.all() 내가 팔로우한 유저 목록
admin을 팔로우한 사람들 admin.followers.all() 나를 팔로우한 유저 목록
팔로우 추가 admin.following.add(other_user) 내부적으로 UserFollowing 생성됨
팔로우 제거 admin.following.remove(other_user) 내부적으로 UserFollowing 삭제됨

⚠️ through 모델을 명시한 경우에는 .add()와 .remove()가 동작하지 않을 수 있어 직접 중간 테이블에 조작 필요

📊 ERD 구조 시각화

User ---< UserFollowing >--- User
   ↑          ↕             ↓
 from_user   중간 테이블    to_user

➡️ UserFollowing이 from_user → to_user로 단방향 연결하는 구조

⚠️ 주의사항 & 팁

항목 내용

중복 방지 unique_together로 중복 팔로우 방지
자기자신 팔로우 방지 from_user != to_user 조건 추천
related_name 쿼리에서 매우 유용함 (ex. user.followers.all())
symmetrical=False 트위터식 단방향 팔로우 구현 가능

✅ 요약

키워드 설명

ManyToManyField('self', symmetrical=False) 유저 → 유저 단방향 관계
through='UserFollowing' 중간 테이블로 관계 직접 설정
related_name 관계 접근 시 직관적인 이름 사용 가능 (예: 팔로워, 팔로잉)

 

🔍 질문 핵심 요약

Q. User 모델에는 following = models.ManyToManyField(...) 한 줄만 정의되어 있는데, 왜 u.followers.count() 같은 반대 방향도 되는 걸까?

✅ 핵심 포인트: related_name 덕분입니다

following = models.ManyToManyField(
    'self',
    symmetrical=False,
    related_name='followers',
    through='UserFollowing',
    through_fields=('from_user', 'to_user')
)

➡️ 여기서 related_name='followers' 이 부분이 바로 핵심입니다.

💡 어떻게 작동하냐면?

접근 표현식 의미 실제 필드 기준
u.following.all() u가 팔로우한 사람들 from_user = u
u.followers.all() u를 팔로우한 사람들 to_user = u ← related_name='followers' 덕분

🔁 정리하자면:

방향 필드 정의 위치 ORM 접근 방식 설명
내가 팔로우한 사람 직접 정의한 following u.following.all() 내가 누굴 팔로우함
나를 팔로우한 사람 related_name='followers' u.followers.all() 누가 나를 팔로우함

✅ 그래서 다음도 가능합니다:

u.followers.count()   # 나를 팔로우하는 사람 수
u.following.count()   # 내가 팔로우한 사람 수

➡️ 모두 ManyToMany 관계에서 자동 제공되는 쿼리셋 기능 덕분입니다.

🔧 만약 related_name을 지정하지 않았다면? Django는 기본값으로 _set 형식을 제공합니다.

# related_name='followers'를 안썼다면?
u.userfollowing_set.all()

➡️ 읽기 어렵고 비직관적이기 때문에 related_name 설정은 꼭 해주는 게 좋습니다

🧠 요약

질문 답변
왜 followers, following 둘 다 쓸 수 있나요? related_name 덕분입니다
following만 정의했는데? related_name='followers'가 역방향 필드를 자동 생성해줍니다
둘 다 ManyToManyField인가요? 실제로는 following만 정의된 것이고, 반대는 related_name으로 연결된 것입니다