저자는 약 30-40개의 블록으로 구성된 대시보드에서 뷰 렌더링에 220-260ms가 소요되어 심각한 성능 저하를 겪었습니다. 이는 각 블록이 중첩된 Partial로 이루어져 있었고, 사용자별 맞춤형 레이아웃으로 인해 캐싱 효과를 기대하기 어려웠기 때문입니다. 프로파일링 결과, 백엔드 처리 시간(50-60ms)에 비해 뷰 렌더링 시간이 월등히 길어 뷰 계층의 최적화 필요성이 대두되었습니다.
Rails 8의 뷰 계층 변화
Rails 8은 다음과 같은 뷰 계층 개선 사항을 포함합니다:
-
Fiber-aware 렌더링
-
개선된 템플릿 캐싱
-
비동기 Partial 렌더링
-
Rails Components 및 ViewComponent 지원
-
더 빠른 상수 조회
이러한 변화는 Partial과 Component 중 어떤 방식을 선택할지에 대한 의문을 증폭시켰고, 특히 대규모 대시보드 환경에서 두 방식의 실제 성능 차이를 측정하는 것이 중요해졌습니다.
벤치마크 설정 및 결과
저자는 Rails 8.0.1, Ruby 3.3, ViewComponent 3.x 환경에서 500개 아이템을 포함하는 대시보드를 대상으로 벤치마크를 수행했습니다. 각 아이템은 아바타, 제목, 설명, 두 개의 통계, 배지로 구성되어 동일한 CSS, HTML, 로직을 Partial과 Component 방식으로 렌더링했습니다.
벤치마크 결과는 다음과 같습니다:
- 🧊 Cold Render (템플릿 캐시 비활성):
- Partial: 265 ms
- Component: 185 ms (Component 약 30% 빠름)
- 🔥 Warm Render (템플릿 캐시 활성):
- Partial: 110 ms
- Component: 98 ms (Component 여전히 우세하나 차이 감소)
- 🧵 Async Rendering:
- Partial: ~90 ms
- Component: ~70 ms (비동기 렌더링에서도 Component가 더 큰 이점)
Partial의 한계: 뷰 컨텍스트 반복 재구성, 헬퍼의 전체 뷰 스택 실행, 깊은 Partial 트리로 인한 오버헤드, 컬렉션 렌더링 비용 증가 등의 숨겨진 비용이 발생합니다.
Component의 장점: 캐시된 템플릿, 빠른 상수 해석, 중복 초기화 방지, 격리된 로직, 최소한의 뷰 컨텍스트, 예측 가능한 성능을 제공하며, UI가 복잡해질수록 그 이점이 더욱 명확해집니다.
Partial과 Component의 적절한 사용 시점
-
Partial이 적합한 경우: 마크업이 매우 작고, 로직이 없으며, 재사용되지 않고, 단일 레코드만 렌더링하며, 컴포넌트 클래스 생성이 불필요한 경우 (예: 간단한 메뉴, 정적 라벨).
-
Component가 적합한 경우: UI 블록이 앱 전체에서 재사용되고, 비공개 메서드/헬퍼 필요, 조건부 로직 포함, 파라미터 전달, 컬렉션 렌더링, 비동기 렌더링 활용, 장기 유지보수 및 UI 확장 고려 시.