Gem 없이 바닐라 Rails UI 컴포넌트 시스템 구축하기

Components in Rails Without Gems

작성자
HackerNews
발행일
2025년 09월 25일

핵심 요약

  • 1 외부 Gem 없이 Rails의 표준 렌더링 기능을 활용하여 재사용 가능한 UI 컴포넌트를 구축하는 `ComponentHelper`를 소개합니다.
  • 2 명시적 로컬 변수, 콘텐츠 블록, 데코레이터, 클래스 기반 컴포넌트 등 다양한 기법을 통해 컴포넌트의 유지보수성과 코드 품질을 향상시킵니다.
  • 3 뷰 로직 과부하 및 헬퍼의 전역 스코프 문제를 해결하며, 바닐라 Rails만으로도 체계적인 컴포넌트 시스템을 구현하는 실용적인 방법을 제시합니다.

도입

이 글은 ViewComponent와 같은 외부 라이브러리 없이 Rails에서 재사용 가능한 UI 컴포넌트를 구축하는 실용적인 방법을 다룹니다. 부분 템플릿(partials)만으로는 유지보수성과 코드 품질에 한계가 있고, 헬퍼(helpers)의 전역 스코프 문제로 인해 뷰 로직이 복잡해지는 상황을 해결하기 위한 대안을 제시합니다. 바닐라 Rails 기능을 최대한 활용하여 깨끗하고 체계적인 컴포넌트 시스템을 만드는 다양한 기법들을 소개합니다.

이 글의 핵심은 Rails의 표준 렌더링을 추상화하는 ComponentHelper입니다. 이 헬퍼는 컴포넌트 이름, 로컬 변수, 선택적 블록을 받아, 블록 유무나 컬렉션 여부에 따라 렌더링 전략(레이아웃, 컬렉션, 부분 템플릿)을 결정합니다. 모든 컴포넌트는 app/views/components/ 디렉토리에 위치합니다.

컴포넌트 구현 기법

  • 명시적 로컬 변수 (Explicit Locals): Rails 7.2 기능으로, <%# locals: (user:, css: user.avatar_css) %>처럼 필요한 변수를 명확히 정의하여 인터페이스를 분명히 합니다. component "avatar", user: User.first.decorate와 같이 사용하며 컬렉션 렌더링도 지원합니다.

  • 콘텐츠 블록 (Content Blocks): yield를 사용하여 컴포넌트 내부에 콘텐츠를 삽입합니다. section이나 breadcrumbs처럼 특정 레이아웃으로 콘텐츠를 감쌀 때 유용하며, component("breadcrumbs", items: [...]) { "Here" }와 같이 블록을 전달합니다.

  • 데코레이터 (Decorators): 뷰 로직을 모델에서 분리하기 위해 User::Decorator와 같이 SimpleDelegator를 상속받아 avatar_css 같은 뷰 전용 메서드를 정의합니다. 이를 통해 모델은 비즈니스 로직에, 데코레이터는 뷰 로직에 집중합니다.

  • 클래스 기반 컴포넌트 (Class-based Components): 복잡한 컴포넌트는 BadgeComponent와 같은 전용 Ruby 클래스를 생성하여 로직을 캡슐화합니다. 이 클래스는 컴포넌트의 상태와 행위를 관리하며, 뷰 템플릿은 <%= tag.span badge.name, class: badge.css %>처럼 클래스 인스턴스의 메서드를 호출하여 HTML을 렌더링합니다. 이는 ViewComponent와 유사하며 테스트 용이성과 유지보수성을 향상시킵니다.

이러한 접근 방식들을 통해 외부 Gem 없이도 Rails 애플리케이션에서 체계적이고 확장 가능한 UI 컴포넌트 시스템을 구축할 수 있습니다.

결론

이 글은 `ComponentHelper`를 중심으로 명시적 로컬 변수, 콘텐츠 블록, 데코레이터, 클래스 기반 컴포넌트 등 다양한 바닐라 Rails 기법을 활용하여 재사용 가능한 UI 컴포넌트를 구축하는 방법을 성공적으로 보여주었습니다. 이러한 접근 방식은 부분 템플릿의 한계와 헬퍼의 전역 스코프 문제를 해결하고, 뷰 로직을 깔끔하게 분리하여 코드의 유지보수성과 테스트 용이성을 크게 향상시킵니다. 외부 Gem에 의존하지 않고도 견고하고 확장 가능한 컴포넌트 시스템을 필요로 하는 프로젝트에 매우 유용한 대안을 제공합니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!