Django는 ORM을 사용하여 SQL 쿼리를 자동 생성하지만, 잘못된 사용은 성능 저하를 유발할 수 있음.
Django 공식 문서에서 추천하는 쿼리 최적화 방법을 정리.
select_related()
사용 (JOIN 최적화)# N+1 문제 발생 (비효율적)
games = Game.objects.all()
for game in games:
print(game.genre.name) # 매번 genre를 조회하는 추가 쿼리 발생
# 최적화된 코드
games = Game.objects.select_related("genre") # JOIN 수행하여 한 번에 조회
prefetch_related()
사용 (ManyToMany, ForeignKey 역참조 최적화)# N+1 문제 발생 (비효율적)
games = Game.objects.all()
for game in games:
for tag in game.tags.all():
print(tag.name) # 여러 개의 쿼리 발생
# 최적화된 코드
games = Game.objects.prefetch_related("tags") # 미리 가져와서 쿼리 수 감소
only()
와 defer()
로 불필요한 필드 로딩 방지# 모든 필드 조회 (비효율적)
game = Game.objects.get(id=1)
# 특정 필드만 조회 (최적화)
game = Game.objects.only("id", "name") # 나머지 필드는 필요할 때까지 조회되지 않음
game = Game.objects.defer("large_text_field") # 특정 필드 제외하여 조회
exists()
를 사용하여 불필요한 데이터 로딩 방지count()
대신 exists()
사용# 비효율적인 방식
if Game.objects.filter(name="Cyberpunk 2077").count() > 0:
print("게임이 존재합니다.")
# 최적화된 코드
if Game.objects.filter(name="Cyberpunk 2077").exists():
print("게임이 존재합니다.") # 더 빠르고 메모리 절약 가능
bulk_create()
와 update()
사용하여 다수의 데이터 처리 최적화# 여러 개의 객체를 한 번에 저장
games = [
Game(name="The Witcher 3"),
Game(name="Elden Ring"),
]
Game.objects.bulk_create(games) # 여러 INSERT 쿼리를 하나로 합침
annotate()
를 활용한 집계 연산 최적화from django.db.models import Count
# 각 게임이 가진 리뷰 개수 가져오기 (JOIN + GROUP BY)
games = Game.objects.annotate(review_count=Count("reviews"))
for game in games:
print(f"{game.name} - {game.review_count}개 리뷰")
iterator()
를 사용하여 대량 데이터 효율적으로 처리iterator()
사용for game in Game.objects.iterator():
print(game.name) # 메모리 최적화됨
select_related()
, prefetch_related()
→ N+1 문제 해결only()
, defer()
→ 불필요한 필드 로딩 방지exists()
사용 → count()
대신 존재 여부만 체크할 때 사용bulk_create()
→ 다량의 데이터 저장 시 성능 최적화annotate()
사용 → ORM에서 직접 집계 연산 수행iterator()
활용 → 대량 데이터 메모리 절약🚀 Django ORM을 잘 활용하면 성능 최적화가 가능하며, 불필요한 SQL 쿼리 실행을 줄일 수 있음!