ViewComponents는 적절한 관심사 분리, 미리보기, 테스트 용이성 등 다양한 이점을 제공하며, UI 컴포넌트 개발 패러다임을 변화시킵니다. 특히 컴포넌트 객체와 렌더링 뷰 코드를 한 곳에 두는 co-location은 UI 컴포넌트가 단일 책임에 집중하도록 돕습니다. 본문에서는 사용자 목록 페이지 예시를 통해 ViewComponents의 적용 과정을 상세히 설명합니다. 초기에는 컨트롤러, 프레젠터, 뷰에 걸쳐 분산되어 있던 필터 설정, 사용자 데이터 래핑, 페이지네이션 구성, 테이블 렌더링, 드롭다운 액션 처리 등의 책임들이 ViewComponents를 통해 명확히 분리됩니다.
ViewComponents 적용 단계
- 필터 컴포넌트 (
Filters Component):UsersFilterComponent(애플리케이션 특정): 사용자 필터링 로직을 정의합니다.FilterComponent(범용): 재사용 가능한 필터 렌더링 로직을 처리합니다.- 이를 통해 컨트롤러에서 필터 관련 로직이 제거되고,
index.html.erb는UsersFilterComponent.new만 렌더링하게 됩니다.
- 사용자 컴포넌트 (
UserComponent):- 테이블 본문 내의 개별 사용자 렌더링 로직을 추상화합니다.
- 기존
UserPresenter의current_roles및dropdown_actions메서드를UserComponent내부로 이동시켜 프레젠터를 제거하고 컨트롤러를 간소화합니다. index.html.erb는@users.each루프 내에서UserComponent.new(user:)를 렌더링합니다.
- 사용자 테이블 컴포넌트 (
UsersTableComponent와 Slots):- 테이블 전체 구조를 추상화하며,
renders_many :users, UserComponent를 사용하여 사용자 컬렉션을UserComponent로 렌더링합니다. headers메서드를 통해 테이블 헤더를 정의합니다.index.html.erb는UsersTableComponent.new(pagy: @pagy)를 블록 형태로 렌더링하고,with_users슬롯을 통해 각 사용자를 전달합니다.
- 테이블 전체 구조를 추상화하며,
- 테이블 컴포넌트 (
TableComponent와 Slots):- 범용적인 테이블 구조(
<thead>,<tbody>,<tfoot>)를 위한 컴포넌트입니다. renders_one :header,renders_one :body,renders_one :footer슬롯을 활용하여 테이블의 각 부분을 분리합니다.UsersTableComponent는 이TableComponent를 사용하여 일관된 스타일을 유지합니다.
- 범용적인 테이블 구조(
- CSS 클래스 커스터마이징:
TableComponent에classes인자를 추가하여 최상위 요소에 커스텀 CSS 클래스를 적용할 수 있도록 합니다.class_names헬퍼를 활용하여 조건부 CSS 클래스 토글 기능을 구현합니다.
- 페이지네이션 컴포넌트 (
PaginationComponent와 조건부 렌더링):- 페이지네이션 관련 로직을 별도의
PaginationComponent로 추상화합니다. UsersTableComponent는renders_one :pagination, PaginationComponent를 사용합니다.#render?메서드를 구현하여 특정 조건에서만 컴포넌트가 렌더링되도록 합니다.
- 페이지네이션 관련 로직을 별도의
미리보기 및 테스트
-
미리보기 (Previewing): Rails Mailer와 유사하게 컴포넌트를 애플리케이션과 격리된 환경에서 테스트할 수 있는 미리보기 기능을 제공합니다. Lookbook과 같은 도구를 활용하면 더욱 강력한 기능을 사용할 수 있습니다.
-
테스트 (Testing):
- 단위 테스트 (Unit Test): 컴포넌트 내부에 정의된 메서드들의 로직을 검증합니다.
- 시스템 테스트 (System Test): Capybara를 활용하여 컴포넌트의 HTML 출력이 예상대로 렌더링되는지 엔드투엔드 흐름을 테스트합니다.