ActiveRecord는 퍼시스턴스, 쿼리, 도메인 동작을 혼합하여 비대한 모델, 느린 쿼리, N+1 버그 등의 문제를 야기할 수 있습니다. 이를 해결하고 의도적으로 사용하기 위한 실용적인 패턴들은 다음과 같습니다.
1. Scopes vs Class Methods
-
Scope: 필터링 목적의 조합 가능한 쿼리.
-
Class Method: 조건부 로직, 인수에 따른 동작 변경 등 비관계형 로직.
-
원칙: 스코프는 필터링, 클래스 메서드는 결정. 복잡한 로직은 스코프에서 피합니다.
2. Query Objects
- 복잡한 쿼리는 전용 쿼리 객체로 추출하여 모델 슬림화, 가독성, 재사용성, 테스트 용이성을 높입니다.
3. Transactions
- 여러 쓰기 작업이 함께 성공해야 할 때, 명시적으로
Model.transaction을 사용하며, 콜백에 트랜잭션 안전성을 의존하지 않습니다.
4. Callbacks
-
안전:
before_validation,after_commit(사이드 이펙트), 정규화. -
회피: 비즈니스 워크플로우, 외부 API 호출 등 복잡한 로직. 중요한 로직은 서비스에서 명확히 합니다.
5. Database Constraints vs Rails Validations
- Rails Validation은 UX를, DB Constraints는 데이터 무결성을 보호합니다. 둘 다 사용하며, 애플리케이션만으로는 데이터 무결성을 신뢰하지 않습니다.
6. N+1 문제 방지
includes,preload,select패턴 활용 및Bulletgem으로 쿼리 설계 시 문제를 해결합니다.
7. 모델 작게 유지
- 모델을 200줄 미만으로 유지하고, 책임 집중 및 워크플로우를 포함하지 않도록 합니다. 복잡한 동작은 서비스 객체로 추출합니다.
ActiveRecord 의사결정 체크리스트
-
데이터 유효성? → 모델
-
재사용 가능한 필터? → 스코프
-
결정 기반 쿼리? → 클래스 메서드
-
복잡한 쿼리? → 쿼리 객체
-
다단계 데이터 변경? → 서비스 + 트랜잭션
-
비동기 작업? → 잡 호출 서비스