N+1 쿼리 문제는 데이터베이스에서 초기 레코드 집합을 위한 1개의 쿼리 후, 각 개별 레코드에 대해 N개의 추가 쿼리가 실행될 때 발생합니다. 예를 들어, Post.all.each do |post| puts post.comments.count end
는 모든 게시물을 가져온 뒤 각 게시물의 댓글 수를 별도로 조회하여 N+1 쿼리를 유발합니다. 이는 쿼리마다 지연 시간을 추가하여 성능을 저하시키고, 데이터셋 규모가 커질수록 확장성을 저해하며, 명확한 오류 없이 조용히 성능을 악화시키는 특징이 있습니다.
이 문제를 감지하기 위해 Bullet Gem을 활용할 수 있습니다. Gemfile에 gem 'bullet'
를 추가하고 bundle install
후, config/environments/development.rb
에 Bullet.enable = true
, Bullet.alert = true
, Bullet.bullet_logger = true
, Bullet.rails_logger = true
, Bullet.add_footer = true
와 같은 설정을 추가합니다. 이 설정들은 Bullet 활성화, 브라우저 팝업 알림, 로그 기록 등을 통해 N+1 쿼리 감지 시 개발자에게 경고를 제공합니다.
문제 해결은 ‘Eager Loading’ 기법을 사용합니다. Ruby on Rails의 includes
, preload
, eager_load
메서드를 통해 연관된 데이터를 미리 로드하여 N+1 쿼리를 방지합니다. Post.includes(:comments).each do |post| puts post.comments.count end
와 같이 수정하면, 게시물 수와 관계없이 단 두 번의 쿼리만으로 모든 데이터를 효율적으로 가져와 성능을 크게 향상시킬 수 있습니다. 또한, config/environments/test.rb
에 Bullet.enable = true
, Bullet.raise = true
설정을 추가하여 테스트 환경에서도 N+1 쿼리를 감지하고 테스트 실패를 유도, CI/CD 파이프라인에서 성능 회귀를 조기에 예방할 수 있습니다.