본문 바로가기
Django 장고

원투매니에서 발생하는 플러스원 이슈

by jennyiscoding 2024. 10. 24.

gpt 대답: 

 

원투매니(One-to-Many) 관계에서 발생하는 "플러스원" 이슈는 데이터베이스 쿼리에서 N+1 문제(N+1 Query Problem)로 불리며, ORM(Object-Relational Mapping)을 사용할 때 자주 발생하는 성능 관련 문제입니다.

N+1 문제란?

N+1 문제는 데이터베이스에서 "1개의 주요 쿼리"와 관련된 "N개의 추가 쿼리"가 불필요하게 발생하는 상황을 말합니다. 주로 1대 다 관계에서 발생하며, 데이터를 불러올 때 각 항목에 대해 추가적인 쿼리를 실행하는 방식 때문에 성능 저하가 발생합니다.

N+1 문제 예시

Django ORM에서 Author와 Book이 One-to-Many 관계라고 가정해보겠습니다. 하나의 Author는 여러 권의 Book을 가질 수 있습니다.

python
코드 복사
# 예시: 모든 저자와 그 저자의 책을 가져오는 코드 authors = Author.objects.all() for author in authors: books = author.book_set.all() # 저자의 책을 가져옴 for book in books: print(book.title)

이 코드에서는 저자 목록을 한 번의 쿼리로 가져옵니다. 그러나 각 저자마다 해당 저자의 책을 가져오기 위해 추가적인 쿼리가 실행됩니다. 따라서 N명의 저자에 대해 N개의 추가 쿼리가 발생하게 됩니다.

쿼리 실행 과정:

  1. 1번 쿼리: SELECT * FROM author; → 모든 저자 데이터를 가져옴.
  2. N번 추가 쿼리: 각 저자에 대해 SELECT * FROM book WHERE author_id = ?; → 각 저자의 책을 N번 가져옴.

즉, 저자를 가져오는 데 1번의 쿼리, 그리고 각 저자마다 그 저자의 책을 가져오기 위한 N번의 추가 쿼리가 실행되기 때문에 N+1 쿼리가 발생합니다.

N+1 문제 해결 방법

1. select_related() 사용 (One-to-One, ForeignKey)

select_related()는 One-to-One 또는 ForeignKey 관계에서 N+1 문제를 해결하기 위해 사용하는 방법입니다. 관련된 객체를 미리 함께 가져오므로 추가적인 쿼리를 방지합니다.

python
코드 복사
# 저자와 해당 책들을 한 번에 가져옴 authors = Author.objects.select_related('book_set').all() for author in authors: books = author.book_set.all() for book in books: print(book.title)

2. prefetch_related() 사용 (Many-to-Many, One-to-Many)

prefetch_related()는 One-to-Many 또는 Many-to-Many 관계에서 N+1 문제를 해결할 때 사용됩니다. 이를 통해 관련된 데이터를 미리 가져와 N번의 추가 쿼리 문제를 해결할 수 있습니다.

python
코드 복사
# 저자와 책을 한 번에 가져오는 최적화된 방식 authors = Author.objects.prefetch_related('book_set').all() for author in authors: books = author.book_set.all() for book in books: print(book.title)

이 경우 Django는 저자에 대한 쿼리와, 책에 대한 별도의 쿼리를 미리 한 번에 실행하고 메모리에서 매칭하여 사용합니다.

N+1 문제의 영향

N+1 문제는 소규모 데이터베이스에서는 크게 눈에 띄지 않을 수 있지만, 데이터 양이 많아지면 쿼리 수가 기하급수적으로 늘어나므로 성능 저하가 심각하게 발생할 수 있습니다. 이를 방지하기 위해 select_related()나 prefetch_related() 같은 ORM 최적화 기능을 사용하여 데이터 조회를 최적화하는 것이 중요합니다.