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
이 선언으로 rss
및 index
메서드를 제외한 모든 곳에서 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#index
는main
레이아웃을 사용합니다.SpecialArticlesController#index
는special
레이아웃을 사용합니다.OldArticlesController#show
는 레이아웃을 전혀 사용하지 않습니다.OldArticlesController#index
는old
레이아웃을 사용합니다.
템플릿 상속
레이아웃 상속 논리와 유사하게, 템플릿 또는 부분 템플릿이 기본 경로에서 찾을 수 없는 경우 컨트롤러는 상속 체인에서 렌더링할 템플릿 또는 부분 템플릿을 찾습니다. 예를 들어:
# 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_to
와 redirect_back
은 즉시 메서드 실행을 중단하고 반환하지 않습니다. 이들 뒤에 오는 문장들도 실행됩니다. 필요한 경우 명시적인 return
또는 다른 중단 메커니즘을 사용할 수 있습니다.
다른 리디렉션 상태 코드 가져오기
Rails는 redirect_to
를 호출할 때 302 상태 코드(임시 리디렉션)를 사용합니다. 301(영구 리디렉션)과 같은 다른 상태 코드를 사용하려면 :status
옵션을 사용할 수 있습니다:
redirect_to photos_path, status: 301
render
의 :status
옵션과 마찬가지로, redirect_to
의 :status
옵션은 숫자 및 기호 헤더 지정을 모두 허용합니다.
render
와 redirect_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/1
로 show
액션을 요청했고 컨트롤러는 책이 없다는 것을 발견했으므로 컨트롤러는 브라우저에 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가 응답으로 뷰를 렌더링할 때, 이전 섹션에서 다룬 현재 레이아웃 찾기 규칙에 따라 뷰를 현재 레이아웃과 결합합니다. 레이아웃 내에서는 다양한 출력 조각을 결합하여 전체 응답을 만들 수 있는 세 가지 도구를 사용할 수 있습니다:
- 자산 태그
yield
및content_for
- 부분 템플릿
자산 태그 헬퍼
자산 태그 헬퍼는 뷰에 피드, JavaScript, 스타일시트, 이미지, 비디오, 오디오에 대한 링크를 생성하는 HTML을 제공합니다. Rails에는 다음과 같은 6개의 자산 태그 헬퍼가 있습니다:
이 태그들은 레이아웃이나 다른 뷰에서 사용할 수 있지만, auto_discovery_link_tag
, javascript_include_tag
, stylesheet_link_tag
는 주로 레이아웃의 <head>
섹션에서 사용됩니다.
경고: 자산 태그 헬퍼는 지정된 위치의 자산 존재 여부를 _확인하지 않습니다. 당신이 무엇을 하고 있는지 알고 있다고 가정하고 링크를 생성합니다.
피드 링크 생성하기: auto_discovery_link_tag
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.js
와 app/assets/javascripts/columns.js
를 동시에 포함하려면:
<%= javascript_include_tag "main", "columns" %>
app/assets/javascripts/main.js
와 app/assets/javascripts/photos/columns.js
를 포함하려면:
<%= javascript_include_tag "main", "/photos/columns" %>
http://example.com/main.js
를 포함하려면:
<%= javascript_include_tag "http://example.com/main.js" %>
CSS 파일 링크 생성하기: stylesheet_link_tag
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.css
와 app/assets/stylesheets/columns.css
를 포함하려면:
<%= stylesheet_link_tag "main", "columns" %>
app/assets/stylesheets/main.css
와 app/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_tag
는 rel="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
를 사용할 수 있습니다.
이에 대한 간단한 단축 구문도 있습니다. @products
가 Product
인스턴스의 컬렉션이라고 가정하면 다음과 같이 작성할 수 있습니다:
<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는 각 컬렉션 멤버에 적합한 고객 또는 직원 부분 템플릿을 사용합니다.
컬렉션이 비어 있는 경우 render
는 nil
을 반환하므로 대체 콘텐츠를 제공하는 것이 비교적 쉽습니다.
<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
로 대체할 수 있습니다.