Rails에서의 레이아웃과 렌더링

이 가이드는 Action Controller와 Action View의 기본적인 레이아웃 기능을 다룹니다.

이 가이드를 읽고 나면 다음을 알 수 있습니다:

  • Rails에 내장된 다양한 렌더링 방법을 사용하는 방법.
  • 여러 콘텐츠 섹션이 있는 레이아웃을 만드는 방법.
  • 뷰를 DRY하게 만들기 위해 부분 템플릿을 사용하는 방법.
  • 중첩 레이아웃(서브 템플릿)을 사용하는 방법.

개요: 구성 요소들의 상호 작용

이 가이드는 모델-뷰-컨트롤러 삼각형에서 컨트롤러와 뷰 간의 상호 작용에 초점을 맞추고 있습니다. 알다시피 컨트롤러는 요청 처리 전체 과정을 조율하는 책임이 있지만, 보통 무거운 코드는 모델에 위임합니다. 그리고 사용자에게 응답을 보내야 할 때가 되면, 컨트롤러는 뷰에 일을 넘깁니다. 이 일 넘김이 이 가이드의 주제입니다.

큰 그림으로 보면, 이는 어떤 응답을 보낼지 결정하고 그에 적합한 메서드를 호출하는 것을 포함합니다. 응답이 전체 뷰인 경우, Rails는 뷰를 레이아웃으로 감싸고 부분 뷰를 가져오는 등의 추가 작업을 수행합니다. 이 가이드의 뒷부분에서 이러한 경로를 모두 살펴볼 것입니다.

응답 생성하기

컨트롤러의 관점에서 볼 때, 브라우저에 보내는 HTTP 응답을 만드는 방법은 세 가지가 있습니다:

  • render를 호출하여 브라우저에 보낼 전체 응답을 만듭니다.
  • redirect_to를 호출하여 브라우저에 HTTP 리디렉션 상태 코드를 보냅니다.
  • head를 호출하여 브라우저에 보낼 HTTP 헤더만으로 구성된 응답을 만듭니다.

기본 렌더링: 구성보다는 규약

“구성보다는 규약"이라는 Rails의 철학을 들어보셨을 것입니다. 기본 렌더링은 이 철학의 훌륭한 예입니다. 기본적으로 Rails 컨트롤러는 유효한 경로에 해당하는 이름의 뷰를 자동으로 렌더링합니다. 예를 들어, BooksController 클래스에 다음과 같은 코드가 있다면:

class BooksController < ApplicationController
end

그리고 경로 파일에 다음과 같이 정의되어 있다면:

resources :books

그리고 app/views/books/index.html.erb에 다음과 같은 뷰 파일이 있다면:

<h1>Books are coming soon!</h1>

Rails는 /books로 이동할 때 app/views/books/index.html.erb를 자동으로 렌더링하고 "Books are coming soon!"을 화면에 표시할 것입니다.

그러나 곧 다가올 화면은 최소한의 유용성만 있으므로, 곧 Book 모델을 만들고 BooksController에 index 액션을 추가할 것입니다:

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end

주목할 점은 "구성보다는 규약” 원칙에 따라 index 액션의 끝에 명시적인 렌더링이 없다는 것입니다. 규칙은 액션 끝에 무언가를 명시적으로 렌더링하지 않으면 Rails가 자동으로 컨트롤러의 뷰 경로에서 action_name.html.erb 템플릿을 찾아 렌더링한다는 것입니다. 따라서 이 경우 Rails는 app/views/books/index.html.erb 파일을 렌더링할 것입니다.

모든 책의 속성을 뷰에 표시하려면 다음과 같은 ERB 템플릿을 사용할 수 있습니다:

<h1>Listing Books</h1>

<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Content</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @books.each do |book| %>
      <tr>
        <td><%= book.title %></td>
        <td><%= book.content %></td>
        <td><%= link_to "Show", book %></td>
        <td><%= link_to "Edit", edit_book_path(book) %></td>
        <td><%= link_to "Destroy", book, data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to "New book", new_book_path %>

참고: 실제 렌더링은 ActionView::Template::Handlers 모듈의 중첩 클래스에 의해 수행됩니다. 이 가이드에서는 이 과정을 자세히 다루지 않지만, 뷰 파일의 확장자가 템플릿 핸들러 선택에 영향을 준다는 것을 알아두는 것이 중요합니다.

render 사용하기

대부분의 경우, 컨트롤러의 render 메서드가 브라우저에 사용할 애플리케이션 콘텐츠를 렌더링하는 주된 작업을 수행합니다. render의 동작을 다양하게 사용자 정의할 수 있습니다. Rails 템플릿의 기본 뷰를 렌더링하거나, 특정 템플릿, 파일, 인라인 코드, 아무것도 렌더링하지 않을 수 있습니다. 텍스트, JSON, XML을 렌더링할 수 있습니다. 렌더링된 응답의 콘텐츠 유형이나 HTTP 상태를 지정할 수도 있습니다.

팁: 브라우저를 통해 확인하지 않고도 render의 정확한 결과를 보려면 render_to_string을 호출할 수 있습니다. 이 메서드는 render와 정확히 같은 옵션을 사용하지만, 응답을 브라우저에 보내는 대신 문자열을 반환합니다.

액션의 뷰 렌더링하기

같은 컨트롤러 내의 다른 템플릿을 렌더링하려면 뷰 이름을 사용하여 render를 사용할 수 있습니다:

def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render "edit"
  end
end

update 호출이 실패하면 이 컨트롤러의 update 액션을 호출할 때 edit.html.erb 템플릿이 렌더링됩니다.

문자열 대신 기호를 사용하여 렌더링할 액션을 지정할 수도 있습니다:

def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render :edit, status: :unprocessable_entity
  end
end

다른 컨트롤러의 액션 템플릿 렌더링하기

완전히 다른 컨트롤러의 템플릿을 렌더링하고 싶다면 render를 사용할 수 있습니다. 이때 템플릿의 전체 경로(app/views를 기준)를 지정하면 됩니다. 예를 들어, AdminProductsController에서 실행 중인 코드가 app/controllers/admin 디렉토리에 있다면 다음과 같이 app/views/products 템플릿의 결과를 렌더링할 수 있습니다:

render "products/show"

Rails는 문자열에 포함된 슬래시 문자로 인해 이 뷰가 다른 컨트롤러에 속한다는 것을 알 수 있습니다. 명시적으로 지정하고 싶다면 :template 옵션을 사용할 수 있습니다(Rails 2.2 이전에는 이 옵션이 필수였습니다):

render template: "products/show"

정리하면

위의 두 가지 렌더링 방법(같은 컨트롤러의 다른 액션 템플릿 렌더링, 다른 컨트롤러의 다른 액션 템플릿 렌더링)은 사실 같은 작업의 변형입니다.

실제로 BooksController 클래스의 update 액션에서 책이 성공적으로 업데이트되지 않으면 edit.html.erb 템플릿을 렌더링하고 싶다면, 다음과 같은 모든 렌더링 호출이 views/books 디렉토리의 edit.html.erb 템플릿을 렌더링할 것입니다:

render :edit
render action: :edit
render "edit"
render action: "edit"
render "books/edit"
render template: "books/edit"

어떤 것을 사용할지는 스타일과 관습의 문제이지만, 코드에 가장 적합한 가장 간단한 것을 사용하는 것이 좋습니다.

:inline을 사용하여 render 하기

render 메서드는 메서드 호출의 일부로 ERB를 제공하는 :inline 옵션을 사용하여 뷰 없이도 전체 응답을 만들 수 있습니다. 이것은 완전히 유효합니다:

render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>"

경고: 이 옵션을 사용할 이유는 거의 없습니다. ERB를 컨트롤러에 섞는 것은 Rails의 MVC 지향성을 무너뜨리고 다른 개발자가 프로젝트의 로직을 따라가기 어렵게 만듭니다. 대신 별도의 erb 뷰를 사용하세요.

기본적으로 인라인 렌더링은 ERB를 사용합니다. :type 옵션을 사용하여 Builder를 강제로 사용하도록 할 수 있습니다:

render inline: "xml.p {'Horrid coding practice!'}", type: :builder

텍스트 렌더링하기

render:plain 옵션을 사용하면 브라우저에 순수 텍스트(마크업 없음)를 보낼 수 있습니다:

render plain: "OK"

팁: 순수 텍스트 렌더링은 Ajax 또는 웹 서비스 요청에 응답할 때 가장 유용합니다. 이런 요청은 적절한 HTML 이외의 것을 기대합니다.

참고: 기본적으로 :plain 옵션을 사용하면 텍스트가 현재 레이아웃을 사용하지 않고 렌더링됩니다. 텍스트를 현재 레이아웃에 넣으려면 layout: true 옵션을 추가하고 레이아웃 파일의 확장자를 .text.erb로 사용해야 합니다.

HTML 렌더링하기

render:html 옵션을 사용하면 브라우저에 HTML 문자열을 보낼 수 있습니다:

render html: helpers.tag.strong('Not Found')

팁: 이것은 작은 HTML 코드 조각을 렌더링할 때 유용합니다. 그러나 마크업이 복잡한 경우에네, 번역을 계속하겠습니다.

JSON 렌더링하기

JSON은 많은 Ajax 라이브러리에서 사용되는 JavaScript 데이터 형식입니다. Rails에는 객체를 JSON으로 변환하고 해당 JSON을 브라우저에 렌더링하는 기능이 내장되어 있습니다:

render json: @product

팁: 렌더링할 객체의 to_json을 호출할 필요가 없습니다. :json 옵션을 사용하면 render가 자동으로 to_json을 호출합니다.

XML 렌더링하기

Rails에는 객체를 XML로 변환하고 해당 XML을 호출자에게 렌더링하는 기능도 내장되어 있습니다:

render xml: @product

팁: 렌더링할 객체의 to_xml을 호출할 필요가 없습니다. :xml 옵션을 사용하면 render가 자동으로 to_xml을 호출합니다.

순수 JavaScript 렌더링하기

Rails는 순수 JavaScript를 렌더링할 수 있습니다:

render js: "alert('Hello Rails');"

이렇게 하면 제공된 문자열이 text/javascript MIME 유형으로 브라우저에 전송됩니다.

원시 본문 렌더링하기

render:body 옵션을 사용하면 콘텐츠 유형을 설정하지 않고 원시 콘텐츠를 브라우저에 보낼 수 있습니다:

render body: "raw"

팁: 이 옵션은 응답의 콘텐츠 유형에 신경 쓰지 않는 경우에만 사용해야 합니다. 대부분의 경우 :plain 또는 :html을 사용하는 것이 더 적절할 것입니다.

참고: 달리 지정하지 않는 한 이 렌더 옵션의 응답은 기본 콘텐츠 유형인 text/plain이 됩니다.

원시 파일 렌더링하기

Rails는 절대 경로의 원시 파일을 렌더링할 수 있습니다. 이는 404 페이지와 같은 정적 파일을 조건부로 렌더링할 때 유용합니다.

render file: "#{Rails.root}/public/404.html", layout: false

이렇게 하면 원시 파일이 렌더링됩니다(ERB나 다른 핸들러를 지원하지 않음). 기본적으로 현재 레이아웃 내에서 렌더링됩니다.

경고: 사용자 입력과 함께 :file 옵션을 사용하면 보안 문제가 발생할 수 있습니다. 공격자가 이 작업을 사용하여 파일 시스템의 보안에 민감한 파일에 액세스할 수 있기 때문입니다.

팁: 레이아웃이 필요하지 않은 경우 send_file이 종종 더 빠르고 나은 옵션입니다.

객체 렌더링하기

Rails는 #render_in에 응답하는 객체를 렌더링할 수 있습니다. 객체에 #format을 정의하면 형식을 제어할 수 있습니다.

class Greeting
  def render_in(view_context)
    view_context.render html: "Hello, World"
  end

  def format
    :html
  end
end

render Greeting.new
# => "Hello World"

이렇게 하면 제공된 객체의 render_in이 현재 뷰 컨텍스트로 호출됩니다. render:renderable 옵션을 사용하여 객체를 제공할 수도 있습니다:

render renderable: Greeting.new
# => "Hello World"

render의 옵션

render 메서드 호출은 일반적으로 다음 6개의 옵션을 허용합니다:

  • :content_type
  • :layout
  • :location
  • :status
  • :formats
  • :variants
:content_type 옵션

기본적으로 Rails는 렌더링 작업의 결과를 text/html MIME 콘텐츠 유형(또는 :json 옵션을 사용하면 application/json, :xml 옵션을 사용하면 application/xml)으로 제공합니다. 이를 변경해야 할 때가 있는데, :content_type 옵션을 사용하여 이를 수행할 수 있습니다:

render template: "feed", content_type: "application/rss"
:layout 옵션

render의 대부분의 옵션에서 렌더링된 콘텐츠는 현재 레이아웃의 일부로 표시됩니다. 이 가이드의 뒷부분에서 레이아웃에 대해 자세히 알아볼 것입니다.

:layout 옵션을 사용하여 현재 액션에 대해 특정 파일을 레이아웃으로 사용하도록 Rails에 지시할 수 있습니다:

render layout: "special_layout"

또한 레이아웃 없이 렌더링하도록 지시할 수 있습니다:

render layout: false
:location 옵션

:location 옵션을 사용하여 HTTP Location 헤더를 설정할 수 있습니다:

render xml: photo, location: photo_url(photo)
:status 옵션

Rails는 자동으로 적절한 HTTP 상태 코드(대부분의 경우 200 OK)를 생성합니다. :status 옵션을 사용하여 이를 변경할 수 있습니다:

render status: 500
render status: :forbidden

Rails는 숫자 상태 코드와 아래 표에 나와 있는 해당 기호를 모두 이해합니다.

응답 클래스 HTTP 상태 코드 기호
정보 100 :continue
101 :switching_protocols
102 :processing
성공 200 :ok
201 :created
202 :accepted
203 :nonauthoritativeinformation
204 :no_content
205 :reset_content
206 :partial_content
207 :multi_status
208 :already_reported
226 :im_used
리디렉션 300 :multiple_choices
301 :moved_permanently
302 :found
303 :see_other
304 :not_modified
305 :use_proxy
307 :temporary_redirect
308 :permanent_redirect
클라이언트 오류 400 :bad_request
401 :unauthorized
402 :payment_required
403 :forbidden
404 :not_found
405 :methodnotallowed
406 :not_acceptable
407 :proxyauthenticationrequired
408 :request_timeout
409 :conflict
410 :gone
411 :length_required
412 :precondition_failed
413 :payloadtoolarge
414 :uritoolong
415 :unsupportedmediatype
416 :rangenotsatisfiable
417 :expectation_failed
421 :misdirected_request
422 :unprocessable_entity
423 :locked
424 :failed_dependency
426 :upgrade_required
428 :precondition_required
429 :toomanyrequests
431 :requestheaderfieldstoolarge
451 :unavailableforlegal_reasons
서버 오류 500 :internalservererror
501 :not_implemented
502 :bad_gateway
503 :service_unavailable
504 :gateway_timeout
505 :httpversionnot_supported
506 :variantalsonegotiates
507 :insufficient_storage
508 :loop_detected
510 :not_extended
511 :networkauthenticationrequired

참고: 콘텐츠 상태 코드(100-199, 204, 205 또는 304)와 함께 콘텐츠를 렌더링하려고 하면 응답에서 삭제됩니다.

:formats 옵션

Rails는 기본적으로 요청에 지정된 형식(또는 기본적으로 :html)을 사용합니다. :formats 옵션을 사용하여 기호 또는 배열로 형식을 변경할 수 있습니다:

render formats: :xml
render formats: [:json, :xml]

지정된 형식의 템플릿이 없으면 ActionView::MissingTemplate 오류가 발생합니다.

:variants 옵션

이것은 Rails에게 동일한 형식의 템플릿 변형을 찾도록 지시합니다. 기호 또는 배열로 :variants 옵션을 전달하여 변형 목록을 지정할 수 있습니다.

사용 예는 다음과 같습니다.

# HomeController#index에서 호출됨
render variants: [:mobile, :desktop]

이 변형 집합으로 Rails는 다음과 같은 템플릿을 찾아 첫 번째로 존재하는 것을 사용합니다.

  • app/views/home/index.html+mobile.erb
  • app/views/home/index.html+desktop.erb
  • app/views/home/index.html.erb

지정된 형식의 템플릿이 없으면 ActionView::MissingTemplate 오류가 발생합니다.

변형을 렌더링 호출에 설정하는 대신 컨트롤러 액션에서 요청 객체에 설정할 수도 있습니다.

def index
  request.variant = determine_variant
end

private
  def determine_variant
    variant = nil
    # 사용할 변형(들)을 결정하는 코드
    variant = :mobile if session[:use_mobile]

    variant
  end

레이아웃 찾기

현재 레이아웃을 찾기 위해 Rails는 먼저 app/views/layouts 디렉토리에서 컨트롤러와 같은 기본 이름의 파일을 찾습니다. 예를 들어, PhotosController 클래스의 액션을 렌더링하면 app/views/layouts/photos.html.erb(또는 app/views/layouts/photos.builder)를 사용합니다. 컨트롤러별 레이아웃이 없으면 Rails는 app/views/layouts/application.html.erb 또는 app/views/layouts/application.builder를 사용합니다. .erb 레이아웃이 없으면 .builder 레이아웃을 사용합니다. Rails는 또한 개별 컨트롤러와 액션에 대해 특정 레이아웃을 지정하는 여러 가지 방법을 제공합니다.

컨트롤러에 대한 레이아웃 지정하기

[layout][] 선언을 사용하여 기본 레이아웃 규칙을 컨트롤러에서 재정의할 수 있습니다. 예를 들어:

class ProductsController < ApplicationController
  layout "inventory"
  #...
end

이 선언으로 ProductsController에 의해 렌더링된 모든 뷰는 app/views/layouts/inventory.html.erb를 레이아웃으로 사용합니다.

전체 애플리케이션에 대한 특정 레이아웃을 지정하려면 ApplicationController 클래스에서 layout네, 번역을 계속하겠습니다.

런타임에 레이아웃 선택하기

기호를 사용하여 레이아웃 선택을 요청 처리 시로 미룰 수 있습니다:

class ProductsController < ApplicationController
  layout :products_layout

  def show
    @product = Product.find(params[:id])
  end

  private
    def products_layout
      @current_user.special? ? "special" : "products"
    end
end

이제 현재 사용자가 특별 사용자인 경우 “special” 레이아웃을, 그렇지 않은 경우 “products” 레이아웃을 사용하게 됩니다.

인라인 메서드(Proc 등)를 사용하여 레이아웃을 동적으로 결정할 수도 있습니다. 예를 들어 Proc 객체를 전달하면 Proc 블록에 controller 인스턴스가 제공되므로 현재 요청을 기반으로 레이아웃을 결정할 수 있습니다:

class ProductsController < ApplicationController
  layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" }
end
조건부 레이아웃

컨트롤러 수준에서 지정된 레이아웃은 :only:except 옵션을 지원합니다. 이 옵션은 컨트롤러 내의 메서드 이름 또는 메서드 이름 배열을 허용합니다:

class ProductsController < ApplicationController
  layout "product", except: [:index, :rss]
end

이 선언으로 rssindex 메서드를 제외한 모든 곳에서 product 레이아웃이 사용됩니다.

레이아웃 상속

레이아웃 선언은 계층 구조 아래로 캐스케이드되며, 더 구체적인 레이아웃 선언이 항상 더 일반적인 것을 재정의합니다. 예를 들어:

  • application_controller.rb

    class ApplicationController < ActionController::Base
      layout "main"
    end
    
  • articles_controller.rb

    class ArticlesController < ApplicationController
    end
    
  • special_articles_controller.rb

    class SpecialArticlesController < ArticlesController
      layout "special"
    end
    
  • old_articles_controller.rb

    class OldArticlesController < SpecialArticlesController
      layout false
    
      def show
        @article = Article.find(params[:id])
      end
    
      def index
        @old_articles = Article.older
        render layout: "old"
      end
      # ...
    end
    

이 애플리케이션에서:

  • 일반적으로 뷰는 main 레이아웃에서 렌더링됩니다.
  • ArticlesController#indexmain 레이아웃을 사용합니다.
  • SpecialArticlesController#indexspecial 레이아웃을 사용합니다.
  • OldArticlesController#show는 레이아웃을 전혀 사용하지 않습니다.
  • OldArticlesController#indexold 레이아웃을 사용합니다.
템플릿 상속

레이아웃 상속 논리와 유사하게, 템플릿 또는 부분 템플릿이 기본 경로에서 찾을 수 없는 경우 컨트롤러는 상속 체인에서 렌더링할 템플릿 또는 부분 템플릿을 찾습니다. 예를 들어:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
end
# app/controllers/admin_controller.rb
class AdminController < ApplicationController
end
# app/controllers/admin/products_controller.rb
class Admin::ProductsController < AdminController
  def index
  end
end

admin/products#index 액션에 대한 조회 순서는 다음과 같습니다:

  • app/views/admin/products/
  • app/views/admin/
  • app/views/application/

이를 통해 app/views/application/이 공유 부분 템플릿의 좋은 장소가 됩니다. 이 부분 템플릿은 ERB에서 다음과 같이 렌더링할 수 있습니다:

<%# app/views/admin/products/index.html.erb %>
<%= render @products || "empty_list" %>

<%# app/views/application/_empty_list.html.erb %>
There are no items in this list <em>yet</em>.

이중 렌더링 오류 피하기

대부분의 Rails 개발자는 “액션당 렌더링 또는 리디렉션은 한 번만 가능합니다"라는 오류 메시지를 보게 됩니다. 이것은 짜증나는 일이지만 비교적 쉽게 해결할 수 있습니다. 보통 render의 작동 방식에 대한 근본적인 오해 때문에 발생합니다.

예를 들어, 이 코드는 이 오류를 발생시킬 것입니다:

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
  render action: "regular_show"
end

@book.special?true로 평가되면 Rails는 @book 변수를 special_show 뷰에 덤프하는 렌더링 프로세스를 시작합니다. 그러나 이것은 show 액션의 나머지 코드가 계속 실행되는 것을 막지 않으며, 액션의 끝에 도달하면 regular_show 뷰를 렌더링하려 하다가 오류가 발생합니다. 해결책은 간단합니다: render 또는 redirect를 호출하는 코드 경로가 하나만 있도록 하세요. return을 사용하면 도움이 될 수 있습니다. 다음은 수정된 메서드입니다:

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
    return
  end
  render action: "regular_show"
end

ActionController가 render가 호출되었는지 감지하므로 다음과 같이 작성하면 오류 없이 작동합니다:

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
end

이렇게 하면 special?가 true인 책은 special_show 템플릿으로, 그렇지 않은 책은 기본 show 템플릿으로 렌더링됩니다.

redirect_to 사용하기

HTTP 요청에 대한 응답을 반환하는 또 다른 방법은 redirect_to를 사용하는 것입니다. 앞서 보았듯이 render는 응답 구성에 사용할 뷰(또는 다른 자산)를 지정합니다. redirect_to 메서드는 완전히 다른 작업을 수행합니다: 브라우저에 새 요청을 보내도록 지시합니다. 예를 들어, 코드 어디에서든 사진 인덱스로 리디렉션할 수 있습니다:

redirect_to photos_url

redirect_back을 사용하여 사용자를 방금 왔던 페이지로 돌려보낼 수 있습니다. 이 위치는 HTTP_REFERER 헤더에서 가져오지만 브라우저에서 이 헤더를 설정하지 않을 수 있으므로 fallback_location을 제공해야 합니다.

redirect_back(fallback_location: root_path)

참고: redirect_toredirect_back은 즉시 메서드 실행을 중단하고 반환하지 않습니다. 이들 뒤에 오는 문장들도 실행됩니다. 필요한 경우 명시적인 return 또는 다른 중단 메커니즘을 사용할 수 있습니다.

다른 리디렉션 상태 코드 가져오기

Rails는 redirect_to를 호출할 때 302 상태 코드(임시 리디렉션)를 사용합니다. 301(영구 리디렉션)과 같은 다른 상태 코드를 사용하려면 :status 옵션을 사용할 수 있습니다:

redirect_to photos_path, status: 301

render:status 옵션과 마찬가지로, redirect_to:status 옵션은 숫자 및 기호 헤더 지정을 모두 허용합니다.

renderredirect_to의 차이

때때로 초보 개발자들은 redirect_to를 일종의 goto 명령으로 생각하여 Rails 코드 내에서 실행 위치를 이동시키는 것으로 생각합니다. 이것은 올바르지 않습니다.

현재 액션이 완료되면 브라우저에 응답을 반환합니다. 그 후 코드 실행이 중지되고 새 요청을 기다리게 되는데, 단지 HTTP 302 상태 코드를 보내 브라우저에 다음에 어떤 요청을 해야 하는지 알려주었을 뿐입니다.

다음 액션을 보면 차이를 알 수 있습니다:

def index
  @books = Book.all
end

def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    render action: "index"
  end
end

이 코드 형태에서는 @book 변수가 nil인 경우 문제가 발생할 것입니다. 기억하세요, render :action은 대상 액션의 코드를 실행하지 않으므로 index 뷰에 필요한 @books 변수가 설정되지 않습니다. 이를 해결하려면 대신 리디렉션을 사용해야 합니다:

def index
  @books = Book.all
end

def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    redirect_to action: :index
  end
end

이 코드에서는 브라우저가 인덱스 페이지에 대한 새 요청을 보내므로 index 메서드의 코드가 실행되고 모든 것이 잘 작동합니다.

이 코드의 단점은 브라우저와 왕복해야 한다는 것입니다: 브라우저가 /books/1show 액션을 요청했고 컨트롤러는 책이 없다는 것을 발견했으므로 컨트롤러는 브라우저에 302 리디렉션 응답을 보내 /books/로 가라고 지시합니다. 브라우저는 이에 따라 index 액션에 대한 새 요청을 컨트롤러에 보내고, 컨트롤러는 데이터베이스의 모든 책을 가져와 인덱스 템플릿을 렌더링하여 브라우저에 다시 보냅니다.

작은 애플리케이션에서는 이 추가 대기 시간이 문제가 되지 않을 수 있지만, 응답 시간이 중요한 경우에는 고려해야 할 사항입니다. 다음과 같은 방법으로 이 문제를 해결할 수 있습니다:

def index
  @books = Book.all
end

def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    @books = Book.all
    flash.now[:alert] = "Your book was not found"
    render "index"
  end
end

이렇게 하면 지정된 ID의 책이 없음을 감지하고 @books 인스턴스 변수에 모든 책을 채운 다음 직접 index.html.erb 템플릿을 렌더링하여 사용자에게 플래시 경고 메시지와 함께 반환합니다.

head를 사용하여 헤더 전용 응답 생성하기

head 메서드를 사용하면 브라우저에 헤더만 포함된 응답을 보낼 수 있습니다. head 메서드는 HTTP 상태 코드를 나타내는 숫자 또는 기호(참조 테이블 참조)를 허용합니다. 옵션 인수는 헤더 이름과 값의 해시로 해석됩니다. 예를 들어, 오류 헤더만 반환할 수 네, 번역을 계속하겠습니다.

head :bad_request

이렇게 하면 다음과 같은 헤더가 생성됩니다:

HTTP/1.1 400 Bad Request
Connection: close
Date: Sun, 24 Jan 2010 12:15:53 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
X-Runtime: 0.013483
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache

또는 다른 HTTP 헤더를 사용하여 다른 정보를 전달할 수 있습니다:

head :created, location: photo_path(@photo)

이렇게 하면 다음과 같은 헤더가 생성됩니다:

HTTP/1.1 201 Created
Connection: close
Date: Sun, 24 Jan 2010 12:16:44 GMT
Transfer-Encoding: chunked
Location: /photos/1
Content-Type: text/html; charset=utf-8
X-Runtime: 0.083496
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache

레이아웃 구조화하기

Rails가 응답으로 뷰를 렌더링할 때, 이전 섹션에서 다룬 현재 레이아웃 찾기 규칙에 따라 뷰를 현재 레이아웃과 결합합니다. 레이아웃 내에서는 다양한 출력 조각을 결합하여 전체 응답을 만들 수 있는 세 가지 도구를 사용할 수 있습니다:

자산 태그 헬퍼

자산 태그 헬퍼는 뷰에 피드, JavaScript, 스타일시트, 이미지, 비디오, 오디오에 대한 링크를 생성하는 HTML을 제공합니다. Rails에는 다음과 같은 6개의 자산 태그 헬퍼가 있습니다:

이 태그들은 레이아웃이나 다른 뷰에서 사용할 수 있지만, auto_discovery_link_tag, javascript_include_tag, stylesheet_link_tag는 주로 레이아웃의 <head> 섹션에서 사용됩니다.

경고: 자산 태그 헬퍼는 지정된 위치의 자산 존재 여부를 _확인하지 않습니다. 당신이 무엇을 하고 있는지 알고 있다고 가정하고 링크를 생성합니다.

auto_discovery_link_tag 헬퍼는 대부분의 브라우저와 피드 리더가 RSS, Atom 또는 JSON 피드의 존재를 감지할 수 있는 HTML을 생성합니다. 링크 유형(:rss, :atom 또는 :json), url_for에 전달되는 옵션 해시, 태그에 대한 옵션 해시를 허용합니다:

<%= auto_discovery_link_tag(:rss, {action: "feed"},
  {title: "RSS Feed"}) %>

auto_discovery_link_tag에 대한 세 가지 태그 옵션이 있습니다:

  • :rel은 링크의 rel 값을 지정합니다. 기본값은 "alternate"입니다.
  • :type은 명시적 MIME 유형을 지정합니다. Rails는 자동으로 적절한 MIME 유형을 생성합니다.
  • :title은 링크의 제목을 지정합니다. 기본값은 대문자 :type 값, 예를 들어 "ATOM” 또는 “RSS"입니다.

JavaScript 파일 링크 생성하기: javascript_include_tag

javascript_include_tag 헬퍼는 제공된 각 소스에 대한 HTML script 태그를 반환합니다.

Rails에서 Asset Pipeline을 사용하는 경우, 이 헬퍼는 이전 버전의 Rails에서 사용되던 public/javascripts가 아닌 /assets/javascripts/에 대한 링크를 생성합니다. 이 링크는 자산 파이프라인에 의해 제공됩니다.

Rails 애플리케이션 또는 Rails 엔진 내의 JavaScript 파일은 app/assets, lib/assets 또는 vendor/assets 중 하나의 위치에 있습니다. 이러한 위치는 Asset Pipeline 가이드의 자산 구성 섹션에서 자세히 설명됩니다.

문서 루트에 대한 전체 경로 또는 URL을 지정할 수 있습니다. 예를 들어 app/assets/javascripts, lib/assets/javascripts 또는 vendor/assets/javascripts 중 하나에 있는 main.js라는 JavaScript 파일에 링크하려면 다음과 같이 하면 됩니다:

<%= javascript_include_tag "main" %>

Rails는 그러면 다음과 같은 script 태그를 출력합니다:

<script src='/assets/main.js'></script>

이 자산 요청은 Sprockets gem에 의해 제공됩니다.

app/assets/javascripts/main.jsapp/assets/javascripts/columns.js를 동시에 포함하려면:

<%= javascript_include_tag "main", "columns" %>

app/assets/javascripts/main.jsapp/assets/javascripts/photos/columns.js를 포함하려면:

<%= javascript_include_tag "main", "/photos/columns" %>

http://example.com/main.js를 포함하려면:

<%= javascript_include_tag "http://example.com/main.js" %>

stylesheet_link_tag 헬퍼는 제공된 각 소스에 대한 HTML <link> 태그를 반환합니다.

Rails에서 "Asset Pipeline"을 사용하는 경우, 이 헬퍼는 /assets/stylesheets/에 대한 링크를 생성합니다. 이 링크는 Sprockets gem에 의해 처리됩니다. 스타일시트 파일은 app/assets, lib/assets 또는 vendor/assets 중 하나의 위치에 있을 수 있습니다.

문서 루트에 대한 전체 경로 또는 URL을 지정할 수 있습니다. 예를 들어 app/assets/stylesheets 디렉토리에 있는 파일에 링크하려면 다음과 같이 하면 됩니다:

<%= stylesheet_link_tag "main" %>

app/assets/stylesheets/main.cssapp/assets/stylesheets/columns.css를 포함하려면:

<%= stylesheet_link_tag "main", "columns" %>

app/assets/stylesheets/main.cssapp/assets/stylesheets/photos/columns.css를 포함하려면:

<%= stylesheet_link_tag "main", "photos/columns" %>

http://example.com/main.css를 포함하려면:

<%= stylesheet_link_tag "http://example.com/main.css" %>

기본적으로 stylesheet_link_tagrel="stylesheet"를 가진 링크를 생성합니다. 적절한 옵션(:rel)을 지정하여 이 기본값을 재정의할 수 있습니다:

<%= stylesheet_link_tag "main_print", media: "print" %>

이미지 링크 생성하기: image_tag

image_tag 헬퍼는 지정된 파일에 대한 HTML <img /> 태그를 생성합니다. 기본적으로 파일은 public/images에서 로드됩니다.

경고: 이미지 확장자를 지정해야 한다는 점에 유의하세요.

<%= image_tag "header.png" %>

원하는 경우 이미지 경로를 제공할 수 있습니다:

<%= image_tag "icons/delete.gif" %>

추가 HTML 옵션을 해시로 제공할 수 있습니다:

<%= image_tag "icons/delete.gif", {height: 45} %>

사용자가 브라우저에서 이미지를 끄면 사용될 대체 텍스트를 제공할 수 있습니다. 명시적으로 alt 텍스트를 지정하지 않으면 파일 이름(확장자 제외)이 대문자로 변환되어 사용됩니다. 예를 들어 다음 두 이미지 태그는 동일한 코드를 생성합니다:

<%= image_tag "home.gif" %>
<%= image_tag "home.gif", alt: "Home" %>

”{width}x{height}“ 형식의 특수 크기 태그도 지정할 수 있습니다:

<%= image_tag "home.gif", size: "50x20" %>

위의 특수 태그 외에도 :class, :id 또는 :name과 같은 표준 HTML 옵션을 마지막 해시로 제공할 수 있습니다:

<%= image_tag "home.gif", alt: "Go Home",
                          id: "HomeImage",
                          class: "nav_bar" %>

비디오 링크 생성하기: video_tag

video_tag 헬퍼는 지정된 파일에 대한 HTML5 <video> 태그를 생성합니다. 기본적으로 파일은 public/videos에서 로드됩니다.

<%= video_tag "movie.ogg" %>

이렇게 하면 다음과 같이 생성됩니다:

<video src="/videos/movie.ogg" />

image_tag와 마찬가지로 절대 경로나 public/videos 디렉토리에 상대적인 경로를 제공할 수 있습니다. 또한 size: "#{width}x#{height}"와 같은 옵션을 지정할 수 있습니다. 비디오 태그는 HTML 옵션 해시를 통해 <video> HTML 옵션 전체를 지원합니다.

비디오 태그는 다음과 같은 옵션을 지원합니다:

  • poster: "image_name.png", 비디오가 재생되기 전에 표시할 이미지를 제공합니다.
  • autoplay: true, 페이지 로드 시 비디오 재생을 시작합니다.
  • loop: true, 비디오가 끝나면 반복 재생합니다.
  • controls: true, 사용자가 비디오와 상호 작용할 수 있는 브라우저 제공 컨트롤을 제공합니다.
  • autobuffer: true, 페이지 로드 시 비디오 파일이 미리 로드됩니다.

배열로 여러 비디오를 전달하여 video_tag에서 재생할 수도 있습니다:

<%= video_tag ["trailer.ogg", "movie.ogg"] %>

이렇게 하면 다음과 같이 생성됩니다:

<video>
  <source src="/videos/trailer.ogg">
  <source src="/videos/movie.ogg">
</video>

오디오 파일 링크 생성하기: audio_tag

audio_tag 헬퍼는 지정된 파일에 대한 HTML5 <audio> 태그를 생성합니다. 기본적으로 파일은 public/audios에서 로드됩니다.네, 번역을 계속하겠습니다.

<%= audio_tag "music.mp3" %>

원하는 경우 오디오 파일 경로를 제공할 수 있습니다:

<%= audio_tag "music/first_song.mp3" %>

:id, :class 등과 같은 추가 옵션도 제공할 수 있습니다.

video_tag와 마찬가지로 audio_tag에도 특별한 옵션이 있습니다:

  • autoplay: true, 페이지 로드 시 오디오 재생을 시작합니다.
  • controls: true, 사용자가 오디오와 상호 작용할 수 있는 브라우저 제공 컨트롤을 제공합니다.
  • autobuffer: true, 페이지 로드 시 오디오 파일이 미리 로드됩니다.

yield 이해하기

레이아웃 컨텍스트 내에서 yield는 뷰에서 가져온 콘텐츠가 삽입되어야 하는 섹션을 식별합니다. 가장 단순한 방법은 전체 뷰 내용이 삽입되는 단일 yield를 사용하는 것입니다:

<html>
  <head>
  </head>
  <body>
  <%= yield %>
  </body>
</html>

여러 개의 yield 영역이 있는 레이아웃도 만들 수 있습니다:

<html>
  <head>
  <%= yield :head %>
  </head>
  <body>
  <%= yield %>
  </body>
</html>

뷰의 기본 본문은 항상 이름 없는 yield에 렌더링됩니다. 이름 있는 yield에 콘텐츠를 렌더링하려면 content_for 메서드를 사용합니다.

content_for 메서드 사용하기

content_for 메서드를 사용하면 레이아웃의 이름 있는 yield 블록에 콘텐츠를 삽입할 수 있습니다. 예를 들어, 방금 본 레이아웃에서 이 뷰가 작동할 것입니다:

<% content_for :head do %>
  <title>A simple page</title>
<% end %>

<p>Hello, Rails!</p>

이 페이지를 제공된 레이아웃에 렌더링하면 다음과 같은 HTML이 생성됩니다:

<html>
  <head>
  <title>A simple page</title>
  </head>
  <body>
  <p>Hello, Rails!</p>
  </body>
</html>

content_for 메서드는 사이드바와 푸터와 같은 레이아웃의 구별된 영역에 자신의 콘텐츠 블록을 삽입해야 할 때 매우 유용합니다. 또한 페이지별 JavaScript 또는 CSS 파일을 헤더에 삽입하는 데에도 유용합니다.

부분 템플릿 사용하기

부분 템플릿 - 일반적으로 "부분"이라고 불리는 - 렌더링 프로세스를 더 관리 가능한 청크로 나누는 또 다른 장치입니다. 부분 템플릿을 사용하면 특정 응답 조각의 렌더링 코드를 자체 파일로 이동할 수 있습니다.

부분 템플릿 이름 지정하기

뷰의 일부로 부분 템플릿을 렌더링하려면 뷰 내에서 render 메서드를 사용합니다:

<%= render "menu" %>

이렇게 하면 _menu.html.erb 파일이 렌더링됩니다. 주목할 점은 선행 밑줄 문자입니다: 부분 템플릿은 일반 뷰와 구분하기 위해 선행 밑줄로 명명됩니다. 다른 폴더에서 부분 템플릿을 가져오는 경우에도 이 규칙이 적용됩니다:

<%= render "application/menu" %>

이 코드는 app/views/application/_menu.html.erb에서 부분 템플릿을 가져옵니다.

부분 템플릿을 사용하여 뷰 단순화하기

부분 템플릿을 사용하는 한 가지 방법은 서브루틴의 등가물로 취급하는 것입니다: 세부 사항을 뷰에서 이동시켜 전체 흐름을 더 쉽게 파악할 수 있게 하는 것입니다. 예를 들어 다음과 같은 뷰가 있을 수 있습니다:

<%= render "application/ad_banner" %>

<h1>Products</h1>

<p>Here are a few of our fine products:</p>
<%# ... %>

<%= render "application/footer" %>

여기서 _ad_banner.html.erb_footer.html.erb 부분 템플릿은 애플리케이션의 많은 페이지에서 공유되는 콘텐츠를 포함할 수 있습니다. 특정 페이지에 집중할 때는 이러한 섹션의 세부 사항을 볼 필요가 없습니다.

이 가이드의 이전 섹션에서 본 것처럼 yield는 양식 레이아웃 정의를 여러 유사한 리소스에 대해 DRY하게 만드는 데 매우 강력한 도구입니다. 예를 들어 다음과 같이 사용할 수 있습니다:

  • users/index.html.erb

    <%= render "application/search_filters", search: @q do |form| %>
      <p>
        Name contains: <%= form.text_field :name_contains %>
      </p>
    <% end %>
    
  • roles/index.html.erb

    <%= render "application/search_filters", search: @q do |form| %>
      <p>
        Title contains: <%= form.text_field :title_contains %>
      </p>
    <% end %>
    
  • application/_search_filters.html.erb

    <%= form_with model: search do |form| %>
      <h1>Search form:</h1>
      <fieldset>
        <%= yield form %>
      </fieldset>
      <p>
        <%= form.submit "Search" %>
      </p>
    <% end %>
    

팁: 애플리케이션의 모든 페이지에서 공유되는 콘텐츠의 경우 레이아웃에서 직접 부분 템플릿을 사용할 수 있습니다.

부분 레이아웃

부분 템플릿에도 자체 레이아웃 파일을 사용할 수 있습니다. 뷰에서와 마찬가지로 말이죠. 예를 들어 다음과 같이 부분 템플릿을 호출할 수 있습니다:

<%= render partial: "link_area", layout: "graybar" %>

이렇게 하면 _link_area.html.erb 부분 템플릿을 찾아 _graybar.html.erb 레이아웃을 사용하여 렌더링합니다. 부분 레이아웃은 일반 부분 템플릿과 마찬가지로 선행 밑줄 명명 규칙을 따르며, 부분 템플릿과 동일한 폴더(마스터 layouts 폴더가 아님)에 배치됩니다.

또한 :partial을 명시적으로 지정해야 하며, :layout과 같은 추가 옵션을 전달할 때도 필요합니다.

지역 변수 전달하기

부분 템플릿에 지역 변수를 전달할 수도 있어 더욱 강력하고 유연하게 만들 수 있습니다. 이 기술을 사용하면 새 페이지와 편집 페이지 간의 중복을 줄이면서도 약간의 고유한 콘텐츠를 유지할 수 있습니다:

  • new.html.erb

    <h1>New zone</h1>
    <%= render partial: "form", locals: {zone: @zone} %>
    
  • edit.html.erb

    <h1>Editing zone</h1>
    <%= render partial: "form", locals: {zone: @zone} %>
    
  • _form.html.erb

    <%= form_with model: zone do |form| %>
      <p>
        <b>Zone name</b><br>
        <%= form.text_field :name %>
      </p>
      <p>
        <%= form.submit %>
      </p>
    <% end %>
    

이 두 뷰에서 동일한 부분 템플릿이 렌더링되지만, Action View의 submit 헬퍼는 새 작업의 경우 "Create Zone"을, 편집 작업의 경우 "Update Zone"을 반환합니다.

특정 경우에만 지역 변수를 부분 템플릿에 전달하려면 local_assigns를 사용할 수 있습니다.

  • index.html.erb

    <%= render user.articles %>
    
  • show.html.erb

    <%= render article, full: true %>
    
  • _article.html.erb

    <h2><%= article.title %></h2>
    
    <% if local_assigns[:full] %>
      <%= simple_format article.body %>
    <% else %>
      <%= truncate article.body %>
    <% end %>
    

이렇게 하면 모든 지역 변수를 선언할 필요 없이 부분 템플릿을 사용할 수 있습니다.

각 부분 템플릿에는 부분 템플릿의 이름(선행 밑줄 제외)과 동일한 이름의 지역 변수가 있습니다. :object 옵션을 통해 이 지역 변수에 객체를 전달할 수 있습니다:

<%= render partial: "customer", object: @new_customer %>

customer 부분 템플릿 내에서 customer 변수는 부모 뷰의 @new_customer 인스턴스를 참조합니다.

모델 인스턴스를 부분 템플릿에 렌더링하려면 간단한 구문을 사용할 수 있습니다:

<%= render @customer %>

@customer 인스턴스 변수에 Customer 모델의 인스턴스가 포함되어 있다고 가정하면, 이렇게 하면 _customer.html.erb를 사용하여 렌더링되며 customer 지역 변수가 부모 뷰의 @customer 인스턴스 변수를 참조하게 됩니다.

컬렉션 렌더링하기

부분 템플릿은 컬렉션 렌더링에 매우 유용합니다. :collection 옵션을 통해 부분 템플릿에 컬렉션을 전달하면, 컬렉션의 각 멤버에 대해 부분 템플릿이 한 번씩 삽입됩니다:

  • index.html.erb

    <h1>Products</h1>
    <%= render partial: "product", collection: @products %>
    
  • _product.html.erb

    <p>Product Name: <%= product.name %></p>
    

부분 템플릿이 복수형 컬렉션으로 호출되면 부분 템플릿의 개별 인스턴스는 렌더링되는 컬렉션 멤버에 대한 변수 이름으로 product를 사용할 수 있습니다.

이에 대한 간단한 단축 구문도 있습니다. @productsProduct 인스턴스의 컬렉션이라고 가정하면 다음과 같이 작성할 수 있습니다:

<h1>Products</h1>
<%= render @products %>

Rails는 컬렉션의 각 멤버에 대해 사용할 부분 템플릿의 이름을 모델 이름에서 유추합니다. 실제로 이질적인 컬렉션을 렌더링하고 이 방식을 사용할 수 있으며, Rails는 컬렉션의 각 멤버에 대해 적절한 부분 템플릿을 선택합니다:

  • index.html.erb

    <h1>Contacts</h1>
    <%= render [customer1, employee1, customer2, employee2] %>
    
  • customers/_customer.html.erb

    <p>Customer: <%= customer.name %></p>
    
  • employees/_employee.html.erb

    ”`html+erb네, 번역을 계속하겠습니다.

  • employees/_employee.html.erb

    <p>Employee: <%= employee.name %></p>
    

이 경우 Rails는 각 컬렉션 멤버에 적합한 고객 또는 직원 부분 템플릿을 사용합니다.

컬렉션이 비어 있는 경우 rendernil을 반환하므로 대체 콘텐츠를 제공하는 것이 비교적 쉽습니다.

<h1>Products</h1>
<%= render(@products) || "There are no products available." %>

지역 변수

부분 템플릿 내에서 사용자 정의 지역 변수 이름을 사용하려면 부분 템플릿 호출에 :as 옵션을 지정하세요:

<%= render partial: "product", collection: @products, as: :item %>

이 변경으로 @products 컬렉션의 각 인스턴스에 item 지역 변수로 액세스할 수 있습니다.

locals: {} 옵션을 사용하여 렌더링되는 모든 부분 템플릿에 임의의 지역 변수를 전달할 수도 있습니다:

<%= render partial: "product", collection: @products,
           as: :item, locals: {title: "Products Page"} %>

이 경우 부분 템플릿에서 title 지역 변수를 “Products Page"의 값으로 사용할 수 있습니다.

카운터 변수

Rails는 부분 템플릿에 대한 카운터 변수도 제공합니다. 변수 이름은 부분 템플릿의 제목 뒤에 _counter가 붙습니다. 예를 들어 @products 컬렉션을 렌더링할 때 _product.html.erb 부분 템플릿에서 product_counter 변수를 사용할 수 있습니다. 이 변수는 부분 템플릿이 현재 뷰에서 몇 번째로 렌더링되었는지를 나타내며, 첫 번째 렌더링에는 값이 0입니다.

# index.html.erb
<%= render partial: "product", collection: @products %>
# _product.html.erb
<%= product_counter %> # 첫 번째 제품은 0, 두 번째 제품은 1...

이는 지역 변수 이름이 as: 옵션을 사용하여 변경된 경우에도 동일하게 작동합니다. 따라서 as: :item을 사용한 경우 카운터 변수는 item_counter가 됩니다.

간격 템플릿

부분 템플릿 사이에 렌더링할 두 번째 부분 템플릿을 지정하려면 :spacer_template 옵션을 사용할 수 있습니다:

<%= render partial: @products, spacer_template: "product_ruler" %>

Rails는 각 _product 부분 템플릿 사이에 _product_ruler 부분 템플릿(데이터 없이)을 렌더링합니다.

컬렉션 부분 레이아웃

컬렉션을 렌더링할 때 :layout 옵션을 사용할 수도 있습니다:

<%= render partial: "product", collection: @products, layout: "special_layout" %>

레이아웃은 컬렉션의 각 항목에 대해 부분 템플릿과 함께 렌더링됩니다. 현재 객체와 객체_카운터 변수는 부분 템플릿과 마찬가지로 레이아웃에서도 사용할 수 있습니다.

중첩 레이아웃 사용하기

애플리케이션에 특정 컨트롤러를 지원하기 위해 약간 다른 레이아웃이 필요할 수 있습니다. 이를 위해 중첩 레이아웃(때로는 서브 템플릿이라고 함)을 사용할 수 있습니다. 예를 들면 다음과 같습니다:

ApplicationController 레이아웃이 다음과 같다고 가정해 보겠습니다:

  • app/views/layouts/application.html.erb

    <html>
    <head>
      <title><%= @page_title or "Page Title" %></title>
      <%= stylesheet_link_tag "layout" %>
      <style><%= yield :stylesheets %></style>
    </head>
    <body>
      <div id="top_menu">Top menu items here</div>
      <div id="menu">Menu items here</div>
      <div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>
    </body>
    </html>
    

NewsController에서 생성된 페이지의 경우 상단 메뉴를 숨기고 오른쪽 메뉴를 추가하고 싶습니다:

  • app/views/layouts/news.html.erb

    <% content_for :stylesheets do %>
      #top_menu {display: none}
      #right_menu {float: right; background-color: yellow; color: black}
    <% end %>
    <% content_for :content do %>
      <div id="right_menu">Right menu items here</div>
      <%= content_for?(:news_content) ? yield(:news_content) : yield %>
    <% end %>
    <%= render template: "layouts/application" %>
    

이것이 전부입니다. News 뷰는 새 레이아웃을 사용하여 상단 메뉴를 숨기고 "content” div 내에 새 오른쪽 메뉴를 추가합니다.

이와 유사한 결과를 얻기 위해 다양한 하위 템플릿 체계를 사용하는 여러 가지 방법이 있습니다. 중첩 수준에 제한이 없다는 점에 유의하세요. ActionView::render 메서드를 통해 render template: 'layouts/news'를 호출하여 News 레이아웃을 기반으로 새 레이아웃을 만들 수 있습니다. News 레이아웃을 하위 템플릿화할 필요가 없다고 확신하는 경우 content_for?(:news_content) ? yield(:news_content) : yield를 단순히 yield로 대체할 수 있습니다.