if not Participants.objects.filter(
schedule=target_schedule,
participant=user,
is_host=True,
).exists():
raise PermissionDenied("해당 약속의 호스트만 시간을 수정할 수 있습니다.")
Python은 동적 타입 언어라서 타입 안정성이 덜 중요해 보이지만,
백엔드에서는 타입 힌트 + 정적 분석 도구를 같이 써서
안정성을 올리는 것이 기본
class Participants(models.Model):
schedule = models.ForeignKey(Schedules, ...)
participant = models.ForeignKey(settings.AUTH_USER_MODEL, ...)
• schedule 필드 타입: Schedules
• participant 필드 타입: User (또는 CustomUser)
그래서 다음 코드는 타입이 일치함
Participants.objects.filter(schedule=target_schedule, participant=user)
• schedule=target_schedule → Schedules 인스턴스
• participant=user → User 인스턴스
반대로 이렇게 쓰면?
Participants.objects.filter(participant_id=user)
• participant_id는 int(pk) 를 기대
• user는 User 객체
→ 사람 눈에는 얼핏 맞아 보이지만, 사실 타입이 꼬인 상태.
django-stubs + mypy 같은 걸 쓰면
Participants.objects.filter(participant_id=user)
이런 코드에서
• participant_id: int
• user: User
타입이 안 맞는 걸 mypy가 에러로 잡아줄 수 있음
→ 런타임 전에 버그를 발견할 수 있음
반대로
Participants.objects.filter(participant=user)
• participant: User
이건 타입이 딱 맞으니까 정적 분석에서도 깨끗하게 통과
예를 들어 나중에
• PK 타입을 int → UUID로 바꾸거나
• User 모델을 커스텀 모델로 변경하거나
했을 때,
participant=user # 객체 비교
participant_id=user.id # PK 직접 비교
둘 다 돌아가긴 하지만
객체 비교 패턴은
User가 어떤 타입이든 상관없이
항상 그 객체를 넣는 방식이라
PK 타입 변경에 덜 민감
⸻
여기서 IDE는 VSCode, PyCharm, Cursor 같은 에디터들을 말함
객체를 다루면
IDE가 타입 정보를 더 잘 추적해서 자동완성이 잘 뜸
예를 들어
participant = Participants.objects.get(...)
participant.participant. # 여기서 자동완성
여기서 participant.participant는 User 객체니까
• .email
• .username
• .id
• .is_active
• .last_login
같은 필드들이 자동완성에 뜸
반대로 코드 전체가 무조건 *_id 기반으로만 돌아가면:
participant.participant_id # int
여기서 IDE는 이걸 그냥 int로만 인식하고 자동완성이 끝남
즉, 객체를 중심으로 쓰는 코드일수록
IDE가 타입을 추론하기 쉬워지고 → 자동완성/힌트 품질이 확 올라감
예를 들어,
participant = user # User 인스턴스
로 코드를 많이 쓰면
• user 변수를 따라가서 User 모델 정의로 점프
• User 관련 메서드/필드 사용처 추적이 쉬움
반대로 id만 들고 다니면
user_id = user.id
...
Participants.objects.filter(participant_id=user_id)
이 패턴이 많으면,
IDE 입장에서는 user_id가 단순 int로만 보이고
어디가 진짜 User 연관 로직인지 구분이 힘들어짐
예를 들어 나중에
participant 필드 이름을 member로 바꾸고 싶다고 했을 때
객체 스타일
Participants.objects.filter(participant=user)
이 상태에서 필드명을 participant → member로 바꾸고
IDE에서 rename refactor를 돌리면
• 모델에서 participant → member
• 관련된 ORM 코드들에서도 participant= → member=로 같이 바뀜
반면 전부 participant_id만 쓰고 있으면
Participants.objects.filter(participant_id=user.id)
• 여기는 단순 문자열/예약 네이밍이라 IDE가 FK 필드 이름의 _id 패턴임을 잘 인식 못 할 수도 있음
• 전부 수동으로 grep → replace 해야 해서 실수 위험이 커짐
⸻
if not Participants.objects.filter(
schedule=target_schedule,
participant=user,
is_host=True
).exists():
raise PermissionDenied("해당 약속의 호스트만 시간을 수정할 수 있습니다.")
이 방식이
• 타입 관점:
• schedule → Schedules 객체
• participant → User 객체
→ 모델 정의와 1:1 매칭, 타입 안정
• IDE 관점
• target_schedule. 찍으면 Schedules 필드들 자동완성
• user. 찍으면 User 속성 자동완성
→ 디버깅/리팩토링에 유리
객체 중심으로 생각하는 Django 스타일
객체 비교 방식 (participant=user)을 쓰면
• 타입 안정성: FK가 기대하는 타입과 정확히 맞아 떨어져서
정적 분석 도구와 리팩토링에 강함
• IDE 지원: 자동완성, Go to definition, rename refactor 등 도구의 힘을
최대한 끌어다 쓸 수 있음
→ 결국 버그를 줄이고, 리팩토링을 부담 없이 많이 할 수 있는 코드가 됨