데이터베이스를 죽이지 마세요: Rails 앱을 더 빠르게 만드는 5가지 ActiveRecord 팁

발행: (2026년 2월 22일 오전 09:19 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

이 글에서는 ActiveRecord와 함께한 나의 여정을 공유합니다. 처음 Rails를 시작했을 때는 ActiveRecord가 데이터베이스에 데이터를 저장해 주는 “마법”이라고만 생각했었습니다. 하지만 규모가 큰 프로젝트를 진행하면서 훨씬 더 많은 것이 있다는 것을 깨달았습니다. 동작하는 코드를 쉽게 작성할 수 있지만, 앱을 느리게 만드는 코드를 쉽게 작성할 수도 있습니다. 아래는 간단한 쿼리부터 고급 최적화까지 다섯 단계의 ActiveRecord 지식 수준을 정리한 것입니다.

LEVEL 1: 기본 (체이닝)

대부분의 초보자는 findwhere를 알고 있습니다. ActiveRecord의 멋진 점은 데이터를 실제로 요청할 때까지 이 메서드들을 무한히 체이닝할 수 있다는 것입니다.

# Instead of doing this
users = User.where(active: true)
recent_users = users.where('created_at > ?', 1.day.ago)

# You can chain it nicely
User.where(active: true).where('created_at > ?', 1.day.ago)

이 코드는 하나의 SQL 쿼리만 생성합니다. Rails는 데이터베이스에 접근하는 시점을 가능한 마지막 순간까지 미룹니다.

LEVEL 2: 스코프로 정리하기

여러 컨트롤러에서 동일한 where 조건을 반복하는 것은 번거롭습니다. 모델에 스코프를 정의하면 클래스 메서드처럼 사용할 수 있습니다.

# app/models/post.rb
class Post  { where(published: true, deleted: false) }
  scope :recent, -> { where('created_at > ?', 1.week.ago) }
end

이제 컨트롤러 코드는 훨씬 깔끔해집니다:

# app/controllers/posts_controller.rb
def index
  @posts = Post.live.recent
end

LEVEL 3: N+1 문제 해결 (고급)

가장 흔한 실수는 부모 레코드를 로드한 뒤 자식 레코드를 반복하면서 각 연관 관계마다 별도의 쿼리를 발생시키는 것입니다.

# controller
@posts = Post.all

# view (ERB)

Rails는 포스트에 대해 1개의 쿼리를 실행하고, 사용자에 대해 N개의 추가 쿼리를 실행합니다(포스트당 1개). 100개의 포스트라면 총 101개의 쿼리가 실행되어 성능이 크게 저하됩니다.

STEP 1: includes 사용

연관 레코드를 한 번의 쿼리로 가져오도록 ActiveRecord에 알려줍니다.

@posts = Post.includes(:user).all

Rails는 이제 2개의 쿼리만 실행합니다:

  1. 모든 포스트를 가져옵니다.
  2. 해당 포스트와 연관된 모든 사용자를 가져옵니다.

LEVEL 4: Pluck vs Map (최적화)

단일 컬럼만 필요할 때는 전체 ActiveRecord 객체를 로드하지 않는 것이 좋습니다.

# Heavy: loads all User objects into memory
User.all.map(&:email)

대신 pluck을 사용하면 데이터베이스에서 바로 컬럼을 가져올 수 있습니다:

# Fast: fetches only the email column
User.pluck(:email)

pluck은 모델 객체를 인스턴스화하지 않고 문자열 배열을 반환하므로 메모리와 시간을 절약합니다.

LEVEL 5: present? 대신 exists? 사용

레코드가 존재하는지만 확인하고 싶다면 레코드를 로드하지 마세요.

# Bad: loads the post into memory just to check existence
if Post.where(id: params[:id]).present?
  # …
end

# Good: runs a lightweight SQL `SELECT 1` with LIMIT 1
if Post.exists?(params[:id])
  # …
end

exists?는 불리언 값을 반환하고, 매치가 발견되는 즉시 쿼리를 중단합니다.


이 몇 가지 트릭을 마스터하면 Rails 애플리케이션이 더 빠르게 동작하고 코드도 더 깔끔해집니다.

0 조회
Back to Blog

관련 글

더 보기 »

성능 비교: React vs WebForms Core

네트워크 요청, 대역폭 소비 및 클라이언트 실행 모델에 집중하세요. 현대 웹 아키텍처에서 성능은 렌더링 속도만을 의미하지 않습니다. A criti...