Stimulus와 TomSelect를 활용한 동적 종속 선택 박스 구현

Dependent Select | Drifting Ruby

작성자
발행일
2025년 07월 06일

핵심 요약

  • 1 Stimulus와 TomSelect를 사용하여 웹 애플리케이션에서 동적 종속 선택 박스를 효율적으로 구현합니다.
  • 2 상위 선택 박스의 값 변경 시 AJAX 요청을 통해 하위 선택 박스의 옵션을 실시간으로 업데이트합니다.
  • 3 Rails 컨트롤러는 필터링된 데이터를 JSON 형태로 제공하며, Stimulus 컨트롤러가 이를 받아 UI를 동적으로 조작합니다.

도입

웹 애플리케이션 개발에서 사용자 경험을 향상시키기 위한 동적 UI 요소는 필수적입니다. 특히, 한 선택 박스의 선택 결과에 따라 다른 선택 박스의 옵션이 동적으로 변경되어야 하는 '종속 선택 박스(Dependent Select Box)' 기능은 복잡한 데이터 입력 폼에서 자주 요구됩니다. 본 문서는 Ruby on Rails 환경에서 Stimulus JavaScript 프레임워크와 TomSelect 라이브러리를 활용하여 이러한 동적 종속 선택 박스를 어떻게 구현하는지 상세히 설명합니다.

프론트엔드 설정

동적 종속 선택 박스 구현을 위해 TomSelect 라이브러리를 통합하는 것이 첫 단계입니다. Rails 7+ 환경에서는 importmap을 통해 JavaScript 라이브러리를 관리하는 것이 권장됩니다.

  • TomSelect 설치 및 설정: bin/importmap pin tom-select 명령어를 사용하여 TomSelect를 importmap에 추가하고, config/importmap.rb 파일에 CDN 경로를 명시합니다. CSS 파일은 app/views/layouts/application.html.erb에 직접 링크하여 전역적으로 적용합니다.

    ruby # config/importmap.rb pin "tom-select", to: "https://cdn.jsdelivr.net/npm/tom-select@2.4.3/+esm"

    ```html

    ```

Stimulus 컨트롤러 구현

두 가지 Stimulus 컨트롤러가 필요합니다. 하나는 일반 TomSelect 초기화를 위한 것이고, 다른 하나는 종속 선택 박스 로직을 처리합니다.

  • select_controller.js: 모든 data-controller="select" 요소에 TomSelect를 초기화하는 기본적인 컨트롤러입니다. 연결 시 TomSelect 인스턴스를 생성하고, 연결 해제 시 인스턴스를 파괴하여 메모리 누수를 방지합니다.

    ```javascript // app/javascript/controllers/select_controller.js import { Controller } from “@hotwired/stimulus” import TomSelect from “tom-select”

    export default class extends Controller { connect() { new TomSelect(this.element) } disconnect() { if (this.element.tomselect) { this.element.tomselect.destroy() } } } ```

  • select_with_dependent_controller.js: 이 컨트롤러는 종속 선택 박스의 핵심 로직을 담당합니다. static values를 사용하여 AJAX 요청에 필요한 URL, 파라미터 이름, 대상 요소 ID, 그리고 JSON 응답에서 값을 추출할 키를 설정합니다. update 메서드는 상위 선택 박스의 change 이벤트에 의해 트리거됩니다. 이 메서드는 현재 선택된 값을 기반으로 AJAX 요청을 생성하고, 서버로부터 받은 JSON 데이터를 파싱하여 하위 선택 박스의 옵션을 동적으로 업데이트합니다. TomSelect가 적용된 경우 clearOptions, addOption, refreshOptions 메서드를 사용하여 옵션을 조작하며, 일반 <select> 요소의 경우 innerHTML을 직접 조작합니다.

    javascript // app/javascript/controllers/select_with_dependent_controller.js // ... (생략) export default class extends Controller { static values = { url: String, param: "id", target: String, target_name: "name", target_value: "id" } connect() { new TomSelect(this.element) } disconnect() { /* ... */ } update() { const url = new URL(this.urlValue, window.location.origin) url.searchParams.set(this.paramValue, this.element.value) fetch(url, { headers: { Accept: "application/json" } }) .then(response => response.json()) .then(data => { const target = document.getElementById(this.targetValue) if (target && target.tomselect) { target.tomselect.clearOptions() data.forEach(option => { target.tomselect.addOption({ value: option[this.targetValueValue], text: option[this.targetNameValue] }) }) target.tomselect.refreshOptions(false) } // ... (일반 select fallback) }) } }

Rails 백엔드 구성

백엔드는 AJAX 요청에 응답하여 필터링된 데이터를 JSON 형태로 제공하는 역할을 합니다.

  • 라우팅 설정: config/routes.rb 파일에 searches/products 리소스에 대한 라우트를 정의하여 index 액션을 통해 제품 목록을 가져올 수 있도록 합니다.

    ruby # config/routes.rb namespace :searches do resources :products, only: :index end

  • 컨트롤러 구현: app/controllers/searches/products_controller.rb에서 index 액션은 params[:company_id]를 기반으로 제품을 필터링하고, 그 결과를 JSON 형태로 렌더링합니다. filter_by_company private 메서드는 company_id 파라미터의 유무에 따라 필터링 조건을 동적으로 생성합니다.

    ruby # app/controllers/searches/products_controller.rb module Searches class ProductsController < ApplicationController def index @products = Product.where(filter_by_company) render json: @products.to_json end private def filter_by_company return {} unless params[:company_id].present? { company_id: params[:company_id] } end end end

뷰 통합

app/views/welcome/index.html.erb 파일에서 두 개의 선택 박스를 정의하고 Stimulus 컨트롤러와 연결합니다.

  • 상위 선택 박스: company 선택 박스에는 data-controller="select-with-dependent"data-action="change->select-with-dependent#update"를 적용하여 변경 시 select_with_dependent 컨트롤러의 update 메서드가 호출되도록 합니다. 또한, data-select-with-dependent-url-value, data-select-with-dependent-target-value, data-select-with-dependent-param-value 속성을 통해 컨트롤러에 필요한 정보를 전달합니다.
  • 하위 선택 박스: product 선택 박스에는 data-controller="select"를 적용하여 기본적인 TomSelect 기능을 활성화하고, id: :product_select를 부여하여 상위 컨트롤러가 이 요소를 식별할 수 있도록 합니다.

결론

이 구현 방식은 Stimulus와 TomSelect의 강력한 조합을 통해 Rails 애플리케이션에서 동적이고 사용자 친화적인 종속 선택 박스 기능을 제공합니다. 프론트엔드와 백엔드의 명확한 역할 분리를 통해 코드의 가독성과 유지보수성을 높일 수 있으며, AJAX를 활용하여 페이지 전체를 새로고침하지 않고도 필요한 데이터만 동적으로 업데이트함으로써 사용자 경험을 크게 향상시킵니다. 이러한 접근 방식은 복잡한 폼이나 필터링 기능을 요구하는 다양한 웹 애플리케이션 시나리오에 효과적으로 적용될 수 있습니다.

댓글 0

댓글 작성

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

아직 댓글이 없습니다

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