edge
더 많은 정보: rubyonrails.org에서 확인하세요: 더 많은 Ruby on Rails

레일스에서의 레이아웃과 렌더링

이 가이드는 액션 컨트롤러와 액션 뷰의 기본 레이아웃 기능을 다룹니다.

이 가이드를 읽고 나면 다음을 알게 될 것입니다:

1 개요: 각 부분이 어떻게 연결되는지

이 가이드는 모델-뷰-컨트롤러(MVC) 삼각형에서 컨트롤러와 뷰의 상호작용에 초점을 맞춥니다. 컨트롤러는 레일스에서 요청 처리 과정 전체를 조율하는 역할을 담당하지만, 일반적으로 무거운 코드는 모델에게 넘깁니다. 그러나 사용자에게 응답을 보내야 할 때 컨트롤러는 뷰에게 작업을 넘깁니다. 이 넘김 작업이 이 가이드의 주제입니다.

대략적으로 말하면, 이 작업은 응답으로 보낼 내용을 결정하고 해당 응답을 생성하기 위한 적절한 메소드를 호출하는 것을 포함합니다. 응답이 완전한 뷰인 경우, 레일스는 뷰를 레이아웃으로 감싸고 부분 뷰를 가져오기 위해 추가 작업을 수행합니다. 이 가이드에서 이러한 경로를 모두 볼 수 있습니다.

2 응답 생성하기

컨트롤러의 관점에서 HTTP 응답을 생성하는 방법은 세 가지가 있습니다:

  • 브라우저로 보내기 위해 전체 응답을 생성하기 위해 render를 호출합니다.
  • 브라우저로 HTTP 리디렉션 상태 코드를 보내기 위해 redirect_to를 호출합니다.
  • 브라우저로 보낼 HTTP 헤더만으로 구성된 응답을 생성하기 위해 head를 호출합니다.

2.1 기본적으로 렌더링: 액션에서의 관례적인 설정

레일스가 "관례보다 설정"을 장려한다는 것을 들어보았을 것입니다. 기본 렌더링은 이에 대한 훌륭한 예입니다. 레일스에서 기본적으로 컨트롤러는 유효한 라우트에 해당하는 이름을 가진 뷰를 자동으로 렌더링합니다. 예를 들어, BooksController 클래스에 다음 코드가 있다면:

class BooksController < ApplicationController
end

그리고 라우트 파일에 다음 코드가 있다면:

resources :books

그리고 app/views/books/index.html.erb라는 뷰 파일이 있다면:

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

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

그러나 곧 Book 모델을 생성하고 BooksController에 인덱스 액션을 추가할 것이므로, 곧 나타날 화면은 최소한으로 유용합니다:

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

"관례보다 설정" 원칙에 따라 인덱스 액션의 끝에 명시적인 렌더링이 없습니다. 규칙은 컨트롤러 액션의 끝에서 명시적으로 무언가를 렌더링하지 않으면, 레일스는 컨트롤러의 뷰 경로에서 action_name.html.erb 템플릿을 자동으로 찾아 렌더링합니다. 따라서 이 경우 레일스는 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 모듈의 중첩된 클래스에 의해 수행됩니다. 이 가이드에서는 이 과정을 자세히 다루지 않지만, 뷰의 파일 확장자는 템플릿 핸들러의 선택을 제어하는 데 중요합니다.

2.2 render 사용하기

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

팁: 브라우저에서 검사하지 않고 render 호출의 정확한 결과를 보려면 render_to_string을 호출할 수 있습니다. 이 메소드는 render와 동일한 옵션을 사용하지만, 브라우저로 응답을 보내지 않고 문자열을 반환합니다.

2.2.1 액션의 뷰 렌더링

동일한 컨트롤러 내에서 다른 템플릿에 해당하는 뷰를 렌더링하려면 render와 뷰의 이름을 사용할 수 있습니다:

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

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

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

액션 코드가 포함된 컨트롤러와는 완전히 다른 컨트롤러에서 템플릿을 렌더링하려면 render를 사용하여 렌더링할 템플릿의 전체 경로(app/views를 기준으로 상대 경로)를 전달할 수도 있습니다. 예를 들어, app/controllers/admin에 있는 AdminProductsController에서 코드를 실행하고 있다면 다음과 같이 app/views/products에 있는 액션의 결과를 템플릿으로 렌더링할 수 있습니다:

render "products/show"

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

render template: "products/show"

2.2.3 마무리

앞서 설명한 두 가지 렌더링 방법(동일한 컨트롤러 내의 다른 액션의 템플릿 렌더링 및 다른 컨트롤러의 액션의 템플릿 렌더링)은 사실상 동일한 작업의 변형입니다.

실제로 BooksController 클래스에서는 update 액션 내에서 책이 성공적으로 업데이트되지 않으면 views/books 디렉토리의 edit.html.erb 템플릿을 렌더링하는 다음과 같은 모든 render 호출이 동일한 결과를 가져옵니다:

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

어떤 것을 사용하느냐는 스타일과 관례의 문제이지만, 작성 중인 코드에 가장 적합한 가장 간단한 방법을 사용하는 것이 좋습니다.

2.2.4 :inline과 함께 render 사용하기

render 메소드는 뷰 없이도 사용할 수 있습니다. 이 경우 :inline 옵션을 사용하여 메소드 호출의 일부로 ERB를 제공해야 합니다. 다음은 이에 대한 예입니다:

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

2.2.5 텍스트 렌더링

render:plain 옵션을 사용하여 마크업 없는 일반 텍스트를 브라우저로 보낼 수 있습니다:

render plain: "OK"

팁: 순수 텍스트를 렌더링하는 것은 주로 HTML이 아닌 다른 것을 기대하는 Ajax나 웹 서비스 요청에 응답할 때 가장 유용합니다.

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

2.2.6 HTML 렌더링

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

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

팁: 작은 HTML 코드 조각을 렌더링할 때 유용합니다. 그러나 마크업이 복잡한 경우 템플릿 파일로 이동하는 것이 좋습니다.

참고: html: 옵션을 사용할 때 HTML 엔티티는 html_safe-aware API로 구성되지 않은 문자열인 경우 이스케이프됩니다.

2.2.7 JSON 렌더링

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

render json: @product

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

2.2.8 XML 렌더링

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

render xml: @product

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

2.2.9 Vanilla JavaScript 렌더링

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

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

이는 지정된 문자열을 text/javascript MIME 유형으로 브라우저에 전송합니다.

2.2.10 Raw Body 렌더링

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

render body: "raw"

팁: 이 옵션은 응답의 컨텐츠 유형에 관심이 없는 경우에만 사용해야 합니다. 대부분의 경우 :plain 또는 :html을 사용하는 것이 더 적절할 수 있습니다.

참고: 이 렌더 옵션에서 반환된 응답은 기본적으로 Action Dispatch 응답의 기본 컨텐츠 유형인 text/plain입니다.

2.2.11 Raw File 렌더링

Rails는 절대 경로에서 원시 파일을 렌더링할 수 있습니다. 이는 오류 페이지와 같은 정적 파일을 조건부로 렌더링하는 데 유용합니다.

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

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

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

팁: 레이아웃이 필요하지 않은 경우 send_file이 더 빠르고 좋은 옵션일 수 있습니다.

2.2.12 객체 렌더링

Rails는 :render_in에 응답하는 객체를 렌더링할 수 있습니다.

render MyRenderable.new

이는 제공된 객체에서 현재 뷰 컨텍스트와 함께 render_in을 호출합니다.

또한 :renderable 옵션을 사용하여 객체를 제공할 수도 있습니다:

render renderable: MyRenderable.new

2.2.13 render의 옵션

render 메서드에 대한 호출은 일반적으로 여섯 가지 옵션을 허용합니다:

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

기본적으로 Rails는 렌더링 작업의 결과를 text/html MIME 컨텐츠 유형으로 제공합니다(:json 옵션을 사용하면 application/json을 사용하고 :xml 옵션의 경우 application/xml을 사용합니다). 때로는 이를 변경하고 싶을 수 있으며, 이를 위해 :content_type 옵션을 설정할 수 있습니다:

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

대부분의 render 옵션과 마찬가지로, 렌더링된 컨텐츠는 현재 레이아웃의 일부로 표시됩니다. 나중에 이 가이드에서 레이아웃에 대해 자세히 알아보고 사용하는 방법을 배우게 될 것입니다.

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

render layout: "special_layout"

Rails에게 전혀 레이아웃을 사용하지 않고 렌더링하도록 지시할 수도 있습니다:

render layout: false
2.2.13.3 :location 옵션

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

render xml: photo, location: photo_url(photo)
2.2.13.4 :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 :non_authoritative_information
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 :method_not_allowed
406 :not_acceptable
407 :proxy_authentication_required
408 :request_timeout
409 :conflict
410 :gone
411 :length_required
412 :precondition_failed
413 :payload_too_large
414 :uri_too_long
415 :unsupported_media_type
416 :range_not_satisfiable
417 :expectation_failed
421 :misdirected_request
422 :unprocessable_entity
423 :locked
424 :failed_dependency
426 :upgrade_required
428 :precondition_required
429 :too_many_requests
431 :request_header_fields_too_large
451 :unavailable_for_legal_reasons
서버 오류 500 :internal_server_error
501 :not_implemented
502 :bad_gateway
503 :service_unavailable
504 :gateway_timeout
505 :http_version_not_supported
506 :variant_also_negotiates
507 :insufficient_storage
508 :loop_detected
510 :not_extended
511 :network_authentication_required

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

2.2.13.5 :formats 옵션

Rails는 요청에서 지정된 형식(또는 기본값으로 :html)을 사용합니다. 심볼이나 배열로 :formats 옵션을 전달하여 이를 변경할 수 있습니다:

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

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

2.2.13.6 :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

2.2.14 레이아웃 찾기

현재 레이아웃을 찾기 위해 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는 또한 개별 컨트롤러와 액션에 특정 레이아웃을 더 정확하게 할당하는 여러 가지 방법을 제공합니다.

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

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

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

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

전체 애플리케이션에 특정 레이아웃을 할당하려면 ApplicationController 클래스에서 layout 선언을 사용하십시오:

class ApplicationController < ActionController::Base
  layout "main"
  #...
end

이 선언으로 전체 애플리케이션의 모든 뷰는 레이아웃으로 app/views/layouts/main.html.erb를 사용합니다.

2.2.14.2 런타임에서 레이아웃 선택

심볼을 사용하여 레이아웃 선택을 요청 처리 시기까지 연기할 수 있습니다:

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

이제 현재 사용자가 특별한 사용자인 경우 제품을 보는 동안 특별한 레이아웃을 사용합니다.

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

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

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

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

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

2.2.14.4 레이아웃 상속

레이아웃 선언은 계층 구조에서 아래로 전파되며, 더 구체적인 레이아웃 선언이 항상 더 일반적인 선언을 무시합니다. 예를 들어:

  • 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 %>
이 목록에는 항목이 없습니다. <em>아직</em>.

2.2.15 이중 렌더 오류 피하기

일찍이 대부분의 Rails 개발자들은 "Can only render or redirect once per action"라는 오류 메시지를 볼 수 있습니다. 이는 귀찮은 일이지만, 상대적으로 쉽게 해결할 수 있습니다. 일반적으로 이 오류는 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 액션의 나머지 코드가 실행되는 것을 막지 않으며, Rails는 액션의 끝에 도달하면 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

render에 의해 암묵적으로 수행되는 ActionController의 렌더링은 render가 호출되었는지 여부를 감지하므로 다음은 오류 없이 작동합니다:

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

이는 special?가 설정된 책을 special_show 템플릿으로 렌더링하고, 다른 책은 기본 show 템플릿으로 렌더링합니다.

2.3 redirect_to 사용하기

HTTP 요청에 대한 응답을 반환하는 다른 방법은 redirect_to를 사용하는 것입니다. 앞에서 보았듯이, render는 Rails에게 어떤 뷰(또는 다른 에셋)를 사용하여 응답을 구성해야 하는지 알려줍니다. redirect_to 메서드는 완전히 다른 작업을 수행합니다: 브라우저에게 다른 URL에 대한 새 요청을 보내도록 지시합니다. 예를 들어, 다음 호출로 코드의 어느 곳에서든 현재 위치에서 애플리케이션의 사진 목록으로 리디렉션할 수 있습니다:

redirect_to photos_url

redirect_back을 사용하여 사용자를 방금 전에 있던 페이지로 돌려보낼 수도 있습니다. 이 위치는 브라우저에 의해 설정되지 않을 수 있는 HTTP_REFERER 헤더에서 가져옵니다. 따라서 이 경우에 사용할 fallback_location을 제공해야 합니다.

redirect_back(fallback_location: root_path)

참고: redirect_toredirect_back은 메서드 실행에서 중단하고 즉시 반환하지 않고, 단지 HTTP 응답을 설정합니다. 메서드 이후에 발생하는 문장은 실행됩니다. 필요한 경우 명시적인 return이나 다른 중단 메커니즘을 사용하여 중단할 수 있습니다.

2.3.1 다른 리디렉트 상태 코드 얻기

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

redirect_to photos_path, status: 301

render:status 옵션과 마찬가지로, redirect_to:status는 숫자와 심볼 헤더 표기를 모두 허용합니다.

2.3.2 renderredirect_to의 차이점

경험이 부족한 개발자들은 종종 redirect_to를 Rails 코드에서 한 곳에서 다른 곳으로 실행을 이동하는 일종의 goto 명령으로 생각합니다. 이는 옳지 않습니다. 코드가 실행을 중지하고 브라우저로부터 새 요청을 기다리게 됩니다. 그저 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 변수를 설정할 어떤 것도 없습니다. 렌더링 대신 리디렉션하는 방법으로 이를 수정하는 한 가지 방법이 있습니다: ```ruby 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로 쇼 액션을 요청하고 컨트롤러가 책이 없다는 것을 발견하면 컨트롤러는 브라우저에게 /books/로 이동하라는 302 리디렉션 응답을 보냅니다. 브라우저는 이에 따라 따르고 인덱스 액션을 요청하기 위해 컨트롤러에게 새로운 요청을 다시 보냅니다. 그런 다음 컨트롤러는 데이터베이스에서 모든 책을 가져와 인덱스 템플릿을 렌더링하여 브라우저로 다시 보내고, 브라우저는 화면에 표시합니다.

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

def index
  @books = Book.all
end

def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    @books = Book.all
    flash.now[:alert] = "책을 찾을 수 없습니다"
    render "index"
  end
end

이 코드는 지정된 ID를 가진 책이 없음을 감지하고, 모델의 모든 책을 가진 @books 인스턴스 변수를 채우고, 직접 index.html.erb 템플릿을 렌더링하여 브라우저로 반환하고, 사용자에게 발생한 일에 대해 플래시 알림 메시지를 보냅니다.

2.4 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

3 레이아웃 구조화

Rails가 응답으로 뷰를 렌더링할 때, 이전에 이 가이드에서 다룬 현재 레이아웃을 찾는 규칙을 사용하여 뷰와 현재 레이아웃을 결합합니다. 레이아웃 내에서 전체 응답을 형성하기 위해 다양한 출력 조각을 결합하는 세 가지 도구에 액세스할 수 있습니다:

3.1 에셋 태그 헬퍼

에셋 태그 헬퍼는 뷰를 피드, 자바스크립트, 스타일시트, 이미지, 비디오 및 오디오에 연결하는 HTML을 생성하는 메소드를 제공합니다. Rails에는 다음과 같은 여섯 가지 에셋 태그 헬퍼가 있습니다:

이 태그들은 레이아웃이나 다른 뷰에서 사용할 수 있지만, 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 피드"}) %>

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

  • :rel은 링크의 rel 값을 지정합니다. 기본값은 "alternate"입니다.
  • :type은 명시적인 MIME 타입을 지정합니다. Rails는 자동으로 적절한 MIME 타입을 생성합니다.
  • :title은 링크의 제목을 지정합니다. 기본값은 대문자 :type 값입니다. 예를 들어, "ATOM" 또는 "RSS"입니다. #### javascript_include_tag을 사용하여 JavaScript 파일에 링크하기

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

Asset Pipeline이 활성화된 Rails를 사용하는 경우, 이 헬퍼는 이전 버전의 Rails에서 사용된 public/javascripts 대신 /assets/javascripts/로 링크를 생성합니다. 이 링크는 Asset Pipeline에 의해 제공됩니다.

Rails 애플리케이션 또는 Rails 엔진 내의 JavaScript 파일은 app/assets, lib/assets 또는 vendor/assets 중 하나의 위치에 저장됩니다. 이러한 위치에 대한 자세한 설명은 Asset Pipeline 가이드의 Asset Organization 섹션에서 확인할 수 있습니다.

문서 루트에 대한 전체 경로 또는 URL을 지정할 수 있습니다. 예를 들어, app/assets, lib/assets 또는 vendor/assets 중 하나의 디렉토리 안에 있는 javascripts 디렉토리에 있는 JavaScript 파일에 링크하려면 다음과 같이 작성합니다:

<%= javascript_include_tag "main" %>

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

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

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

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> 태그를 반환합니다.

"Asset Pipeline"이 활성화된 Rails를 사용하는 경우, 이 헬퍼는 /assets/stylesheets/로 링크를 생성합니다. 이 링크는 Sprockets 젬에 의해 처리됩니다. 스타일시트 파일은 app/assets, lib/assets 또는 vendor/assets 중 하나의 위치에 저장될 수 있습니다.

문서 루트에 대한 전체 경로 또는 URL을 지정할 수 있습니다. 예를 들어, app/assets, lib/assets 또는 vendor/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" %>

3.1.3 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" %>

특정 크기 태그를 "{너비}x{높이}" 형식으로 지정할 수도 있습니다:

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

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

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

3.1.4 video_tag을 사용하여 비디오에 링크하기

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

<%= video_tag "movie.ogg" %>

다음과 같이 출력됩니다:

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

image_tag와 마찬가지로 절대 경로 또는 public/videos 디렉토리를 기준으로 상대 경로를 지정할 수 있습니다. 또한 image_tag처럼 size: "#{width}x#{height}" 옵션을 지정할 수도 있습니다. 비디오 태그는 id, class 등과 같은 HTML 옵션도 지정할 수 있습니다.

비디오 태그는 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>

3.1.5 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는 페이지 로드 시 사용자에게 파일을 사전로드합니다.

3.2 yield 이해하기

레이아웃의 맥락에서 yield는 뷰에서 콘텐츠를 삽입해야 할 섹션을 식별합니다. 가장 간단한 방법은 하나의 yield를 가지고 있어서 현재 렌더링되는 뷰의 전체 내용이 삽입되는 것입니다:

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

또한 여러 개의 yield 영역을 가진 레이아웃을 만들 수도 있습니다:

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

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

3.3 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 파일을 헤더에 로드하는 태그를 삽입하는 데에도 유용합니다.

3.4 파셜 사용하기

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

3.4.1 파셜 이름 지정하기

뷰의 일부로 파셜을 렌더링하려면 뷰 내에서 render 메소드를 사용합니다:

<%= render "menu" %>

이렇게 하면 렌더링 중인 뷰 내에서 _menu.html.erb라는 파일이 해당 지점에 렌더링됩니다. 앞에 밑줄 문자가 있는 것에 주목하세요: 파셜은 일반적인 뷰와 구분하기 위해 선행 밑줄 문자로 이름이 지정됩니다. 이는 다른 폴더에서 파셜을 가져올 때에도 마찬가지입니다:

<%= render "shared/menu" %>

이 코드는 app/views/shared/_menu.html.erb에서 파셜을 가져옵니다.

3.4.2 파셜을 사용하여 뷰 단순화하기

파셜을 사용하는 한 가지 방법은 서브루틴의 동등물로서 사용하는 것입니다. 즉, 뷰에서 세부 정보를 이동하여 더 쉽게 이해할 수 있도록 하는 방법입니다. 예를 들어, 다음과 같은 뷰가 있을 수 있습니다:

<%= render "shared/ad_banner" %>

<h1>Products</h1>

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

<%= render "shared/footer" %>

여기서 _ad_banner.html.erb_footer.html.erb 파셜은 응용 프로그램의 많은 페이지에서 공유되는 콘텐츠를 포함할 수 있습니다. 특정 페이지에 집중할 때 이러한 섹션의 세부 정보를 볼 필요가 없습니다.

이 가이드의 이전 섹션에서 본 대로 yield는 레이아웃을 정리하는 데 매우 강력한 도구입니다. 그것은 순수한 루비이므로 거의 모든 곳에서 사용할 수 있습니다. 예를 들어, 여러 유사한 리소스에 대한 폼 레이아웃 정의를 DRY하게 유지하는 데에 사용할 수 있습니다:

  • users/index.html.erb

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

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

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

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

3.4.3 부분 레이아웃

부분은 뷰가 레이아웃을 사용할 수 있는 것처럼 자체 레이아웃 파일을 사용할 수 있습니다. 예를 들어, 다음과 같이 부분을 호출할 수 있습니다:

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

이는 _link_area.html.erb라는 부분을 찾아 _graybar.html.erb 레이아웃을 사용하여 렌더링합니다. 부분 레이아웃은 일반 부분과 마찬가지로 선행 언더스코어 네이밍을 따르며, 소속된 부분과 동일한 폴더에 배치됩니다(마스터 layouts 폴더에는 배치되지 않음).

또한 :layout과 같은 추가 옵션을 전달할 때는 명시적으로 :partial을 지정해야 함에 유의하세요.

3.4.4 로컬 변수 전달

부분에도 로컬 변수를 전달할 수 있으므로 더욱 강력하고 유연하게 사용할 수 있습니다. 예를 들어, 이 기법을 사용하여 새로 만들기 페이지와 편집 페이지 간의 중복을 줄이면서도 약간의 고유한 콘텐츠를 유지할 수 있습니다:

  • new.html.erb

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

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

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

두 뷰에 동일한 부분이 렌더링되지만, 액션 뷰의 submit 도우미는 새로운 액션에 대해 "존 생성"을 반환하고 편집 액션에 대해 "존 업데이트"를 반환합니다.

특정 경우에만 부분에 로컬 변수를 전달하려면 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 인스턴스 변수를 참조합니다.

3.4.5 컬렉션 렌더링

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

  • index.html.erb

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

    <p>제품 이름: <%= product.name %></p>
    

부분이 복수형 컬렉션으로 호출될 때, 부분의 개별 인스턴스는 렌더링되는 컬렉션의 멤버에 대한 변수인 부분 이름을 통해 액세스할 수 있습니다. 이 경우 부분은 _product이며, _product 부분 내에서 렌더링되는 인스턴스에 대해 product를 참조할 수 있습니다.

이에 대한 약식 표현도 있습니다. @productsProduct 인스턴스의 컬렉션이라면, index.html.erb에 다음과 같이 간단히 작성하여 동일한 결과를 얻을 수 있습니다:

<h1>제품</h1>
<%= render @products %>

Rails는 컬렉션의 모델 이름을 보고 사용할 부분의 이름을 결정합니다. 실제로 이 방법으로 이루어진 이질적인 컬렉션을 생성하고 렌더링할 수도 있으며, Rails는 컬렉션의 각 멤버에 대해 적절한 부분을 선택합니다:

  • index.html.erb

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

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

    <p>직원: <%= employee.name %></p>
    

이 경우, Rails는 컬렉션의 각 멤버에 대해 적절한 고객 또는 직원 부분을 사용합니다. 컬렉션이 비어있는 경우, render는 nil을 반환하므로 대체 콘텐츠를 제공하는 것은 매우 간단합니다.

<h1>제품</h1>
<%= render(@products) || "사용 가능한 제품이 없습니다." %>

3.4.6 로컬 변수

파셜 내에서 사용자 정의 로컬 변수 이름을 사용하려면, 파셜 호출에서 :as 옵션을 지정하면 됩니다:

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

이 변경으로 인해, 파셜 내에서 @products 컬렉션의 인스턴스에 item 로컬 변수로 액세스할 수 있습니다.

locals: {} 옵션을 사용하여 렌더링하는 모든 파셜에 임의의 로컬 변수를 전달할 수도 있습니다:

<%= render partial: "product", collection: @products,
           as: :item, locals: {title: "제품 페이지"} %>

이 경우, 파셜은 값이 "제품 페이지"인 title 로컬 변수에 액세스할 수 있습니다.

3.4.7 카운터 변수

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가 됩니다.

3.4.8 간격 템플릿

spacer_template 옵션을 사용하여 메인 파셜의 인스턴스 사이에 두 번째 파셜을 지정할 수도 있습니다:

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

Rails는 _product_ruler 파셜을 (데이터를 전달하지 않고) 각 _product 파셜 쌍 사이에 렌더링합니다.

3.4.9 컬렉션 파셜 레이아웃

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

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

레이아웃은 컬렉션의 각 항목과 함께 렌더링됩니다. 현재 객체와 object_counter 변수도 레이아웃에서 사용할 수 있으며, 파셜 내에서와 동일한 방식으로 사용할 수 있습니다.

3.5 중첩된 레이아웃 사용

일부 컨트롤러를 지원하기 위해 일반적인 애플리케이션 레이아웃과 약간 다른 레이아웃이 필요한 경우, 메인 레이아웃을 반복하고 편집하는 대신 중첩된 레이아웃(때로는 서브 템플릿이라고도 함)을 사용하여 이를 수행할 수 있습니다. 다음은 예입니다:

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

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

    <html>
    <head>
      <title><%= @page_title or "페이지 제목" %></title>
      <%= stylesheet_link_tag "layout" %>
      <style><%= yield :stylesheets %></style>
    </head>
    <body>
      <div id="top_menu">상단 메뉴 항목</div>
      <div id="menu">메뉴 항목</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">오른쪽 메뉴 항목</div>
      <%= content_for?(:news_content) ? yield(:news_content) : yield %>
    <% end %>
    <%= render template: "layouts/application" %>
    

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

이 기술을 사용하여 다른 서브 템플릿 체계로 유사한 결과를 얻는 방법은 여러 가지가 있습니다. 중첩 수준에 제한이 없다는 점에 유의하십시오. 새로운 레이아웃을 News 레이아웃을 기반으로 하기 위해 ActionView::render 메서드를 render template: 'layouts/news'를 통해 사용할 수 있습니다. News 레이아웃을 서브 템플릿화하지 않을 것이라고 확신하는 경우, content_for?(:news_content) ? yield(:news_content) : yield를 단순히 yield로 대체할 수 있습니다.

피드백

이 가이드의 품질을 개선하는 데 도움을 주시기를 권장합니다.

오타나 사실적인 오류를 발견하면 기여해주십시오. 시작하려면 문서 기여 섹션을 읽어보세요.

불완전한 내용이나 최신 정보가 아닌 내용을 발견할 수도 있습니다. 주요한 부분에 누락된 문서를 추가해주세요. Edge 가이드에서 이미 문제가 해결되었는지 확인하세요. 스타일과 규칙은 Ruby on Rails 가이드 지침을 확인하세요.

수정할 내용을 발견했지만 직접 수정할 수 없는 경우 이슈를 열어주세요.

마지막으로, Ruby on Rails 문서에 관한 모든 토론은 공식 Ruby on Rails 포럼에서 환영합니다.