이 가이드는 사용 가능한 모든 form helper들의 완전한 목록이 되는 것을 의도하지 않습니다. form helper들과 그들의 인자들의 전체 목록은 Rails API 문서를 참조해 주세요.
1 기본 폼 다루기
주요 form helper는 form_with
입니다.
<%= form_with do |form| %>
Form 내용
<% end %>
인자 없이 호출되면 method
속성값이 post
로 설정되고 action
속성값이 현재 페이지로 설정된 HTML <form>
태그를 생성합니다. 예를 들어, 현재 페이지가 /home
의 홈 페이지라고 가정하면, 생성되는 HTML은 다음과 같습니다:
<form action="/home" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="Lz6ILqUEs2CGdDa-oz38TqcqQORavGnbGkG0CQA8zc8peOps-K7sHgFSTPSkBx89pQxh3p5zPIkjoOTiA_UWbQ" autocomplete="off">
폼 내용
</form>
폼에 type이 hidden
인 input
요소가 포함되어 있는 것을 볼 수 있습니다. GET이 아닌 폼 제출에서는 이 authenticity_token
hidden input이 필요합니다.
이 토큰은 Rails의 보안 기능으로 cross-site request forgery (CSRF) 공격을 방지하는데 사용되며, form helper는 GET이 아닌 모든 폼에 대해 이를 자동으로 생성합니다(보안 기능이 활성화되어 있다고 가정할 때). 이에 대한 자세한 내용은 Securing Rails Applications 가이드에서 확인할 수 있습니다.
1.1 기본적인 검색 폼
웹에서 가장 기본적인 폼 중 하나는 검색 폼입니다. 이 폼은 다음을 포함합니다:
- "GET" method를 가진 form 요소,
- input을 위한 label,
- text input 요소, 그리고
- submit 요소.
다음은 form_with
를 사용하여 검색 폼을 만드는 방법입니다:
<%= form_with url: "/search", method: :get do |form| %>
<%= form.label :query, "검색어:" %>
<%= form.search_field :query %>
<%= form.submit "검색" %>
<% end %>
이는 다음과 같은 HTML을 생성할 것입니다:
<form action="/search" accept-charset="UTF-8" method="get">
<label for="query">검색어:</label>
<input type="search" name="query" id="query">
<input type="submit" name="commit" value="검색" data-disable-with="검색">
</form>
검색 폼에서 form_with
의 url
옵션을 사용하고 있음을 주목하세요. url: "/search"
를 설정하면 폼의 action 값이 기본 현재 페이지 경로에서 action="/search"
로 변경됩니다.
일반적으로, form_with
에 url: my_path
를 전달하면 폼이 요청을 보낼 위치를 지정합니다. 다른 방법으로는 아래에서 배우게 될 Active Model 객체를 폼에 전달하는 것입니다. URL 헬퍼를 사용할 수도 있습니다.
위의 검색 폼 예시는 또한 form builder 객체를 보여줍니다. 다음 섹션에서 form builder 객체가 제공하는 많은 헬퍼들(form.label
과 form.text_field
와 같은)에 대해 배우게 될 것입니다.
모든 폼 input
요소에 대해, 이름으로부터 id
속성이 생성됩니다(위 예시에서는 "query"
). 이러한 ID들은 CSS 스타일링이나 JavaScript로 폼 컨트롤을 조작하는데 매우 유용할 수 있습니다.
검색 폼에는 "GET" 메소드를 사용하세요. 일반적으로, Rails 규칙은 컨트롤러 액션에 적절한 HTTP 동사를 사용하는 것을 권장합니다. 검색에 "GET"을 사용하면 사용자가 특정 검색을 북마크할 수 있습니다.
1.2 폼 요소 생성을 위한 Helpers
form_with
가 생성하는 form builder 객체는 텍스트 필드, 체크박스, 라디오 버튼과 같은 일반적인 폼 요소들을 생성하기 위한 많은 helper 메서드를 제공합니다.
이러한 메서드들의 첫 번째 인자는 항상 input의 이름입니다. 이것을 기억하는 것이 중요한데, 폼이 제출될 때 그 이름이 params
해시 안의 폼 데이터와 함께 controller로 전달되기 때문입니다. 이 이름은 해당 필드에 사용자가 입력한 값을 위한 params
의 키가 될 것입니다.
예를 들어, 폼에 <%= form.text_field :query %>
가 포함되어 있다면, controller에서 params[:query]
를 통해 이 필드의 값을 얻을 수 있습니다.
input에 이름을 지정할 때, Rails는 배열이나 해시와 같은 비스칼라 값을 가진 파라미터를 제출할 수 있게 해주는 특정 규칙을 사용하며, 이는 params
에서도 접근 가능합니다. 이에 대해 더 자세히 알아보려면 이 가이드의 Form Input 명명 규칙과 Params 해시 섹션을 참조하세요. 이러한 helper들의 정확한 사용법에 대해서는 API 문서를 참조하세요.
1.2.1 Checkboxes
Checkbox는 단일 값을 선택하거나 선택 해제할 수 있는 폼 컨트롤입니다. Checkbox 그룹은 일반적으로 사용자가 그룹에서 하나 이상의 옵션을 선택할 수 있도록 하는 데 사용됩니다.
다음은 폼에서 세 개의 체크박스를 사용하는 예시입니다:
<%= form.checkbox :biography %>
<%= form.label :biography, "전기" %>
<%= form.checkbox :romance %>
<%= form.label :romance, "로맨스" %>
<%= form.checkbox :mystery %>
<%= form.label :mystery, "미스터리" %>
위 코드는 다음과 같이 생성될 것입니다:
<input name="biography" type="hidden" value="0" autocomplete="off"><input type="checkbox" value="1" name="biography" id="biography">
<label for="biography">전기</label>
<input name="romance" type="hidden" value="0" autocomplete="off"><input type="checkbox" value="1" name="romance" id="romance">
<label for="romance">로맨스</label>
<input name="mystery" type="hidden" value="0" autocomplete="off"><input type="checkbox" value="1" name="mystery" id="mystery">
<label for="mystery">미스터리</label>
checkbox
의 첫 번째 매개변수는 params
hash에서 찾을 수 있는 input의 이름입니다. 만약 사용자가 "Biography" checkbox만 체크했다면, params
hash는 다음과 같이 포함될 것입니다:
{
"biography" => "1",
"romance" => "0",
"mystery" => "0"
}
사용자가 체크박스를 선택했는지 확인하기 위해 params[:biography]
를 사용할 수 있습니다.
체크박스의 값(params
에 나타날 값)은 선택적으로 checked_value
와 unchecked_value
매개변수를 사용하여 지정할 수 있습니다. 자세한 내용은 API 문서를 참조하세요.
또한 collection_checkboxes
도 있는데, 이는 Collection 관련 Helpers 섹션에서 자세히 알아볼 수 있습니다.
1.2.2 Radio Buttons
Radio button은 사용자가 선택지 목록에서 한 번에 하나의 옵션만 선택할 수 있게 하는 form 컨트롤입니다.
예를 들어, 좋아하는 아이스크림 맛을 선택하는 radio button:
<%= form.radio_button :flavor, "chocolate_chip" %>
<%= form.label :flavor_chocolate_chip, "초콜릿 칩" %>
<%= form.radio_button :flavor, "vanilla" %>
<%= form.label :flavor_vanilla, "바닐라" %>
<%= form.radio_button :flavor, "hazelnut" %>
<%= form.label :flavor_hazelnut, "헤이즐넛" %>
위 코드는 다음과 같은 HTML을 생성할 것입니다:
<input type="radio" value="chocolate_chip" name="flavor" id="flavor_chocolate_chip">
<label for="flavor_chocolate_chip">초콜릿 칩</label>
<input type="radio" value="vanilla" name="flavor" id="flavor_vanilla">
<label for="flavor_vanilla">바닐라</label>
<input type="radio" value="hazelnut" name="flavor" id="flavor_hazelnut">
<label for="flavor_hazelnut">헤이즐넛</label>
radio_button
의 두 번째 인수는 input의 value입니다. 이러한 radio button들이 같은 이름(flavor
)을 공유하기 때문에, 사용자는 그 중 하나만 선택할 수 있으며, params[:flavor]
에는 "chocolate_chip"
, "vanilla"
, 또는 hazelnut
중 하나가 포함됩니다.
checkbox와 radio button에는 항상 label을 사용하세요. label은 for
속성을 사용하여 텍스트를 특정 옵션과 연결하고, 클릭 가능한 영역을 확장하여 사용자가 input을 더 쉽게 클릭할 수 있게 합니다.
1.3 관심있는 다른 Helpers
text, email, password, date, time을 포함한 다른 많은 form 컨트롤들이 있습니다. 아래 예제들은 더 많은 helper들과 그들이 생성하는 HTML을 보여줍니다.
날짜와 시간 관련 helper들:
<%= form.date_field :born_on %> <!-- 생년월일을 위한 날짜 필드 -->
<%= form.time_field :started_at %> <!-- 시작 시간을 위한 시간 필드 -->
<%= form.datetime_local_field :graduation_day %> <!-- 졸업일을 위한 날짜/시간 필드 -->
<%= form.month_field :birthday_month %> <!-- 생일 달을 위한 월 필드 -->
<%= form.week_field :birthday_week %> <!-- 생일 주를 위한 주 필드 -->
번역할 원문을 제공해주세요.
<input type="date" name="born_on" id="born_on">
<input type="time" name="started_at" id="started_at">
<input type="datetime-local" name="graduation_day" id="graduation_day">
<input type="month" name="birthday_month" id="birthday_month">
<input type="week" name="birthday_week" id="birthday_week">
특별한 포매팅 관련 Helper들:
<%= form.password_field :password %>
<%= form.email_field :address %>
<%= form.telephone_field :phone %>
<%= form.url_field :homepage %>
죄송하지만 번역할 텍스트가 제공되지 않았네요. 번역을 원하시는 Rails 가이드 문서를 공유해 주시면 요청하신 대로 기술 용어는 영어로 유지하고 마크다운 문법과 링크를 그대로 유지하여 한국어로 번역해드리도록 하겠습니다.
<input type="password" name="password" id="password">
<input type="email" name="address" id="address">
<input type="tel" name="phone" id="phone">
<input type="url" name="homepage" id="homepage">
기타 자주 사용되는 헬퍼:
<%= form.textarea :message, size: "70x5" %>
<%= form.hidden_field :parent_id, value: "foo" %>
<%= form.number_field :price, in: 1.0..20.0, step: 0.5 %>
<%= form.range_field :discount, in: 1..100 %>
<%= form.search_field :name %>
<%= form.color_field :favorite_color %>
주의: 문서를 번역할 수 있도록 원문을 제공해주세요.
인용된 텍스트나 번역할 Rails 가이드 문서가 보이지 않습니다. 번역을 위해서는 원문이 필요합니다. 원문을 공유해주시면 요청하신대로:
- 기술 용어는 영어로 유지
- 마크다운 문법과 링크 유지
- 특수문자 그대로 유지
- 번역 외 다른 설명 없이
번역해드리도록 하겠습니다.
<textarea name="message" id="message" cols="70" rows="5"></textarea>
<input value="foo" autocomplete="off" type="hidden" name="parent_id" id="parent_id">
<input step="0.5" min="1.0" max="20.0" type="number" name="price" id="price">
<input min="1" max="100" type="range" name="discount" id="discount">
<input type="search" name="name" id="name">
<input value="#000000" type="color" name="favorite_color" id="favorite_color">
Hidden input은 사용자에게는 보이지 않지만 일반 텍스트 input처럼 데이터를 보유합니다. 내부의 값은 JavaScript를 통해 변경할 수 있습니다.
password input 필드를 사용하는 경우, 해당 파라미터들이 로깅되지 않도록 애플리케이션을 설정하고 싶을 수 있습니다. Securing Rails Applications 가이드에서 관련 방법을 확인할 수 있습니다.
2 Model 객체로 Form 생성하기
2.1 Form을 Object에 바인딩하기
form_with
helper에는 form builder object를 model object에 바인딩할 수 있게 해주는 :model
옵션이 있습니다. 이는 form이 해당 model object의 scope를 가지게 되며, form의 필드들이 그 model object의 값들로 채워진다는 것을 의미합니다.
예를 들어, @book
model object가 있다고 가정해봅시다:
@book = Book.find(42)
# => #<Book id: 42, title: "Walden", author: "Henry David Thoreau">
그리고 새로운 book을 생성하는 다음의 form:
<%= form_with model: @book do |form| %>
HTML이 다음과 같이 생성됩니다:
<form action="/books" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="ChwHeyegcpAFDdBvXvDuvbfW7yCA3e8gvhyieai7DhG28C3akh-dyuv-IBittsjPrIjETlQQvQJ91T77QQ8xWA" autocomplete="off">
<div>
<label for="book_title">제목</label>
<input type="text" name="book[title]" id="book_title">
</div>
<div>
<label for="book_author">저자</label>
<input type="text" name="book[author]" id="book_author">
</div>
<input type="submit" name="commit" value="책 생성" data-disable-with="책 생성">
</form>
form_with
를 모델 객체와 함께 사용할 때 주목해야 할 중요한 사항들:
- form의
action
은 적절한 값으로 자동 채워집니다.action="/books"
와 같이 말이죠. book을 업데이트하는 경우라면action="/books/42"
가 됩니다. - form 필드 이름은
book[...]
으로 스코프가 지정됩니다. 이는params[:book]
이 이러한 모든 필드의 값을 포함하는 해시가 된다는 것을 의미합니다. 입력 이름의 중요성에 대해서는 이 가이드의 Form Input Naming Conventions and Params Hash 챕터에서 자세히 읽어볼 수 있습니다. - submit 버튼에는 자동으로 적절한 텍스트 값이 부여됩니다. 이 경우에는 "Create Book"입니다.
일반적으로 form 입력은 모델 속성을 반영합니다. 하지만 반드시 그럴 필요는 없습니다. 다른 정보가 필요한 경우 form에 필드를 포함시키고 params[:book][:my_non_attribute_input]
을 통해 접근할 수 있습니다.
2.1.1 Composite Primary Key Forms
composite primary key를 가진 모델이 있는 경우, form 빌딩 문법은 동일하지만 출력이 약간 다릅니다.
예를 들어, [:author_id, :id]
와 같은 복합 키를 가진 @book
모델 객체를 업데이트하는 경우:
@book = Book.find([2, 25])
# => #<Book id: 25, title: "Some book", author_id: 2>
다음 form입니다:
<%= form_with model: @book do |form| %>
<%= form.text_field :title %>
<%= form.submit %>
<% end %>
HTML 출력은 다음과 같이 생성됩니다:
<form action="/books/2_25" method="post" accept-charset="UTF-8" >
<input name="authenticity_token" type="hidden" value="ChwHeyegcpAFDdBvXvDuvbfW7yCA3e8gvhyieai7DhG28C3akh-dyuv-IBittsjPrIjETlQQvQJ91T77QQ8xWA" />
<input type="text" name="book[title]" id="book_title" value="Some book" />
<input type="submit" name="commit" value="도서 업데이트" data-disable-with="도서 업데이트">
</form>
생성된 URL에는 언더스코어로 구분된 author_id
와 id
가 포함되어 있습니다. 제출되면 컨트롤러는 파라미터에서 각 primary key 값을 추출하여 단일 primary key를 사용할 때와 같이 레코드를 업데이트할 수 있습니다.
2.1.2 fields_for
Helper
fields_for
helper는 동일한 폼 내에서 관련된 모델 객체의 필드를 렌더링하는 데 사용됩니다. 연관된 "내부" 모델은 일반적으로 Active Record 연관을 통해 "메인" 폼 모델과 연결되어 있습니다. 예를 들어, ContactDetail
모델과 연관된 Person
모델이 있다면, 다음과 같이 두 모델의 입력을 모두 포함하는 단일 폼을 만들 수 있습니다:
<%= form_with model: @person do |person_form| %>
<%= person_form.text_field :name %>
<%= fields_for :contact_detail, @person.contact_detail do |contact_detail_form| %>
<%= contact_detail_form.text_field :phone_number %>
<% end %>
<% end %>
위 내용은 다음과 같은 출력을 생성합니다:
<form action="/people" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="..." autocomplete="off" />
<input type="text" name="person[name]" id="person_name" />
<input type="text" name="contact_detail[phone_number]" id="contact_detail_phone_number" />
</form>
fields_for
가 생성하는 객체는 form_with
가 생성하는 것과 같은 form builder입니다. fields_for
헬퍼는 <form>
태그를 렌더링하지 않고 비슷한 binding을 만듭니다. field_for
에 대해 더 자세히 알아보려면 API docs를 참고하세요.
2.2 Record Identification 활용하기
RESTful 리소스를 다룰 때, form_with
의 호출은 record identification을 활용하여 단순화할 수 있습니다. 이는 모델 인스턴스를 전달하면 Rails가 모델 이름, 메서드 및 기타 사항을 파악한다는 것을 의미합니다. 아래의 새 레코드 생성 예시에서, form_with
에 대한 두 호출 모두 동일한 HTML을 생성합니다:
# 긴 방식:
form_with(model: @article, url: articles_path)
# 단축 방식:
form_with(model: @article)
마찬가지로 아래와 같이 기존 article을 편집할 때, form_with
를 호출하는 두 방법 모두 동일한 HTML을 생성합니다:
# 긴 방식:
form_with(model: @article, url: article_path(@article), method: "patch")
# 단축 방식:
form_with(model: @article)
새로운 레코드나 기존 레코드와 관계없이 form_with
의 간단한 호출 방식이 동일하다는 점에 주목하세요. Record identification은 record.persisted?
를 통해 레코드가 새로운 것인지 판단할 만큼 똑똑합니다. 또한 제출할 올바른 경로와 객체 클래스를 기반으로 한 이름도 선택합니다.
이는 routes 파일에서 Article
모델이 resources :articles
로 선언되어 있다고 가정합니다.
만약 singular resource를 가지고 있다면, form_with
와 함께 작동하도록 하기 위해 resource
와 resolve
를 호출해야 합니다:
resource :article
resolve("Article") { [:article] }
resource 라우트를 하나의 Article
레코드에 대해 정의하고 url 도우미를 생성합니다. 그리고 Article
다형성 모델을 [:article]
로 변환하는 커스텀 resolving 룰을 만듭니다.
Resource를 선언하면 여러 가지 부수 효과가 있습니다. Resource 설정과 사용에 대한 자세한 내용은 Rails Routing from the Outside In 가이드를 참조하세요.
모델에서 single-table inheritance를 사용할 때, 부모 클래스만 resource로 선언된 경우 하위 클래스의 record 식별에 의존할 수 없습니다. :url
과 :scope
(모델 이름)를 명시적으로 지정해야 합니다.
2.3 네임스페이스로 작업하기
네임스페이스가 지정된 라우트가 있다면, form_with
는 이를 위한 단축어가 있습니다. 예를 들어, 애플리케이션에 admin
네임스페이스가 있는 경우:
form_with model: [:admin, @article]
이 예제는 article을 admin namespace 아래에서 처리하고 있어 /admin/articles/1
와 같은 URL이 생성됩니다.
위 예시는 admin namespace 내의 Admin::ArticlesController
로 전송되는 form을 생성합니다. 따라서 update의 경우 admin_article_path(@article)
로 전송됩니다.
여러 단계의 namespace가 있는 경우에도 문법은 비슷합니다:
form_with model: [:admin, :management, @article]
Rails의 라우팅 시스템과 관련 규칙에 대한 자세한 내용은 Rails 라우팅 가이드를 참고하세요.
2.4 PATCH, PUT 또는 DELETE 메서드를 사용하는 폼
Rails 프레임워크는 RESTful 디자인을 권장하며, 이는 애플리케이션의 폼들이 GET
과 POST
외에도 method
가 PATCH
, PUT
, 또는 DELETE
인 요청을 만든다는 것을 의미합니다. 하지만 HTML 폼은 폼 제출 시 GET
과 POST
외의 메서드를 지원하지 않습니다.
Rails는 "_method"
라는 이름의 hidden input을 통해 POST 위에서 다른 메서드를 에뮬레이션하는 방식으로 이 제한을 해결합니다. 예를 들면:
form_with(url: search_path, method: "patch")
위의 form은 다음과 같은 HTML 출력을 생성할 것입니다:
<form action="/search" accept-charset="UTF-8" method="post">
<input type="hidden" name="_method" value="patch" autocomplete="off">
<input type="hidden" name="authenticity_token" value="R4quRuXQAq75TyWpSf8AwRyLt-R1uMtPP1dHTTWJE5zbukiaY8poSTXxq3Z7uAjXfPHiKQDsWE1i2_-h0HSktQ" autocomplete="off">
<!-- ... -->
</form>
POSTed 데이터를 파싱할 때, Rails는 특별한 _method
파라미터를 고려하여 요청의 HTTP method가 _method
의 값으로 설정된 것처럼 처리합니다(이 예시에서는 PATCH
).
폼을 렌더링할 때, submission 버튼들은 formmethod:
키워드를 통해 선언된 method
속성을 재정의할 수 있습니다:
<%= form_with url: "/posts/1", method: :patch do |form| %>
<%= form.button "Delete", formmethod: :delete, data: { confirm: "정말 삭제하시겠습니까?" } %>
<%= form.button "Update" %>
<% end %>
<form>
요소와 마찬가지로, 대부분의 브라우저는 GET
과 POST
이외의 formmethod를 통해 선언된 form 메서드를 지원하지 않습니다.
Rails는 formmethod, value, name 속성들을 조합하여 POST를 통해 다른 메서드를 에뮬레이션함으로써 이 문제를 해결합니다:
<form accept-charset="UTF-8" action="/posts/1" method="post">
<input name="_method" type="hidden" value="patch" />
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
<!-- ... -->
<button type="submit" formmethod="post" name="_method" value="delete" data-confirm="정말로 삭제하시겠습니까?">삭제</button>
<button type="submit" name="button">업데이트</button>
</form>
이 경우, "Update" 버튼은 PATCH
로 처리되고 "Delete" 버튼은 DELETE
로 처리됩니다.
3 손쉽게 Select Box 만들기
Select box는 드롭다운 리스트라고도 하며, 사용자가 옵션 목록에서 선택할 수 있게 합니다. Select box를 위한 HTML은 선택할 수 있는 각 옵션마다 하나의 <option>
요소가 필요한 상당한 양의 마크업이 필요합니다. Rails는 이러한 마크업을 생성하는데 도움이 되는 helper 메서드를 제공합니다.
예를 들어, 사용자가 선택할 수 있는 도시 목록이 있다고 가정해봅시다. select
helper를 사용할 수 있습니다:
<%= form.select :city, ["Berlin", "Chicago", "Madrid"] %>
위 코드는 다음과 같은 HTML 출력을 생성할 것입니다:
<select name="city" id="city">
<option value="Berlin">베를린</option>
<option value="Chicago">시카고</option>
<option value="Madrid">마드리드</option>
</select>
그리고 선택값은 일반적으로 params[:city]
로 사용할 수 있습니다.
<option>
값들은 label과 다르게 지정할 수도 있습니다:
<%= form.select :city, [["베를린", "BE"], ["시카고", "CHI"], ["마드리드", "MD"]] %>
입력할 Rails 가이드 문서를 제시해주시면 지시하신대로 번역해드리겠습니다.
<select name="city" id="city">
<option value="BE">베를린</option>
<option value="CHI">시카고</option>
<option value="MD">마드리드</option>
</select>
이렇게 하면, 사용자는 전체 도시 이름을 보게 되지만, params[:city]
는 "BE"
, "CHI"
, 또는 "MD"
중 하나가 됩니다.
마지막으로, :selected
인자를 사용하여 select box의 기본 선택값을 지정할 수 있습니다:
<%= form.select :city, [["베를린", "BE"], ["시카고", "CHI"], ["마드리드", "MD"]], selected: "CHI" %>
죄송하지만 번역할 Rails 가이드 문서가 보이지 않네요. 번역하고자 하는 문서를 공유해 주시면 요청하신 대로 번역해 드리겠습니다.
<select name="city" id="city">
<option value="BE">베를린</option>
<option value="CHI" selected="selected">시카고</option>
<option value="MD">마드리드</option>
</select>
3.1 Select Box를 위한 Option Groups
어떤 경우에는 연관된 옵션들을 그룹으로 묶어서 사용자 경험을 향상시킬 수 있습니다. select
에 Hash
(또는 유사한 Array
)를 전달하여 이를 수행할 수 있습니다:
<%= form.select :city,
{
"Europe" => [ ["Berlin", "BE"], ["Madrid", "MD"] ],
"North America" => [ ["Chicago", "CHI"] ],
},
selected: "CHI" %>
번역해드릴 원문이 누락되어 있습니다. Rails 가이드 문서의 원문을 제공해주시면 요청하신 조건에 맞춰 번역해드리겠습니다.
<select name="city" id="city">
<optgroup label="유럽">
<option value="BE">베를린</option>
<option value="MD">마드리드</option>
</optgroup>
<optgroup label="북아메리카">
<option value="CHI" selected="selected">시카고</option>
</optgroup>
</select>
3.2 Model 객체에 Select Box 바인딩하기
다른 form 컨트롤과 마찬가지로 select box도 model 속성에 바인딩할 수 있습니다. 예를 들어, 다음과 같은 @person
model 객체가 있다고 가정해봅시다:
@person = Person.new(city: "MD")
다음 양식입니다:
<%= form_with model: @person do |form| %>
<%= form.select :city, [["베를린", "BE"], ["시카고", "CHI"], ["마드리드", "MD"]] %>
<% end %>
다음과 같은 select box가 출력됩니다:
<select name="person[city]" id="person_city">
<option value="BE">베를린</option>
<option value="CHI">시카고</option>
<option value="MD" selected="selected">마드리드</option>
</select>
유일한 차이점은 선택된 옵션이 params[:city]
대신 params[:person][:city]
에서 찾을 수 있다는 것입니다.
선택된 옵션이 자동으로 selected="selected"
로 표시되었음을 주목하세요. 이 select 박스가 기존의 @person
레코드에 바인딩되어 있었기 때문에, :selected
인자를 따로 지정할 필요가 없었습니다.
4 Date와 Time Form Helper 사용하기
앞서 언급된 date_field
와 time_field
helper 외에도, Rails는 일반 select 박스를 렌더링하는 대체 날짜와 시간 form helper를 제공합니다. date_select
helper는 연도, 월, 일에 대해 별도의 select 박스를 렌더링합니다.
예를 들어, 다음과 같은 @person
모델 객체가 있다면:
@person = Person.new(birth_date: Date.new(1995, 12, 21))
다음 form입니다:
<%= form_with model: @person do |form| %>
<%= form.date_select :birth_date %>
<% end %>
다음과 같은 select box들을 출력할 것입니다:
<select name="person[birth_date(1i)]" id="person_birth_date_1i">
<option value="1990">1990</option>
<option value="1991">1991</option>
<option value="1992">1992</option>
<option value="1993">1993</option>
<option value="1994">1994</option>
<option value="1995" selected="selected">1995</option>
<option value="1996">1996</option>
<option value="1997">1997</option>
<option value="1998">1998</option>
<option value="1999">1999</option>
<option value="2000">2000</option>
</select>
<select name="person[birth_date(2i)]" id="person_birth_date_2i">
<option value="1">1월</option>
<option value="2">2월</option>
<option value="3">3월</option>
<option value="4">4월</option>
<option value="5">5월</option>
<option value="6">6월</option>
<option value="7">7월</option>
<option value="8">8월</option>
<option value="9">9월</option>
<option value="10">10월</option>
<option value="11">11월</option>
<option value="12" selected="selected">12월</option>
</select>
<select name="person[birth_date(3i)]" id="person_birth_date_3i">
<option value="1">1</option>
...
<option value="21" selected="selected">21</option>
...
<option value="31">31</option>
</select>
양식이 제출될 때 전체 날짜를 포함하는 단일 값이 params
해시에 없다는 점에 주목하세요. 대신 "birth_date(1i)"
와 같은 특별한 이름을 가진 여러 값들이 있을 것입니다. 하지만 Active Model은 모델 속성의 선언된 타입을 기반으로 이러한 값들을 전체 날짜로 조합하는 방법을 알고 있습니다. 따라서 양식이 전체 날짜를 표현하기 위해 단일 필드를 사용한 것처럼 Person.new
나 Person#update
에 params[:person]
를 전달할 수 있습니다.
date_select
헬퍼 외에도, Rails는 시간과 분을 위한 select 박스를 출력하는 time_select
를 제공합니다. 날짜와 시간 select 박스를 모두 결합한 datetime_select
도 있습니다.
4.1 시간 또는 날짜 컴포넌트를 위한 Select Box
Rails는 개별 날짜와 시간 컴포넌트를 렌더링하기 위한 select box helper들도 제공합니다: select_year
, select_month
, select_day
, select_hour
, select_minute
, select_second
. 이러한 helper들은 "독립" 메서드입니다. 즉, form builder 인스턴스에서 호출되지 않습니다. 예를 들어:
<%= select_year 2024, prefix: "party" %>
위의 코드는 다음과 같은 select 박스를 출력합니다:
<select id="party_year" name="party[year]">
<option value="2019">2019</option>
<option value="2020">2020</option>
<option value="2021">2021</option>
<option value="2022">2022</option>
<option value="2023">2023</option>
<option value="2024" selected="selected">2024</option>
<option value="2025">2025</option>
<option value="2026">2026</option>
<option value="2027">2027</option>
<option value="2028">2028</option>
<option value="2029">2029</option>
</select>
이러한 helper들에 대해서, 기본값으로 숫자 대신 Date
또는 Time
객체를 지정할 수 있으며(예를 들어 위의 예시 대신 <%= select_year Date.today, prefix: "party" %>
를 사용), 적절한 날짜와 시간 부분이 추출되어 사용됩니다.
4.2 Time Zone 선택하기
사용자에게 어느 time zone에 있는지 물어볼 필요가 있을 때, 매우 편리한 time_zone_select
helper를 사용할 수 있습니다.
일반적으로 사용자가 선택할 수 있는 time zone 옵션 목록을 제공해야 합니다. 미리 정의된 ActiveSupport::TimeZone
객체의 목록이 없다면 이는 지루한 작업이 될 수 있습니다. time_with_zone
helper는 이를 감싸고 있으며 다음과 같이 사용할 수 있습니다:
<%= form.time_zone_select :time_zone %>
이는 timezone들의 드롭다운 select를 생성합니다.
번역할 원문을 제공해주시지 않았습니다. Rails 가이드 문서를 번역하고 싶으시다면, 번역하고자 하는 원문을 함께 공유해주세요.
<select name="time_zone" id="time_zone">
<option value="International Date Line West">(GMT-12:00) 국제날짜변경선 서쪽</option>
<option value="American Samoa">(GMT-11:00) 아메리칸 사모아</option>
<option value="Midway Island">(GMT-11:00) 미드웨이 섬</option>
<option value="Hawaii">(GMT-10:00) 하와이</option>
<option value="Alaska">(GMT-09:00) 알래스카</option>
...
<option value="Samoa">(GMT+13:00) 사모아</option>
<option value="Tokelau Is.">(GMT+13:00) 토켈라우 제도</option>
</select>
5 Collection 관련 헬퍼
임의의 객체 컬렉션에서 선택지 집합을 생성해야 하는 경우, Rails는 collection_select
, collection_radio_button
, collection_checkboxes
헬퍼를 제공합니다.
이러한 헬퍼들이 유용한 경우를 살펴보기 위해, City
모델과 Person
의 belongs_to :city
연관관계가 있다고 가정해봅시다:
class City < ApplicationRecord
end
class Person < ApplicationRecord
belongs_to :city
end
데이터베이스에 다음과 같은 도시들이 저장되어 있다고 가정해봅시다:
City.order(:name).map { |city| [city.name, city.id] }
# => [["베를린", 1], ["시카고", 3], ["마드리드", 2]]
아래와 같은 form으로 사용자가 도시를 선택할 수 있도록 할 수 있습니다:
<%= form_with model: @person do |form| %>
<%= form.select :city_id, City.order(:name).map { |city| [city.name, city.id] } %>
<% end %>
name 속성으로 정렬된 City model 객체에서 option 태그를 생성합니다. name은 option 태그에 표시되는 텍스트가 되고, id는 value가 됩니다.
위 내용은 다음과 같은 HTML을 생성할 것입니다:
<select name="person[city_id]" id="person_city_id">
<option value="1">베를린</option>
<option value="3">시카고</option>
<option value="2">마드리드</option>
</select>
위 예시는 선택지를 수동으로 생성하는 방법을 보여줍니다. 하지만 Rails는 collection을 명시적으로 반복하지 않고도 선택지를 생성하는 helper를 제공합니다. 이러한 helper들은 collection의 각 객체에 대해 지정된 메서드를 호출하여 각 선택지의 value와 text label을 결정합니다.
belongs_to
association에 대한 필드를 렌더링할 때는 association 이름 자체가 아닌 foreign key의 이름(위 예시에서는 city_id
)을 지정해야 합니다.
5.1 collection_select
헬퍼
select box를 생성하기 위해서는, collection_select
를 사용할 수 있습니다:
<%= form.collection_select :city_id, City.order(:name), :id, :name %>
위 코드는 수동 반복과 동일한 HTML을 출력합니다:
<select name="person[city_id]" id="person_city_id">
<option value="1">Berlin</option>
<option value="3">Chicago</option>
<option value="2">Madrid</option>
</select>
collection_select
의 인수 순서는 select
의 순서와 다릅니다. collection_select
에서는 value 메서드를 먼저(위 예시의 :id
), text label 메서드를 두 번째로(위 예시의 :name
) 지정합니다. 이는 select
헬퍼에서 사용되는 순서와 반대인데, select
에서는 text label이 먼저 오고 value가 두 번째로 옵니다(이전 예시의 ["Berlin", 1]
).
5.2 collection_radio_buttons
헬퍼
라디오 버튼 세트를 생성하기 위해, collection_radio_buttons
를 사용할 수 있습니다:
<%= form.collection_radio_buttons :city_id, City.order(:name), :id, :name %>
Rails 가이드 문서가 보이지 않습니다. 번역할 텍스트를 공유해주시면 번역을 도와드리겠습니다.
여기서는 번역이 필요한 텍스트가 없습니다. HTML 코드이며 도시 이름인 Berlin, Chicago, Madrid는 고유명사이므로 그대로 유지됩니다.
5.3 collection_checkboxes
헬퍼
예를 들어 has_and_belongs_to_many
관계를 지원하기 위해 체크박스 세트를 생성하려면 collection_checkboxes
를 사용할 수 있습니다:
<%= form.collection_checkboxes :interest_ids, Interest.order(:name), :id, :name %>
한국어 번역 결과를 보여드릴 수 있도록 Rails 가이드 문서를 공유해 주시겠어요?
<input type="checkbox" name="person[interest_id][]" value="3" id="person_interest_id_3">
<label for="person_interest_id_3">공학</label>
<input type="checkbox" name="person[interest_id][]" value="4" id="person_interest_id_4">
<label for="person_interest_id_4">수학</label>
<input type="checkbox" name="person[interest_id][]" value="1" id="person_interest_id_1">
<label for="person_interest_id_1">과학</label>
<input type="checkbox" name="person[interest_id][]" value="2" id="person_interest_id_2">
<label for="person_interest_id_2">기술</label>
6 파일 업로드하기
폼에서의 일반적인 작업은 사용자가 파일을 업로드할 수 있도록 하는 것입니다. 이는 아바타 이미지나 처리할 데이터가 담긴 CSV 파일일 수 있습니다. 파일 업로드 필드는 file_field
helper를 사용하여 렌더링할 수 있습니다.
<%= form_with model: @person do |form| %>
<%= form.file_field :csv_file %>
<% end %>
파일 업로드에서 가장 중요하게 기억해야 할 것은 렌더링되는 form의 enctype
속성이 반드시 multipart/form-data
로 설정되어야 한다는 점입니다. 이는 form_with
안에서 file_field
를 사용하면 자동으로 설정됩니다. 수동으로 이 속성을 설정할 수도 있습니다:
<%= form_with url: "/uploads", multipart: true do |form| %>
<%= file_field_tag :csv_file %>
<% end %>
HTML 폼이 다음과 같이 출력됩니다:
<form enctype="multipart/form-data" action="/people" accept-charset="UTF-8" method="post">
<!-- ... -->
</form>
form_with
규칙에 따라, 위의 두 form의 필드명이 서로 다르다는 점에 주의하세요. 첫 번째 form에서는 person[csv_file]
(params[:person][:csv_file]
로 접근 가능)이 되고, 두 번째 form에서는 단순히 csv_file
(params[:csv_file]
로 접근 가능)이 됩니다.
6.1 CSV File Upload 예제
file_field
를 사용할 때, params
hash의 객체는 ActionDispatch::Http::UploadedFile
의 인스턴스입니다. 다음은 업로드된 CSV 파일의 데이터를 애플리케이션의 레코드에 저장하는 방법의 예시입니다:
require "csv"
def upload
uploaded_file = params[:csv_file]
if uploaded_file.present?
csv_data = CSV.parse(uploaded_file.read, headers: true)
csv_data.each do |row|
# CSV 파일의 각 행을 처리합니다
# SomeInvoiceModel.create(amount: row['Amount'], status: row['Status'])
Rails.logger.info row.inspect
#<CSV::Row "id":"po_1KE3FRDSYPMwkcNz9SFKuaYd" "Amount":"96.22" "Created (UTC)":"2022-01-04 02:59" "Arrival Date (UTC)":"2022-01-05 00:00" "Status":"paid">
end
end
# ...
end
파일이 모델과 함께 저장해야 하는 이미지인 경우(예: 사용자의 프로필 사진), 파일을 저장할 위치(디스크, Amazon S3 등), 이미지 파일 크기 조정, 썸네일 생성 등과 같은 여러 작업을 고려해야 합니다. Active Storage는 이러한 작업을 지원하도록 설계되었습니다.
7 Form Builders 커스터마이징
form_with
또는 fields_for
가 생성하는 객체를 Form Builders라고 부릅니다. Form builders를 사용하면 모델 객체와 연결된 폼 요소를 생성할 수 있으며, 이는 ActionView::Helpers::FormBuilder
의 인스턴스입니다. 이 클래스는 애플리케이션에 커스텀 헬퍼를 추가하기 위해 확장될 수 있습니다.
예를 들어, 애플리케이션 전체에서 label
과 함께 text_field
를 표시하고 싶다면, application_helper.rb
에 다음과 같은 헬퍼 메서드를 추가할 수 있습니다:
module ApplicationHelper
def text_field_with_label(form, attribute)
form.label(attribute) + form.text_field(attribute)
end
end
그리고 일반적으로 form에서 다음과 같이 사용하세요:
<%= form_with model: @person do |form| %>
<%= text_field_with_label form, :first_name %>
<% end %>
ActionView::Helpers::FormBuilder
의 하위 클래스를 생성하여 그곳에 helper들을 추가할 수도 있습니다. 다음과 같이 LabellingFormBuilder
하위 클래스를 정의한 후:
class LabellingFormBuilder < ActionView::Helpers::FormBuilder
def text_field(attribute, options = {})
# super는 원래의 text_field 메서드를 호출합니다
label(attribute) + super
end
end
위의 form은 다음과 같이 대체될 수 있습니다:
<%= form_with model: @person, builder: LabellingFormBuilder do |form| %>
<%= form.text_field :first_name %>
<% end %>
자주 재사용하는 경우 자동으로 builder: LabellingFormBuilder
옵션을 적용하는 labeled_form_with
helper를 정의할 수 있습니다.
module ApplicationHelper
def labeled_form_with(**options, &block)
options[:builder] = LabellingFormBuilder
form_with(**options, &block)
end
end
위의 내용은 form_with
대신에 사용될 수 있습니다:
<%= labeled_form_with model: @person do |form| %>
<%= form.text_field :first_name %>
<% end %>
위 세 가지 경우 (text_field_with_label
helper, LabellingFormBuilder
서브클래스, labeled_form_with
helper) 모두 동일한 HTML 출력을 생성합니다:
<form action="/people" accept-charset="UTF-8" method="post">
<!-- ... -->
<label for="person_first_name">이름</label>
<input type="text" name="person[first_name]" id="person_first_name">
</form>
form builder를 사용하면 다음 코드를 수행했을 때 어떤 일이 발생하는지가 결정됩니다:
<%= render partial: f %>
f
가 ActionView::Helpers::FormBuilder
의 인스턴스인 경우, form builder를 부분 템플릿의 객체로 설정하여 form
부분 템플릿을 렌더링합니다. form builder가 LabellingFormBuilder
클래스인 경우에는 대신 labelling_form
부분 템플릿이 렌더링될 것입니다.
LabellingFormBuilder
와 같은 form builder 커스터마이징은 구현 세부사항을 숨기며(위의 간단한 예제에서는 과도해 보일 수 있습니다). FormBuilder
클래스를 확장하거나 헬퍼를 생성하는 등 다양한 커스터마이징 중에서 선택할 때는 폼에서 커스텀 요소를 얼마나 자주 사용하는지를 기준으로 결정하세요.
8 Form Input 명명 규칙과 params
해시
위에서 설명한 모든 폼 헬퍼는 사용자가 다양한 유형의 입력을 할 수 있도록 폼 요소의 HTML을 생성하는데 도움을 줍니다. 컨트롤러에서 사용자 입력 값에 어떻게 접근할 수 있을까요? params
해시가 그 답입니다. 위의 예제에서 이미 params
해시를 보셨을 것입니다. 이 섹션에서는 폼 입력이 params
해시에서 어떻게 구조화되는지에 대한 명명 규칙을 더 자세히 살펴보겠습니다.
params
해시는 배열과 해시의 배열을 포함할 수 있습니다. 값들은 params
해시의 최상위 레벨에 있거나 다른 해시 안에 중첩될 수 있습니다. 예를 들어, Person 모델의 표준 create
액션에서 params[:person]
은 Person
객체의 모든 속성에 대한 해시가 됩니다.
HTML 폼은 사용자 입력 데이터에 대한 고유한 구조가 없으며, 단지 이름-값 문자열 쌍만을 생성한다는 점에 주의하세요. 애플리케이션에서 보이는 배열과 해시는 Rails가 사용하는 파라미터 명명 규칙의 결과입니다.
params
해시의 필드들은 컨트롤러에서 허용되어야 합니다.
8.1 기본 구조
사용자 입력 폼 데이터의 두 가지 기본 구조는 array와 hash입니다.
Hash는 params
에서 값에 접근하는 데 사용되는 구문을 반영합니다. 예를 들어, 폼이 다음을 포함하는 경우:
<input id="person_name" name="person[name]" type="text" value="Henry"/>
params
해시는 다음을 포함할 것입니다
{ "person" => { "name" => "Henry" } }
params[:person][:name]
은 controller에서 제출된 값을 가져올 것입니다.
Hash는 필요한 만큼 여러 단계로 중첩될 수 있습니다. 예를 들어:
<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>
위의 내용은 params
해시를 다음과 같이 만들 것입니다
{ "person" => { "address" => { "city" => "New York" } } }
다른 구조는 Array입니다. 일반적으로 Rails는 중복된 파라미터 이름을 무시하지만, 파라미터 이름이 빈 대괄호 []
로 끝나는 경우에는 파라미터들이 Array에 누적됩니다.
예를 들어, 사용자가 여러 전화번호를 입력할 수 있도록 하려면 form에 다음과 같이 작성할 수 있습니다:
<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>
<input name="person[phone_number][]" type="text"/>
이것은 제출된 전화번호들이 담긴 배열이 params[:person][:phone_number]
에 생성되는 결과를 가져올 것입니다:
{ "person" => { "phone_number" => ["555-0123", "555-0124", "555-0125"] } }
8.2 배열과 해시 결합하기
여러분은 이 두 개념을 혼합하여 사용할 수 있습니다. 해시의 한 요소가 이전 예시처럼 배열일 수 있습니다. params[:person]
해시는 [:phone_number]
라는 키를 가지고 있고 그 값은 배열입니다.
또한 해시의 배열을 가질 수도 있습니다. 예를 들어, 다음과 같은 form 코드 조각을 반복하여 원하는 수만큼의 주소를 생성할 수 있습니다:
<input name="person[addresses][][line1]" type="text"/>
<input name="person[addresses][][line2]" type="text"/>
<input name="person[addresses][][city]" type="text"/>
<input name="person[addresses][][line1]" type="text"/>
<input name="person[addresses][][line2]" type="text"/>
<input name="person[addresses][][city]" type="text"/>
이는 params[:person][:addresses]
가 해시 배열이 되도록 합니다. 배열 내 각 해시는 line1
, line2
, 그리고 city
라는 키들을 가지게 되며, 다음과 같은 형태가 됩니다:
{ "person" =>
{ "addresses" => [
{ "line1" => "1000 Fifth Avenue",
"line2" => "",
"city" => "New York"
},
{ "line1" => "Calle de Ruiz de Alarcón",
"line2" => "",
"city" => "Madrid"
}
]
}
}
해시는 임의로 중첩될 수 있지만, "배열성"은 한 단계만 허용된다는 점을 주의해야 합니다. 배열은 일반적으로 해시로 대체할 수 있습니다. 예를 들어, 모델 객체의 배열 대신 id나 유사한 키를 가진 모델 객체의 해시를 사용할 수 있습니다.
배열 파라미터는 checkbox
헬퍼와 잘 작동하지 않습니다. HTML 명세에 따르면 체크되지 않은 체크박스는 값을 전송하지 않습니다. 하지만 체크박스가 항상 값을 전송하도록 하는 것이 편리할 때가 많습니다. checkbox
헬퍼는 동일한 이름을 가진 보조 hidden 입력을 생성하여 이를 모방합니다. 체크박스가 체크되지 않은 경우 hidden 입력만 전송됩니다. 체크된 경우에는 둘 다 전송되지만 체크박스가 전송한 값이 우선됩니다. 이 hidden 필드를 생략하려면 include_hidden
옵션을 false
로 설정할 수 있습니다. 기본적으로 이 옵션은 true
입니다.
8.3 인덱스가 있는 Hash
사람의 주소 목록마다 필드셋을 가진 form을 렌더링하고자 한다고 가정해봅시다. [fields_for
][] 헬퍼의 :index
옵션이 이를 도와줄 수 있습니다:
<%= form_with model: @person do |person_form| %>
<%= person_form.text_field :name %>
<% @person.addresses.each do |address| %>
<%= person_form.fields_for address, index: address.id do |address_form| %>
<%= address_form.text_field :city %>
<% end %>
<% end %>
<% end %>
사람이 23과 45의 ID를 가진 두 개의 address를 가지고 있다고 가정하면, 위의 form은 다음과 같은 출력을 생성할 것입니다:
<form accept-charset="UTF-8" action="/people/1" method="post">
<input name="_method" type="hidden" value="patch" />
<input id="person_name" name="person[name]" type="text" />
<input id="person_address_23_city" name="person[address][23][city]" type="text" />
<input id="person_address_45_city" name="person[address][45][city]" type="text" />
</form>
params
해시는 다음과 같은 형태를 가지게 됩니다:
{
"person" => {
"name" => "Bob",
"address" => {
"23" => {
"city" => "Paris"
},
"45" => {
"city" => "London"
}
}
}
}
person_form
폼 빌더에서 fields_for
를 호출했기 때문에 모든 폼 입력이 "person"
해시에 매핑됩니다. 또한 index: address.id
를 지정함으로써 각 도시 입력의 name
속성을 person[address][city]
대신 person[address][#{address.id}][city]
로 렌더링했습니다. 이를 통해 params
해시를 처리할 때 어떤 Address
레코드를 수정해야 하는지 알 수 있습니다.
fields_for
index 옵션에 대한 자세한 내용은 API 문서에서 확인할 수 있습니다.
9 복잡한 폼 만들기
애플리케이션이 성장함에 따라 단일 객체 편집을 넘어서는 더 복잡한 폼을 만들어야 할 수 있습니다. 예를 들어, Person
을 생성할 때 사용자가 동일한 폼 내에서 여러 Address
레코드(집, 직장 등)를 생성할 수 있도록 할 수 있습니다. 나중에 Person
레코드를 편집할 때 사용자는 주소를 추가, 제거 또는 업데이트할 수도 있어야 합니다.
9.1 중첩된 속성을 위한 Model 설정
주어진 model(Person
이 경우)의 관련 record를 편집하기 위해, Active Record는 accepts_nested_attributes_for
메소드를 통해 model 수준의 지원을 제공합니다:
class Person < ApplicationRecord
has_many :addresses, inverse_of: :person
accepts_nested_attributes_for :addresses
end
class Address < ApplicationRecord
belongs_to :person
end
Person에 addresses_attributes=
메소드가 생성되어 addresses를 생성, 업데이트, 삭제할 수 있게 됩니다.
9.2 View에서의 중첩된 Form
다음 form은 사용자가 Person
과 그에 연관된 주소를 생성할 수 있게 해줍니다.
<%= form_with model: @person do |form| %>
주소:
<ul>
<%= form.fields_for :addresses do |addresses_form| %>
<li>
<%= addresses_form.label :kind %>
<%= addresses_form.text_field :kind %>
<%= addresses_form.label :street %>
<%= addresses_form.text_field :street %>
...
</li>
<% end %>
</ul>
<% end %>
연관관계가 nested attributes를 허용할 때, fields_for
는 연관관계의 각 요소마다 블록을 한 번씩 렌더링합니다. 특히, person이 주소를 가지고 있지 않다면 아무것도 렌더링하지 않습니다.
일반적인 패턴은 컨트롤러가 하나 이상의 빈 자식 요소를 생성하여 사용자에게 최소한 하나의 필드 세트를 보여주는 것입니다. 아래 예시는 새로운 person 폼에 2개의 주소 필드 세트가 렌더링되는 결과를 보여줍니다.
예를 들어, 위의 form_with
를 다음과 같이 변경하면:
def new
@person = Person.new
2.times { @person.addresses.build }
end
사람의 새로운 레코드를 생성하고 2개의 빈 address 레코드를 관계를 통해 build하는 방법입니다.
다음과 같은 HTML이 출력됩니다:
<form action="/people" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="lWTbg-4_5i4rNe6ygRFowjDfTj7uf-6UPFQnsL7H9U9Fe2GGUho5PuOxfcohgm2Z-By3veuXwcwDIl-MLdwFRg" autocomplete="off">
주소:
<ul>
<li>
<label for="person_addresses_attributes_0_kind">종류</label>
<input type="text" name="person[addresses_attributes][0][kind]" id="person_addresses_attributes_0_kind">
<label for="person_addresses_attributes_0_street">거리</label>
<input type="text" name="person[addresses_attributes][0][street]" id="person_addresses_attributes_0_street">
...
</li>
<li>
<label for="person_addresses_attributes_1_kind">종류</label>
<input type="text" name="person[addresses_attributes][1][kind]" id="person_addresses_attributes_1_kind">
<label for="person_addresses_attributes_1_street">거리</label>
<input type="text" name="person[addresses_attributes][1][street]" id="person_addresses_attributes_1_street">
...
</li>
</ul>
</form>
fields_for
는 form builder를 yield합니다. 파라미터 이름들은 accepts_nested_attributes_for
가 기대하는 것과 같을 것입니다. 예를 들어, 2개의 주소를 가진 사람을 생성할 때, params
에서 제출되는 파라미터는 다음과 같을 것입니다:
{
"person" => {
"name" => "John Doe",
"addresses_attributes" => {
"0" => {
"kind" => "Home",
"street" => "221b Baker Street"
},
"1" => {
"kind" => "Office",
"street" => "31 Spooner Street"
}
}
}
}
:addresses_attributes
해시의 실제 key 값은 중요하지 않습니다. 하지만 각 address마다 다른 정수형 문자열이어야 합니다.
연관된 객체가 이미 저장되어 있는 경우, fields_for
는 저장된 record의 id
가 포함된 hidden input을 자동으로 생성합니다. fields_for
에 include_id: false
를 전달하면 이를 비활성화할 수 있습니다.
{
"person" => {
"name" => "John Doe",
"addresses_attributes" => {
"0" => {
"id" => 1,
"kind" => "Home",
"street" => "221b Baker Street"
},
"1" => {
"id" => "2",
"kind" => "Office",
"street" => "31 Spooner Street"
}
}
}
}
9.3 Controller에서 Parameters 허용하기
일반적으로 model에 전달하기 전에 controller에서 permitted parameters를 선언해야 합니다:
def create
@person = Person.new(person_params)
# ...
end
private
def person_params
params.expect(person: [ :name, addresses_attributes: [[ :id, :kind, :street ]] ])
end
9.4 Associated Objects 제거하기
accepts_nested_attributes_for
에 allow_destroy: true
를 전달하여 사용자가 associated objects를 삭제할 수 있도록 허용할 수 있습니다.
class Person < ApplicationRecord
has_many :addresses
accepts_nested_attributes_for :addresses, allow_destroy: true
end
만약 객체의 attributes 해시가 _destroy
키를 포함하고 있고 그 값이 true
로 평가되는 경우(예: 1
, '1'
, true
또는 'true'
), 그 객체는 삭제됩니다. 이 폼은 사용자가 주소를 삭제할 수 있도록 합니다:
<%= form_with model: @person do |form| %>
주소:
<ul>
<%= form.fields_for :addresses do |addresses_form| %>
<li>
<%= addresses_form.checkbox :_destroy %>
<%= addresses_form.label :kind %>
<%= addresses_form.text_field :kind %>
...
</li>
<% end %>
</ul>
<% end %>
_destroy
필드를 위한 HTML:
<input type="checkbox" value="1" name="person[addresses_attributes][0][_destroy]" id="person_addresses_attributes_0__destroy">
또한 컨트롤러에서 _destroy
필드를 포함하도록 permitted params를 업데이트해야 합니다:
def person_params
params.require(:person).
permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])
end
9.5 빈 레코드 방지하기
사용자가 채우지 않은 필드 집합을 무시하는 것이 종종 유용합니다. accepts_nested_attributes_for
에 :reject_if
proc을 전달하여 이를 제어할 수 있습니다. 이 proc은 폼에서 제출된 각 속성 해시와 함께 호출됩니다. proc이 true
를 반환하면 Active Record는 해당 해시에 대한 관련 객체를 생성하지 않습니다. 아래 예제는 kind
속성이 설정된 경우에만 주소를 생성하려고 시도합니다.
class Person < ApplicationRecord
has_many :addresses
accepts_nested_attributes_for :addresses, reject_if: lambda { |attributes| attributes["kind"].blank? }
end
편의상 :all_blank
심볼을 전달할 수 있는데, 이는 _destroy
값을 제외한 모든 속성이 비어있는 레코드를 거부하는 proc을 생성합니다.
10 외부 리소스를 위한 폼
Rails form 헬퍼는 외부 리소스에 데이터를 전송하기 위한 폼을 만드는 데 사용할 수 있습니다. 외부 API가 리소스에 대한 authenticity_token
을 기대하는 경우, form_with
에 authenticity_token: 'your_external_token'
파라미터로 전달할 수 있습니다:
<%= form_with url: 'http://farfar.away/form', authenticity_token: 'external_token' do %>
폼 내용
<% end %>
때때로 폼에서 사용할 수 있는 필드들이 외부 API에 의해 제한되어 있고 authenticity_token
을 생성하는 것이 바람직하지 않을 수 있습니다. 토큰을 보내지 않으려면 :authenticity_token
옵션에 false
를 전달할 수 있습니다:
<%= form_with url: 'http://farfar.away/form', authenticity_token: false do %>
폼 내용
<% end %>
11 Form Builder 없이 Tag Helper 사용하기
form builder의 컨텍스트 외부에서 form 필드를 렌더링해야 하는 경우, Rails는 일반적인 form 요소를 위한 tag helper를 제공합니다. 예를 들어, checkbox_tag
가 있습니다:
<%= checkbox_tag "accept" %>
죄송하지만 번역할 Rails 가이드 문서가 보이지 않습니다. 번역을 원하시는 문서 내용을 먼저 공유해 주시면 말씀하신 대로:
- 기술 용어는 영어로 유지
- 마크다운 문법과 링크 유지
- 번역 외 다른 설명 없이
- 마크다운 특수문자 그대로 유지
하여 한국어로 번역해드리도록 하겠습니다.
<input type="checkbox" name="accept" id="accept" value="1" />
일반적으로 이러한 헬퍼들은 form builder에 해당하는 이름에 _tag
접미사가 붙은 이름을 가집니다. 전체 목록은 FormTagHelper
API 문서를 참조하세요.
12 form_tag
와 form_for
사용하기
Rails 5.1에서 form_with
가 도입되기 전에는 그 기능이 form_tag
와 form_for
사이에 나뉘어져 있었습니다. 현재는 둘 다 form_with
를 사용하는 것이 권장되지만, 일부 코드베이스에서는 여전히 사용되는 것을 볼 수 있습니다.