1 Action View란 무엇인가?
Action View는 MVC의 V입니다. Action Controller와 Action View는 함께 웹 요청을 처리합니다. Action Controller는 (MVC의) model 계층과 통신하고 데이터를 검색하는 역할을 합니다. Action View는 그 데이터를 사용하여 웹 요청에 대한 응답 본문을 렌더링하는 역할을 담당합니다.
기본적으로 Action View template(간단히 "view"라고도 함)은 Embedded Ruby(ERB)를 사용하여 작성되며, HTML 문서 내에서 Ruby 코드를 사용할 수 있습니다.
Action View는 form, 날짜, 문자열을 위한 HTML 태그를 동적으로 생성하기 위한 많은 helper 메서드를 제공합니다. 또한 필요에 따라 애플리케이션에 사용자 정의 helper를 추가할 수 있습니다.
Action View는 코드를 단순화하기 위해 to_param
과 to_partial_path
같은 Active Model 기능을 사용할 수 있습니다. 하지만 이것이 Action View가 Active Model에 의존한다는 의미는 아닙니다. Action View는 독립적인 패키지이며 어떤 Ruby 라이브러리와도 함께 사용할 수 있습니다.
2 Rails에서 Action View 사용하기
Action View template(일명 "view")은 app/views
디렉토리의 하위 디렉토리에 저장됩니다. 각 controller의 이름과 일치하는 하위 디렉토리가 있습니다. 그 하위 디렉토리 안의 view 파일들은 controller action에 대한 응답으로 특정 view를 렌더링하는 데 사용됩니다.
예를 들어, scaffolding을 사용하여 article
리소스를 생성하면 Rails는 app/views/articles
에 다음 파일들을 생성합니다:
$ bin/rails generate scaffold article
[...]
invoke scaffold_controller
생성 app/controllers/articles_controller.rb
invoke erb
생성 app/views/articles
생성 app/views/articles/index.html.erb
생성 app/views/articles/edit.html.erb
생성 app/views/articles/show.html.erb
생성 app/views/articles/new.html.erb
생성 app/views/articles/_form.html.erb
[...]
파일 이름은 Rails 명명 규칙을 따릅니다. 이들은 연관된 controller action과 이름을 공유합니다. 예를 들어 index.html.erb
, edit.html.erb
등이 있습니다.
이 명명 규칙을 따르면, Rails는 별도로 지정하지 않아도 controller action이 끝날 때 자동으로 매칭되는 view를 찾아 렌더링합니다. 예를 들어, articles_controller.rb
의 index
action은 자동으로 app/views/articles/
디렉토리 안의 index.html.erb
view를 렌더링합니다. 파일의 이름과 위치 모두 중요합니다.
클라이언트에게 반환되는 최종 HTML은 .html.erb
ERB 파일, 이를 감싸는 layout 템플릿, 그리고 ERB 파일이 참조할 수 있는 모든 partial들의 조합으로 구성됩니다. 이 가이드의 나머지 부분에서는 세 가지 구성 요소인 Templates
, Partials
, Layouts
각각에 대한 자세한 내용을 확인할 수 있습니다.
3 Templates
Action View 템플릿은 다양한 형식으로 작성될 수 있습니다. 템플릿 파일이 .erb
확장자를 가지면, embedded Ruby를 사용하여 HTML 응답을 생성합니다. 템플릿이 .jbuilder
확장자를 가지면, Jbuilder gem을 사용하여 JSON 응답을 생성합니다. 그리고 .builder
확장자를 가진 템플릿은 Builder::XmlMarkup
라이브러리를 사용하여 XML 응답을 생성합니다.
Rails는 파일 확장자를 사용하여 여러 템플릿 시스템을 구분합니다. 예를 들어, ERB 템플릿 시스템을 사용하는 HTML 파일은 .html.erb
를 파일 확장자로 가지며, Jbuilder 템플릿 시스템을 사용하는 JSON 파일은 .json.jbuilder
파일 확장자를 가집니다. 다른 라이브러리들도 다른 템플릿 유형과 파일 확장자를 추가할 수 있습니다.
3.1 ERB
ERB template는 <% %>
와 <%= %>
같은 특별한 ERB 태그를 사용하여 정적 HTML 안에 Ruby 코드를 넣는 방법입니다.
Rails가 .html.erb
로 끝나는 ERB 뷰 템플릿을 처리할 때, 내장된 Ruby 코드를 평가하고 ERB 태그를 동적 출력으로 대체합니다. 이 동적 컨텐츠는 정적 HTML 마크업과 결합되어 최종 HTML 응답을 형성합니다.
ERB 템플릿 내에서 Ruby 코드는 <% %>
와 <%= %>
태그를 모두 사용하여 포함될 수 있습니다. <% %>
태그(=
없는)는 조건문이나 반복문과 같이 Ruby 코드를 실행하되 결과를 직접 출력하고 싶지 않을 때 사용됩니다. <%= %>
태그는 출력을 생성하는 Ruby 코드에 사용되며, 이 예시의 person.name
과 같은 모델 속성처럼 해당 출력을 템플릿 내에 렌더링하고 싶을 때 사용됩니다:
<h1>이름</h1>
<% @people.each do |person| %>
이름: <%= person.name %><br>
<% end %>
loop는 일반적인 embedding 태그(<% %>
)를 사용하여 설정되고, name은 출력 embedding 태그(<%= %>
)를 사용하여 삽입됩니다.
ERB 템플릿에서는 print
와 puts
같은 함수들이 view에 렌더링되지 않는다는 점에 주의하세요. 따라서 다음과 같은 코드는 작동하지 않습니다:
<%# 잘못된 예시 %>
Hi, Mr. <% puts "Frodo" %>
위 예제는 ERB 내에서 <%# %>
태그를 사용하여 comments를 추가할 수 있음을 보여줍니다.
leading과 trailing 공백을 제거하기 위해서는 <%
와 %>
대신 <%-
와 -%>
를 사용할 수 있습니다.
3.2 Jbuilder
Jbuilder
는 Rails 팀이 관리하고 Rails의 기본 Gemfile
에 포함되어 있는 gem입니다. 템플릿을 사용하여 JSON 응답을 만드는 데 사용됩니다.
아직 없다면 Gemfile
에 다음을 추가할 수 있습니다:
gem "jbuilder"
.jbuilder
확장자를 가진 템플릿에는 json
이라는 이름의 Jbuilder
객체가 자동으로 사용 가능합니다.
다음은 기본적인 예시입니다:
json.name("Alex")
json.email("alex@example.com")
HTML을 생성합니다:
{
"name": "Alex",
"email": "alex@example.com"
}
Jbuilder 문서에서 더 많은 예제를 확인하실 수 있습니다.
3.3 Builder
Builder 템플릿은 ERB의 좀 더 프로그래밍적인 대안입니다. JBuilder
와 비슷하지만, JSON 대신 XML을 생성하는 데 사용됩니다.
xml
이라는 이름의 XmlMarkup
객체는 .builder
확장자를 가진 템플릿에서 자동으로 사용할 수 있습니다.
다음은 기본적인 예시입니다:
xml.em("강조됨")
xml.em { xml.b("강조 & 굵게") }
xml.a("링크", "href" => "https://rubyonrails.org")
xml.target("name" => "compile", "option" => "fast")
다음과 같은 결과를 만들어낼 것입니다:
<em>강조</em>
<em><b>강조 & 굵게</b></em>
<a href="https://rubyonrails.org">링크</a>
<target option="fast" name="compile" />
블록이 있는 모든 메서드는 블록 내에 중첩된 마크업이 있는 XML 마크업 태그로 처리됩니다. 예를 들면 다음과 같습니다:
xml.div {
xml.h1(@person.name)
xml.p(@person.bio)
}
다음과 같이 출력됩니다:
<div>
<h1>David Heinemeier Hansson</h1>
<p>'79년 겨울, 덴마크 디자인의 산물...</p>
</div>
더 많은 예시는 Builder 문서를 참고하세요.
3.4 Template Compilation
기본적으로 Rails는 각 템플릿을 렌더링하기 위한 메서드로 컴파일합니다. development 환경에서는 템플릿을 수정할 때 Rails가 파일의 수정 시간을 확인하고 다시 컴파일합니다.
또한 페이지의 서로 다른 부분을 별도로 캐시하고 만료시켜야 할 때 사용하는 Fragment Caching이 있습니다. 이에 대해 더 자세히 알아보려면 캐싱 가이드를 참조하세요.
4 Partials
Partial 템플릿 - 보통 그냥 "partials"라고 부름 - 은 뷰 템플릿을 작은 재사용 가능한 조각으로 나누는 방법입니다. Partials를 사용하면 메인 템플릿의 코드 일부를 별도의 작은 파일로 추출하고, 그 파일을 메인 템플릿에서 렌더링할 수 있습니다. 또한 메인 템플릿에서 partial 파일로 데이터를 전달할 수도 있습니다.
몇 가지 예시를 통해 실제로 살펴보겠습니다:
4.1 Partial 렌더링하기
view의 일부로 partial을 렌더링하려면, view 내에서 render
메서드를 사용합니다:
<%= render "product" %>
view 내에서 렌더링할 같은 폴더에서 _product.html.erb
라는 이름의 파일을 찾습니다. partial 파일 이름은 관례적으로 앞에 밑줄 문자로 시작합니다. 이 파일 이름은 일반적인 view와 partial을 구분합니다. 하지만 view 내에서 렌더링을 위해 partial을 참조할 때는 밑줄을 사용하지 않습니다. 다른 디렉토리의 partial을 참조할 때도 마찬가지입니다:
<%= render "application/product" %>
해당 코드는 app/views/application/
폴더 안에 있는 _product.html.erb
파일을 찾아 화면에 보여줄 것입니다.
4.2 파셜을 사용하여 뷰 단순화하기
파셜을 사용하는 한 가지 방법은 메서드처럼 다루는 것입니다. 뷰의 세부 사항을 뷰 밖으로 옮겨서 무슨 일이 일어나는지 더 쉽게 파악할 수 있게 하는 방법입니다. 예를 들어, 다음과 같은 뷰가 있을 수 있습니다:
<%= render "application/ad_banner" %>
<h1>상품</h1>
<p>당사의 우수 제품 중 일부를 소개합니다:</p>
<% @products.each do |product| %>
<%= render partial: "product", locals: { product: product } %>
<% end %>
<%= render "application/footer" %>
여기서 _ad_banner.html.erb
와 _footer.html.erb
partial들은 애플리케이션의 많은 페이지에서 공유되는 content를 포함할 수 있습니다. Products 페이지를 살펴볼 때 이러한 섹션들의 세부 내용을 볼 필요는 없습니다.
위의 예시는 _product.html.erb
partial도 사용합니다. 이 partial은 개별 product를 렌더링하기 위한 세부 사항을 포함하고 있으며 @products
collection의 각 product를 렌더링하는 데 사용됩니다.
4.3 locals
옵션으로 Partial에 데이터 전달하기
Partial을 렌더링할 때, 렌더링하는 view에서 partial로 데이터를 전달할 수 있습니다. 이를 위해 locals:
옵션 해시를 사용합니다. locals:
옵션의 각 키는 partial-local 변수로 사용할 수 있습니다.
<%# app/views/products/show.html.erb %>
<%= render partial: "product", locals: { my_product: @product } %>
<%# app/views/products/_product.html.erb %>
<%= tag.div id: dom_id(my_product) do %>
<h1><%= my_product.name %></h1>
<% end %>
partial-local variable은 특정 partial에서만 로컬로 존재하며 해당 partial 내에서만 사용할 수 있는 변수입니다. 위의 예시에서 my_product
는 partial-local variable입니다. 이는 원본 view에서 partial로 전달될 때 @product
값이 할당되었습니다.
일반적으로는 이 로컬 변수를 단순히 product
로 부르는 것에 주의하세요. 이 예시에서는 인스턴스 변수명과 템플릿명을 구분하기 위해 my_product
를 사용하고 있습니다.
locals
는 해시이므로 필요에 따라 locals: { my_product: @product, my_reviews: @reviews }
처럼 여러 변수를 전달할 수 있습니다.
하지만 템플릿이 locals:
옵션의 일부로 view에 전달되지 않은 변수를 참조하면, 템플릿은 ActionView::Template::Error
를 발생시킬 것입니다.
<%# app/views/products/_product.html.erb %>
<%= tag.div id: dom_id(my_product) do %>
<h1><%= my_product.name %></h1>
<%# => `product_reviews`에 대해 ActionView::Template::Error가 발생합니다 %>
<% product_reviews.each do |review| %>
<%# ... %>
<% end %>
<% end %>
4.4 local_assigns
사용하기
각 partial에는 local_assigns 라고 불리는 메서드를 사용할 수 있습니다. 이 메서드를 사용하여 locals:
옵션을 통해 전달된 키에 접근할 수 있습니다. partial이 :some_key
가 설정되지 않은 채로 렌더링된 경우, partial 내에서 local_assigns[:some_key]
의 값은 nil
이 됩니다.
예를 들어, 아래 예시에서는 locals:
에 product
만 설정되어 있기 때문에 product_reviews
는 nil
입니다:
<%# app/views/products/show.html.erb %>
<%= render partial: "product", locals: { product: @product } %>
<%# app/views/products/_product.html.erb %>
<% local_assigns[:product] # => "#<Product:0x0000000109ec5d10>" %>
<% local_assigns[:product_reviews] # => nil %>
local_assigns
의 활용 사례 중 하나는 선택적으로 local 변수를 전달하고, 해당 local 변수가 설정되어 있는지 여부에 따라 partial 내에서 조건부로 액션을 수행하는 것입니다. 예를 들면:
<% if local_assigns[:redirect] %>
<%= form.hidden_field :redirect, value: true %>
<% end %>
해당 form에 redirect가 local 변수로 할당되어 있는 경우, hidden field로 redirect의 값을 true로 설정합니다.
다음 예시는 Active Storage의 _blob.html.erb
에서 가져왔습니다. 이 예시는 이 코드가 포함된 partial을 렌더링할 때 in_gallery
지역 변수가 설정되어 있는지 여부에 따라 size를 설정합니다.
<%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
갤러리에 있는 경우 800x600으로, 그렇지 않은 경우 1024x768로 크기를 제한하여 이미지 blob을 표시합니다.
4.5 partial
과 locals
옵션이 없는 render
위의 예시에서 render
는 2개의 옵션인 partial
과 locals
를 사용했습니다. 하지만 이것들이 사용해야 할 유일한 옵션이라면 partial
과 locals
키를 생략하고 값만 명시할 수 있습니다.
예를 들어, 다음과 같이 작성하는 대신:
<%= render partial: "product", locals: { product: @product } %>를 사용하여 partial을 제품 객체와 함께 렌더링합니다.
Rails 가이드를 번역하기 위한 원문을 보내주시면 번역하겠습니다. 현재는 "You can write:"라는 문장만 있어서 번역할 전체 내용이 없는 상태입니다.
<%= render "product", product: @product %>
규칙을 바탕으로 다음과 같은 축약형을 사용할 수도 있습니다:
<%= render @product %>
app/views/products/
위치에서 _product.html.erb
라는 이름의 partial을 찾아내고, 해당 partial에 product
라는 이름을 가진 local 변수를 @product
의 값으로 설정하여 전달합니다.
4.6 as
와 object
옵션
기본적으로 템플릿에 전달된 객체들은 템플릿과 동일한 이름을 가진 로컬 변수에 저장됩니다. 예를 들어 다음과 같습니다:
<%= render @product %>
product 모델 인스턴스를 렌더링합니다.
_product.html.erb
partial 내에서 @product
인스턴스 변수를 로컬 변수 product
로 받게 됩니다. 마치 다음과 같이 작성한 것처럼 동작합니다:
<%= render partial: "product", locals: { product: @product } %>
partial을 렌더링하며 locals
해시를 통해 지역 변수를 전달합니다.
object
옵션을 사용하여 다른 이름을 지정할 수 있습니다. 이는 template의 object가 다른 곳(예: 다른 인스턴스 변수나 로컬 변수)에 있을 때 유용합니다.
예를 들어, 다음과 같은 코드 대신:
<%= render partial: "product", locals: { product: @item } %>를 통해 @item 변수를 product로 product partial에 전달할 수 있습니다
you can write:는 다음과 같이 작성할 수 있습니다:
<%= render partial: "product", object: @item %>
partial의 각 렌더링이 partial template에 object
로 명명된 지역 변수를 통해 전달될 object를 가질 수 있습니다.
이는 인스턴스 변수 @item
을 product
라는 이름의 partial 지역 변수에 할당합니다. 기본값인 product
대신 다른 이름으로 지역 변수를 바꾸고 싶다면 어떻게 해야 할까요? 이때는 :as
옵션을 사용할 수 있습니다.
as
옵션을 사용하면 다음과 같이 지역 변수에 다른 이름을 지정할 수 있습니다:
<%= render partial: "product", object: @item, as: "item" %>
:as
옵션을 사용하면 object
에 지정한 변수의 이름을 partial 내에서 다른 이름으로 변경할 수 있습니다.
이것은 다음과 동일합니다
<%= render partial: "product", locals: { item: @item } %>
4.7 Collection Rendering
view가 @products
와 같은 collection을 순회하면서 collection의 각 객체에 대해 partial template을 렌더링하는 것은 흔한 일입니다. 이 패턴은 배열을 받아들이고 배열의 각 요소에 대해 partial을 렌더링하는 단일 메서드로 구현되었습니다.
다음은 모든 product를 렌더링하는 예시입니다:
<% @products.each do |product| %>
<%= render partial: "product", locals: { product: product } %>
<% end %>
단일 라인으로 다시 작성할 수 있습니다:
<%= render partial: "product", collection: @products %>
partial이 collection과 함께 호출되면, partial의 각 인스턴스들은 partial의 이름을 따서 명명된 변수를 통해 렌더링되는 collection의 멤버에 접근할 수 있습니다. 이 경우, partial이 _product.html.erb
이므로 렌더링되는 collection의 멤버를 참조하기 위해 product
를 사용할 수 있습니다.
또한 collection을 렌더링하기 위해 다음과 같은 규칙을 기반으로 한 축약 문법을 사용할 수 있습니다.
<%= render @products %>
위 코드는 각각의 product를 collection으로 렌더링합니다.
위 코드는 @products
가 Product
인스턴스의 컬렉션이라고 가정합니다. Rails는
컬렉션의 모델 이름(이 경우 Product
)을 살펴보고 네이밍 규칙을 사용하여 사용할 partial의
이름을 결정합니다. 실제로 이 축약형을 사용하여 서로 다른 모델의 인스턴스로 구성된
컬렉션도 렌더링할 수 있으며, Rails는 컬렉션의 각 멤버에 맞는 적절한 partial을 선택합니다.
4.8 Spacer Templates
메인 partial 인스턴스들 사이에 렌더링될 두 번째 partial을 :spacer_template
옵션을 사용하여 지정할 수 있습니다:
<%= render partial: @products, spacer_template: "product_ruler" %>
partial들 사이에 추가될 template을 지정하여 collection을 렌더링합니다.
Rails는 각 쌍의 _product.html.erb
partial 사이에 _product_ruler.html.erb
partial을 렌더링할 것입니다(전달되는 데이터 없이).
4.9 Counter Variables
Rails는 collection으로 호출된 partial 내에서 사용할 수 있는 counter 변수도 제공합니다. 이 변수는 partial의 이름 뒤에 _counter
를 붙인 형태로 명명됩니다. 예를 들어, @products
collection을 렌더링할 때 _product.html.erb
partial에서 product_counter
변수에 접근할 수 있습니다. 이 변수는 해당 partial이 view 내에서 렌더링된 횟수를 나타내며, 첫 번째 렌더링에서는 0
의 값으로 시작합니다.
<%# index.html.erb %>
<%= render partial: "product", collection: @products %>
<%# _product.html.erb %>
<%= product_counter %> # 첫번째 product는 0, 두번째 product는 1...
이는 as:
옵션을 사용하여 local 변수 이름을 변경할 때도 동작합니다.
따라서 as: :item
을 사용하면 counter 변수는 item_counter
가 될 것입니다.
주의: 다음 두 섹션인 Strict Locals와 Local Assigns with Pattern Matching은 partial을 사용하는 더 고급 기능들로, 완전성을 위해 여기에 포함되어 있습니다.
4.10 local_assigns
와 Pattern Matching
local_assigns
는 Hash
이므로, Ruby 3.1의 패턴 매칭 할당 연산자와 호환됩니다:
local_assigns => { product:, **options }
product # => "#<Product:0x0000000109ec5d10>"
options # => {}
파셜-지역 Hash
변수에 :product
이외의 키가 할당되면, 이들은 helper 메서드 호출에 splat할 수 있습니다:
<%# app/views/products/_product.html.erb %>
<% local_assigns => { product:, **options } %>
<%= tag.div id: dom_id(product), **options do %>
<h1><%= product.name %></h1>
<% end %>
<%# app/views/products/show.html.erb %>
<%= render "products/product", product: @product, class: "card" %>
<%# => <div id="product_1" class="card">
# <h1>제품명</h1>
# </div>
%>
Pattern matching 할당 또한 변수 이름 재지정을 지원합니다:
local_assigns => { product: record }
product # => "#<Product:0x0000000109ec5d10>"
record # => "#<Product:0x0000000109ec5d10>"
product == record # => true
fetch
를 사용하여 변수를 조건부로 읽을 수 있으며, locals:
옵션에 키가 없는 경우 기본값으로 대체됩니다.
<%# app/views/products/_product.html.erb %>
<% local_assigns에서 :related_products를 가져오되(기본값은 빈 배열)를 통해 각 related_product를 순회합니다 %>
<%# ... %>
<% end %>
Ruby 3.1의 패턴 매칭 할당과 Hash#with_defaults 호출을 조합하면 간단한 부분적-지역 기본 변수 할당이 가능합니다:
<%# app/views/products/_product.html.erb %>
<% local_assigns.with_defaults(related_products: []) => { product:, related_products: } %>
<%= tag.div id: dom_id(product) do %>
<h1><%= product.name %></h1>
<% related_products.each do |related_product| %>
<%# ... %>
<% end %>
<% end %>
4.11 Strict Locals
Action View partial은 내부적으로 일반 Ruby 메서드로 컴파일됩니다.
Ruby에서는 동적으로 로컬 변수를 생성하는 것이 불가능하기 때문에, partial에 전달되는 모든 locals
조합마다 다른 버전으로 컴파일해야 합니다.
<%# app/views/articles/show.html.erb %>
<%= render partial: "article", layout: "box", locals: { article: @article } %>
<%= render partial: "article", layout: "box", locals: { article: @article, theme: "dark" } %>
위 코드 스니펫은 partial이 두 번 컴파일되도록 하여 더 많은 시간과 메모리를 사용하게 됩니다.
def _render_template_2323231_article_show(buffer, local_assigns, article:)
# ...
end
def _render_template_3243454_article_show(buffer, local_assigns, article:, theme:)
# ...
end
조합의 수가 작을 때는 큰 문제가 되지 않지만, 수가 많아지면 상당한 메모리를 낭비하고 컴파일하는 데 오랜 시간이 걸릴 수 있습니다. 이를 방지하기 위해 strict locals를 사용하여 컴파일된 partial 시그니처를 정의하고, partial의 단일 버전만 컴파일되도록 할 수 있습니다.
<%# locals: (article:, theme: "light") -%>
...
주석은 다음을 의미합니다:
- locals은 article 객체와 theme 문자열 매개변수를 받습니다
- theme의 기본값은 "light"입니다
locals:
시그니처를 사용하여 Ruby method 시그니처와 동일한 구문을 통해 템플릿이 사용할 수 있는 locals
의 수와 종류를 제한하고, 기본값을 설정하는 등의 작업을 할 수 있습니다.
다음은 locals:
시그니처의 예시입니다:
<%# app/views/messages/_message.html.erb %>
<%# locals: (message:) -%>
<%= message %>
위 코드는 message
를 필수적인 local variable로 만듭니다. :message
local variable 인수 없이 partial을 렌더링하면 예외가 발생합니다.
render "messages/message"
# => ActionView::Template::Error: app/views/messages/_message.html.erb에 필요한 지역 변수: :message가 없습니다
기본값이 설정되어 있다면 locals:
에 message
가 전달되지 않았을 때 사용할 수 있습니다.
<%# app/views/messages/_message.html.erb %>
<%# locals: (message: "Hello, world!") -%>
<%= message %>
partial을 :message
local variable 없이 렌더링하면 locals:
시그니처에 설정된 기본값이 사용됩니다.
render "messages/message"
# => "안녕하세요, 세상!"
partial을 local:
서명에 정의되지 않은 로컬 변수와 함께 렌더링하면 예외가 발생합니다:
render "messages/message", unknown_local: "will raise"
# => ActionView::Template::Error: 알 수 없는 local: app/views/messages/_message.html.erb의 :unknown_local
double splat **
연산자를 사용하여 선택적 로컬 변수 인수를 허용할 수 있습니다:
<%# app/views/messages/_message.html.erb %>
<%# locals: (message: "Hello, world!", **attributes) -%>
<%= tag.p(message, **attributes) %>
아니면 locals:
를 빈 ()
로 설정하여 locals
를 완전히 비활성화할 수도 있습니다:
<%# app/views/messages/_message.html.erb %>
<%# locals: () - locals 파라미터가 없음 %>
partial을 어떤 local variable 인자와 함께 렌더링하면 예외가 발생합니다:
render "messages/message", unknown_local: "will raise"
# => ActionView::Template::Error: app/views/messages/_message.html.erb에서 locals를 받아들일 수 없습니다
Action View는 #
접두사가 붙은 주석을 지원하는 모든 템플릿 엔진에서 locals:
시그니처를 처리하며, partial의 어느 라인에서든 시그니처를 읽을 수 있습니다.
주의: 키워드 인자만 지원됩니다. 위치 인자나 블록 인자를 정의하면 렌더링 시점에 Action View Error가 발생합니다.
local_assigns
메서드는 local:
시그니처에 지정된 기본값을 포함하지 않습니다. class
나 if
와 같이 Ruby의 예약어와 동일한 이름을 가진 기본값이 있는 지역 변수에 접근하려면, binding.local_variable_get
을 통해 값에 접근할 수 있습니다.
<%# locals: (class: "message") %>
<div class="<%= binding.local_variable_get(:class) %>">...</div>
5 Layouts
Layout은 Rails controller action의 결과를 공통된 view template으로 렌더링하는 데 사용할 수 있습니다. Rails 애플리케이션은 페이지를 렌더링할 수 있는 여러 layout을 가질 수 있습니다.
예를 들어, 애플리케이션은 로그인한 사용자를 위한 layout과 사이트의 마케팅 부분을 위한 또 다른 layout을 가질 수 있습니다. 로그인한 사용자의 layout은 여러 controller action에서 표시되어야 하는 최상위 내비게이션을 포함할 수 있습니다. SaaS 앱의 영업 layout은 "Pricing"과 "Contact Us" 페이지와 같은 항목에 대한 최상위 내비게이션을 포함할 수 있습니다. 서로 다른 layout은 서로 다른 헤더와 푸터 콘텐츠를 가질 수 있습니다.
현재 controller action의 layout을 찾기 위해, Rails는 먼저 app/views/layouts
에서 controller와 동일한 기본 이름을 가진 파일을 찾습니다. 예를 들어, ProductsController
클래스의 action을 렌더링할 때는 app/views/layouts/products.html.erb
를 사용합니다.
controller별 layout이 존재하지 않는 경우 Rails는 app/views/layouts/application.html.erb
를 사용합니다.
다음은 application.html.erb
파일의 간단한 layout 예시입니다:
<!DOCTYPE html>
<html>
<head>
<title><%= "Your Rails App" %></title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<nav>
<ul>
<li><%= link_to "홈", root_path %></li>
<li><%= link_to "제품", products_path %></li>
<!-- 추가 네비게이션 링크는 여기에 -->
</ul>
</nav>
<%= yield %>
<footer>
<p>© <%= Date.current.year %> 회사명</p>
</footer>
위 예시 레이아웃에서, view content는 <%= yield %>
가 있는 자리에 렌더링되며, 동일한 <head>
, <nav>
, <footer>
content로 둘러싸이게 됩니다.
Rails는 특정 layout을 개별 controller와 action에 할당하는 더 많은 방법을 제공합니다. Rails의 Layouts와 Rendering 가이드에서 layouts에 대해 더 자세히 알아볼 수 있습니다.
5.1 Partial Layouts
Partial은 자체적으로 layout을 적용할 수 있습니다. 이러한 layout은 controller action에 적용되는 layout과는 다르지만, 비슷한 방식으로 동작합니다.
페이지에 표시되는 article을 표시 목적으로 div
로 감싸야 하는 경우를 생각해봅시다. 먼저, 새로운 Article
을 생성합니다:
Article.create(body: "Partial Layout이 멋져요!")
show
템플릿에서는 _article
partial을 box
layout으로 감싸서 렌더링합니다:
<%# app/views/articles/show.html.erb %>
<%= render 특정 'article' partial을, 'box' layout과 함께 렌더링하고, { article: @article } locals를 전달합니다 %>
box
레이아웃은 _article
partial을 단순히 div
로 감쌉니다:
<%# app/views/articles/_box.html.erb %>
<div class="box">
<%= yield %>
</div>
이 경우 _box.html.erb
내에서 사용되지는 않지만, partial layout은 render
호출 시 전달된 로컬 변수 article
에 접근할 수 있습니다.
애플리케이션 전체 layout과는 달리, partial layout의 이름에는 여전히 언더스코어 prefix가 있습니다.
또한 yield
를 호출하는 대신 partial layout 내에서 코드 블록을 렌더링할 수 있습니다. 예를 들어, _article
partial이 없다면 다음과 같이 할 수 있습니다:
<%# app/views/articles/show.html.erb %>
<%= render(layout: 'box', locals: { article: @article }) do %>
<div>
<p><%= article.body %></p>
</div>
<% end %>
위의 동일한 _box
partial을 사용한다고 가정하면, 이는 이전 예제와 동일한 결과를 출력할 것입니다.
5.2 Collection과 Partial 레이아웃
Collection을 렌더링할 때 :layout
옵션을 함께 사용할 수 있습니다.
<%= render partial: "article", collection: @articles, layout: "special_layout" %>
partial을 collection으로 렌더링하고 각 요소에 layout을 적용합니다.
레이아웃은 컬렉션의 각 항목에 대한 partial과 함께 렌더링됩니다. 위 예제의 article
과 article_counter
와 같은 현재 객체와 object_counter 변수는 partial 내에서와 동일한 방식으로 레이아웃에서도 사용할 수 있습니다.
6 Helpers
Rails는 Action View에서 사용할 수 있는 많은 helper 메서드를 제공합니다. 다음과 같은 작업을 위한 메서드들을 포함합니다:
- 날짜, 문자열, 숫자 포맷팅
- 이미지, 비디오, 스타일시트 등에 대한 HTML 링크 생성
- 콘텐츠 sanitizing
- 폼 생성
- 콘텐츠 현지화
helper에 대해 더 자세히 알아보려면 Action View Helpers Guide와 Action View Form Helpers Guide를 참조하세요.
7 Localized Views
Action View는 현재 locale에 따라 다른 템플릿을 렌더링할 수 있는 기능을 가지고 있습니다.
예를 들어, show
액션이 있는 ArticlesController
가 있다고 가정해봅시다. 기본적으로 이 액션을 호출하면 app/views/articles/show.html.erb
를 렌더링합니다. 하지만 I18n.locale = :de
로 설정하면, Action View는 먼저 app/views/articles/show.de.html.erb
템플릿을 렌더링하려고 시도합니다. 현지화된 템플릿이 없는 경우에는 장식되지 않은 버전이 사용됩니다. 이는 모든 경우에 대해 현지화된 뷰를 제공할 필요는 없지만, 사용 가능한 경우 현지화된 뷰가 선호되고 사용된다는 것을 의미합니다.
public 디렉토리의 rescue 파일을 현지화하는 데도 동일한 기술을 사용할 수 있습니다. 예를 들어, I18n.locale = :de
를 설정하고 public/500.de.html
과 public/404.de.html
을 생성하면 현지화된 rescue 페이지를 가질 수 있습니다.
자세한 내용은 Rails Internationalization (I18n) API documentation을 참조하세요.