기술블로그-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으로 연결된 것입니다 |