Turbo Frame 모달을 구현하기 위한 핵심 요구사항은 두 가지입니다. 첫째, 모달로 렌더링하려는 액션에 대한 링크는 data: { turbo_frame: 'modal' }
속성을 사용하여 ‘modal’이라는 특정 turbo-frame
을 대상으로 지정해야 합니다. 예를 들어, 새로운 작업을 생성하는 링크는 <%= link_to 'New Task', new_task_path, class: 'btn', data: { turbo_frame: 'modal' } %>
와 같이 작성됩니다. 이 속성은 링크 클릭 시 해당 turbo-frame
내부에서만 콘텐츠가 로드되도록 Turbo에게 지시합니다.
둘째, 모달로 사용될 컨트롤러 액션은 render layout: 'modal'
을 통해 ‘modal’ 레이아웃을 명시적으로 지정해야 합니다. TasksController
의 new
액션이 좋은 예시입니다. def new; @task = Task.new; render layout: 'modal'; end
와 같이 정의하여, 새로운 작업 생성 폼이 모달 레이아웃 내에서 렌더링되도록 합니다. 폼 제출 후 유효성 검사에 실패할 경우, render :new, status: :unprocessable_content, layout: 'modal'
과 같이 다시 모달 레이아웃으로 에러를 렌더링하여 사용자에게 피드백을 제공합니다.
모달 내에서 폼 제출이 성공했을 때, 모달 프레임을 벗어나 전체 페이지를 업데이트하는 메커니즘은 프론트엔드의 이벤트 리스너를 통해 처리됩니다. turbo-rails
는 기본적으로 turbo-frame
요청 시 레이아웃을 비어있는 자체 레이아웃으로 오버라이드합니다. 따라서 폼 제출 성공 후 리다이렉트가 발생하면, 애플리케이션 레이아웃에 포함된 빈 turbo-frame
이 “누락”된 상태가 됩니다. 이때 document.addEventListener('turbo:frame-missing', (event) => { ... });
이벤트 리스너가 작동합니다. 이 리스너는 event.target.id
가 ‘modal’인 경우 event.preventDefault()
를 호출하여 기본 동작을 막고, event.detail.visit(event.detail.response.url, { action: 'replace' })
를 사용하여 전체 페이지를 방문하도록 지시합니다. action: 'replace'
옵션은 현재 페이지로 리다이렉트될 때 Turbo “새로고침”으로 처리하여 모핑(morphing) 및 스크롤 위치 보존을 가능하게 합니다. 이 방식은 서버에서 성공적인 리다이렉션을 처리하고, 클라이언트에서는 이벤트를 감지하여 전체 페이지 전환을 유도하는 우아한 해결책을 제공합니다.