Rails 애플리케이션의 성능 최적화는 다음 다섯 가지 주요 병목 현상을 체계적으로 해결하는 데 중점을 둡니다.
1. N+1 쿼리 (The Silent Killer)
-
원인: 연관된 데이터를 로드할 때 N개의 추가 쿼리가 발생하여 응답 시간을 10배 이상 지연시킬 수 있습니다. 개발 단계에서는 작은 데이터셋으로 인해 발견하기 어렵습니다.
- 감지:
- 개발 환경:
BulletGem - 운영 환경:
NewRelic,Scout와 같은 APM 도구 - Rails 쿼리 로그:
config.active_record.verbose_query_logs = true
- 개발 환경:
- 해결:
includes: 대부분의 경우 (기본 선택)preload: 다형성 연관 관계, 강제로 별도 쿼리 실행 시eager_load: 연관 테이블에WHERE조건 추가 시joins: 연관 테이블을 로드하지 않고 필터링만 할 경우counter_cache: 자주 접근하는 카운트 값
-
Pro Tip: 테스트 스위트에서
Bullet.raise = true를 설정하여 운영 환경 배포 전 N+1 쿼리 방지. - Gotcha: 백그라운드 작업의 N+1 쿼리는 사용자에게 직접적인 영향이 없어 간과하기 쉽지만, 전체 시스템 리소스에 큰 부담을 줍니다.
2. 데이터베이스 인덱스 누락
-
원인: 중요한 컬럼에 인덱스가 없어 대규모 테이블에서 쿼리 속도가 수 초까지 지연되는 ‘테이블 스캔’이 발생합니다.
- 감지:
- 느린 쿼리 로그 (100ms 이상)
- 높은 데이터베이스 CPU 사용률
EXPLAIN ANALYZE결과에서 ‘Sequential Scan’ 확인
- 해결:
- 인덱스 추가 조건: 외래 키 (항상),
WHERE,ORDER BY,JOIN조건에 자주 사용되는 컬럼. - 인덱스 제외 조건: 1,000행 미만 테이블, 자주 업데이트되는 컬럼, 카디널리티가 매우 낮은 컬럼 (예외: 부분 인덱스).
- 인덱스 추가 조건: 외래 키 (항상),
-
Pro Tip: PostgreSQL의
EXPLAIN (ANALYZE, BUFFERS)를 사용하여 실제 실행 시간과 스캔된 행 수를 확인. - Gotcha: 인덱스는 읽기 성능을 향상시키지만, 쓰기(INSERT, UPDATE) 성능을 저하시키므로 균형 있는 설계가 중요합니다.
3. 비효율적인 뷰 렌더링
-
원인: 데이터베이스 쿼리가 빠르더라도, 뷰 자체의 복잡한 로직, 과도한 부분 템플릿 사용, 불필요한 JSON 직렬화 등으로 인해 페이지 렌더링이 느려질 수 있습니다.
- 감지:
- APM 도구에서 ‘View’ 시간이 높게 측정
- 깊게 중첩된 부분 템플릿 구조
- 해결:
- 프래그먼트 캐싱: 렌더링된 HTML 조각을 캐시하여 재사용.
- 러시아 인형 캐싱 (Russian Doll Caching): 중첩된 콘텐츠에 대한 캐싱 전략으로,
touch: true옵션을 통해 자식 레코드 변경 시 부모 캐시를 자동으로 무효화하여 캐시 일관성 유지.
4. 백그라운드 작업의 메모리 비대화
-
원인: 대규모 데이터셋을 한 번에 로드하거나 (
User.all.each), 불필요한 객체 참조를 유지하거나, 데이터베이스의 배치 작업 기능을 활용하지 않을 때 작업자(worker)의 메모리가 과도하게 사용되어 OOM-kill 또는 처리 지연을 유발합니다. - 감지:
- Sidekiq/작업자 프로세스의 과도한 RAM 사용량
- 작업자 프로세스 OOM-kill 발생
- 시간이 지남에 따라 작업 처리 속도 저하
- 해결:
find_each또는find_in_batches를 사용하여 대규모 데이터셋을 배치 단위로 처리하여 메모리 사용량을 일정하게 유지.
-
Pro Tip:
find_each사용 시 적절한 배치 크기를 설정하여 메모리 효율성을 극대화. - Gotcha: 배치 처리 중 결과를 메모리에 누적하지 않도록 주의하고, 필요한 경우 파일이나 데이터베이스에 점진적으로 저장하는 방식을 고려해야 합니다.
5. 느린 에셋 컴파일
-
원인: 운영 환경에서는 사전 컴파일되지만, 개발 생산성 및 CI/CD 파이프라인 속도에 영향을 미칩니다.
- 감지:
- 긴 배포 시간
- 느린 CI 빌드
- 개발 환경 시작 시간 지연
- 해결:
- 최신 JavaScript 번들러 사용:
esbuild,Vite와 같은 도구는 JavaScript/TypeScript 컴파일 속도를 크게 향상시킵니다. - 이미지 최적화: 이미지 크기 및 포맷 최적화.
- CDN 활용: 정적 에셋을 사용자에게 더 가까운 서버에서 제공하여 로드 시간 단축.
- 최신 JavaScript 번들러 사용:
- Pro Tip:
esbuild는 주로 JS/TS 번들링 속도를 개선하며, CSS 컴파일 개선 효과는 상대적으로 적습니다.