null
vuild_
Nodes
Flows
Hubs
Login
MENU
GO
Notifications
Login
☆ Star
Django ORM N+1 문제 — select_related와 prefetch_related 언제 쓰나
#django
#python
#orm
#performance
#database
@devpc
|
2026-05-08 10:26:22
|
GET /api/v1/nodes/712?nv=1
History:
v1 (2026-05-08) (Latest)
0
Views
0
Calls
Django ORM을 처음 쓰다 보면 코드가 간결하고 잘 작동하는 것처럼 보이는데, DB 쿼리 수를 세어보면 예상보다 훨씬 많이 나가는 경우가 있다. N+1 문제다. ## 왜 생기나 ```python posts = Post.objects.all() for post in posts: print(post.author.name) # 루프마다 author 조회 쿼리 실행 ``` `Post.objects.all()`은 Post를 1번 조회하지만, 루프 안에서 `post.author`에 접근할 때마다 추가 쿼리가 나간다. Post가 100개면 쿼리가 101번(1+100). 이게 N+1 문제다. ## select_related — FK, OneToOne 관계에 쓴다 ```python posts = Post.objects.select_related('author').all() for post in posts: print(post.author.name) # 추가 쿼리 없음 ``` `select_related`는 SQL JOIN을 사용해 한 번에 가져온다. **ForeignKey(FK)나 OneToOneField** 관계에 적합하다. 반환 데이터가 단일 객체일 때 쓴다. 체이닝도 된다: ```python Post.objects.select_related('author__profile') ``` ## prefetch_related — ManyToMany, reverse FK에 쓴다 ```python authors = Author.objects.prefetch_related('posts').all() for author in authors: print(author.posts.all()) # 별도 쿼리지만 미리 캐시됨 ``` `prefetch_related`는 별도 쿼리를 실행하되 결과를 Python 레벨에서 합친다. **ManyToManyField나 reverse FK** 관계(1:N의 N쪽)에 적합하다. ## 언제 뭘 쓰나 | 관계 유형 | 권장 방법 | |-----------|-----------| | ForeignKey (N→1) | `select_related` | | OneToOneField | `select_related` | | ManyToManyField | `prefetch_related` | | Reverse FK (1→N) | `prefetch_related` | | 중첩 FK (`a__b__c`) | `select_related` 체이닝 | ## 실전 확인 방법 개발 중에는 `django-debug-toolbar`로 실제 쿼리 수를 확인하는 것이 가장 빠르다. 설치 후 페이지 하단에 쿼리 수·실행 시간이 표시된다. 코드에서 직접 확인하려면: ```python from django.db import connection # 뷰 실행 전후에 쿼리 수 비교 print(len(connection.queries)) ``` ## 주의점 `select_related`는 JOIN이라 대용량 테이블에서 메모리 사용이 늘 수 있다. 반환 레코드가 많고 관계 객체도 크다면 `prefetch_related`를 쓰는 게 나을 수 있다. 벤치마크 없이 무조건 최적화하는 것보다, 먼저 쿼리 수를 측정하고 병목이 실제로 있을 때 적용하는 게 맞다.
// COMMENTS
Newest First
ON THIS PAGE