Ruby on Rails Patterns and Anti-Patterns 시리즈의 세 번째 기사로 돌아오신 것을 환영합니다. 이전 게시물에서 우리는 Rails 모델과 관련하여 뿐만 아니라 일반적인 패턴과 안티 패턴을 다루었습니다. 이 게시물에서 Wear는 Rails 보기와 관련된 몇 가지 패턴 및 안티 패턴을 살펴봅니다.
Rails 보기는 때때로 완벽하고 빠르게 작동할 수 있으며 다른 때에는 모든 종류의 문제가 있을 수 있습니다. 보기를 처리하는 방법에 대한 자신감을 높이고 싶거나 주제에 대해 더 배우고 싶다면 이 블로그 게시물을 참조하십시오. 너. 바로 들어가 보겠습니다.
아시다시피 Rails 프레임워크는 구성보다 관례를 따릅니다. 그리고 Rails는 MVC(Model-View-Controller) 패턴에 큰 영향을 미치므로 View 코드에도 자연스럽게 적용됩니다. 여기에는 마크업(ERB 또는Slim 파일), JavaScript 및 CSS 파일이 포함됩니다. 언뜻 보기에는 View 레이어가 매우 간단하고 쉽다고 생각할 수 있지만, 요즘에는 View 레이어에 여러 기술이 혼합되어 있다는 점을 기억하세요.
보기에서 JavaScript, HTML 및 CSS를 사용합니다. 이 세 가지는 코드의 혼란과 무질서함을 초래할 수 있습니다. 장기적으로 봤을 때 그다지 의미가 없는 구현으로 이어집니다. 운 좋게도 오늘 우리는 Rails View 레이어와 관련된 몇 가지 일반적인 문제와 솔루션을 살펴볼 것입니다.
역도 보기
이것은 자주 발생하지 않는 실수이지만 발생하면 눈에 거슬리는 실수입니다. 때때로 사람들은 도메인 로직이나 쿼리를 View 내부에 직접 넣는 경향이 있습니다. 이것은 View 레이어가 무거운 리프팅이나 파워리프팅을 하도록 합니다. 흥미로운 점은 Rails가 실제로 이러한 일이 쉽게 발생하도록 허용한다는 것입니다. 이와 관련하여 '안전망'은 없으며 View 레이어에서 원하는 모든 작업을 수행할 수 있습니다.
정의에 따르면 MVC 패턴의 보기 계층에는 프리젠테이션 로직이 포함되어야 합니다. 도메인 논리나 데이터 쿼리로 인해 방해를 받아서는 안 됩니다. Rails에서는 HTML로 평가되는 Ruby 코드를 작성할 수 있는 ERB 파일(Embedded Ruby)을 얻습니다. 색인 페이지에 노래를 나열하는 웹사이트의 예를 고려한다면 보기 로직은 app/views/songs/index.html.erb
.
"파워리프팅"의 의미와 하지 말아야 할 일을 설명하기 위해 다음 예를 살펴보겠습니다.
# app/views/songs/index.html.erb
<div class="songs">
<% Song.where(published: true).order(:title) do |song| %>
<section id="song_<%= song.id %>">
<span><%= song.title %></span>
<span><%= song.description %></span>
<a href="<%= song.download_url %>">Download</a>
</section>
<% end %>
</div>
여기서 큰 안티 패턴은 마크업에서 바로 노래를 가져오는 것입니다. 데이터를 가져오는 책임은 컨트롤러 또는 컨트롤러에서 호출되는 서비스에 위임되어야 합니다. 때때로 사람들이 컨트롤러에서 데이터를 준비하고 나중에 보기에서 더 많은 데이터를 가져오는 것을 봅니다. 이것은 잘못된 디자인이며 데이터베이스에 쿼리를 더 자주 사용하기 때문에 웹사이트가 느려집니다.
대신 해야 할 일은 @songs
를 노출하는 것입니다. 컨트롤러 작업에서 인스턴스 변수를 만들고 다음과 같이 마크업에서 호출합니다.
class SongsController < ApplicationController
...
def index
@songs = Song.all.where(published: true).order(:title)
end
...
end
# app/views/songs/index.html.erb
<div class="songs">
<% @songs.each do |song| %>
<section id="song_<%= song.id %>">
<span><%= song.title %></span>
<span><%= song.description %></span>
<a href="<%= song.download_url %>">Download</a>
</section>
<% end %>
</div>
이러한 예는 완벽하지 않습니다. 컨트롤러 코드를 더 읽기 쉽게 유지하고 SQL 파스타를 피하려면 이전 블로그 게시물을 확인하는 것이 좋습니다. 또한 보기 계층에서 논리를 생략하면 다른 사람들이 이를 기반으로 솔루션을 구축하려고 할 가능성이 높아집니다.
Rails가 제공하는 기능 활용
여기서는 짧게 하겠습니다. 프레임워크로서의 Ruby on Rails는 특히 뷰 내부에 많은 깔끔한 도우미와 함께 제공됩니다. 이 멋진 작은 도우미를 사용하면 View 레이어를 빠르고 쉽게 구축할 수 있습니다. Rails의 초보자는 다음과 같이 ERb 파일에 전체 HTML을 작성하고 싶을 수 있습니다.
# app/views/songs/new.html.erb
<form action="/songs" method="post">
<div class="field">
<label for="song_title">Title</label>
<input type="text" name="song[title]" id="song_title">
</div>
<div class="field">
<label for="song_description">Description</label>
<textarea name="song[description]" id="song_description"></textarea>
</div>
<div class="field">
<label for="song_download_url">Download URL</label>
<textarea name="song[download_url]" id="song_download_url"></textarea>
</div>
<input type="submit" name="commit" value="Create Song">
</form>
이 HTML을 사용하면 아래 스크린샷과 같이 새 노래에 대한 멋진 형식을 얻을 수 있습니다.
그러나 Rails를 사용하면 Rails가 바로 뒤에 있기 때문에 그럴 필요가 없고 일반 HTML을 작성해서는 안 됩니다. form_with
를 사용할 수 있습니다. HTML을 생성하는 뷰 도우미. form_with
Rails 5.1에서 도입되었으며 form_tag
를 대체하기 위해 존재합니다. 및 form_for
그것은 누군가에게 친숙할 수 있습니다. form_with
추가 코드를 작성하지 않아도 됩니다.
<%= form_with(model: song, local: true) do |form| %>
<div class="field">
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div class="field">
<%= form.label :description %>
<%= form.text_area :description %>
</div>
<div class="field">
<%= form.label :download_url do %>
Download URL
<% end %>
<%= form.text_area :download_url %>
</div>
<%= form.submit %>
<% end %>
HTML을 생성하는 것 외에도 form_with
CSRF 공격을 방지하는 인증 토큰도 생성합니다. 따라서 거의 모든 경우에 지정 도우미가 Rails 프레임워크와 잘 작동할 수 있으므로 사용하는 것이 좋습니다. 일반 HTML 양식을 제출하려고 하면 요청과 함께 제출된 유효하지 않은 인증 토큰이 없기 때문에 실패합니다.
form_with
외에 , label
, text_area
및 submit
도우미, Rails와 함께 기본적으로 제공되는 이러한 뷰 도우미가 더 많이 있습니다. 그들은 당신의 삶을 더 쉽게 만들기 위해 존재하며 당신은 그들을 더 잘 알아야 합니다. "올스타" 중 하나는 확실히 link_to
입니다. :
<%= link_to "Songs", songs_path %>
그러면 다음 HTML이 생성됩니다.
<a href="/songs">Songs</a>
이 게시물이 너무 길어서 모든 도우미를 살펴보는 것은 오늘의 주제가 아니기 때문에 각 도우미에 대해 자세히 설명하지 않겠습니다. Rails Action View 도우미 가이드를 살펴보고 웹사이트에 필요한 항목을 선택하는 것이 좋습니다.
보기 코드 재사용 및 구성
완벽한 웹 애플리케이션을 상상해 봅시다. 완벽한 사용 사례에는 if-else 문이 없고 컨트롤러에서 데이터를 가져와 HTML 태그 사이에 넣는 순수한 코드만 있습니다. 그런 종류의 응용 프로그램은 해커톤이나 꿈에 있을 수 있지만 실제 응용 프로그램에는 보기를 렌더링할 때 많은 분기와 조건이 있습니다.
페이지의 일부를 표시하는 논리가 너무 복잡해지면 어떻게 해야 합니까? 거기에서 어디로 가나요? 일반적인 대답은 아마도 최신 JavaScriptlibrary 또는 프레임워크에 도달하여 복잡한 것을 구축하는 것입니다. 하지만 이 게시물은 Rails Views에 관한 것이므로 내부에 있는 옵션을 살펴보겠습니다.
애프터마켓(맞춤형) 도우미
노래 아래에 클릭 유도문안(CTA) 버튼을 표시하고 싶다고 가정해 보겠습니다. 그러나 문제가 있습니다. 노래에는 다운로드 URL이 있거나 어떤 이유로든 누락될 수 있습니다. 다음과 유사한 코드를 작성하고 싶을 수 있습니다.
app/views/songs/show.html.erb
...
<div class="song-cta">
<% if @song.download_url %>
<%= link_to "Download", download_url %>
<% else %>
<%= link_to "Subscribe to artists updates",
artist_updates_path(@song.artist) %>
<% end %>
</div>
...
위의 예를 분리된 표현 논리로 보면 나쁘지 않아 보이죠? 그러나 이러한 conditionalrenders가 더 많으면 코드 가독성이 떨어집니다. 또한 특히 조건이 더 많은 경우 어딘가에서 제대로 렌더링되지 않을 가능성이 높아집니다.
이것들과 싸우는 한 가지 방법은 별도의 도우미로 추출하는 것입니다. 운 좋게도 Rails는 사용자 정의 도우미를 쉽게 작성할 수 있는 방법을 제공합니다. app/helpers
에서 SongsHelper
를 만들 수 있습니다. , 이렇게:
module SongsHelper
def song_cta_link
content_tag(:div, class: 'song-cta') do
if @song.download_url
link_to "Download", @song.download_url
else
link_to "Subscribe to artists updates",
artist_updates_path(@song.artist)
end
end
end
end
노래의 쇼 페이지를 열면 여전히 동일한 결과를 얻을 수 있습니다. 그러나 이 예제를 조금 더 개선할 수 있습니다. 위의 예에서는 인스턴스 변수 @song
를 사용했습니다. . @song
이 있는 위치에서 thishelper를 사용하기로 결정한 경우에는 사용하지 못할 수 있습니다. nil
입니다. . 따라서 인스턴스 변수의 형태로 외부 종속성을 차단하기 위해 다음과 같이 도우미에 인수를 전달할 수 있습니다.
module SongsHelper
def song_cta_link(song)
content_tag(:div, class: 'song-cta') do
if song.download_url
link_to "Download", song.download_url
else
link_to "Subscribe to artists updates",
artist_updates_path(song.artist)
end
end
end
end
그런 다음 보기에서 아래와 같이 도우미를 호출할 수 있습니다.
app/views/songs/show.html.erb
...
<%= song_cta_link(@song) %>
...
이를 통해 뷰에서 이전과 동일한 결과를 얻을 수 있습니다. 도우미를 사용할 때 좋은 점은 앞으로 해당 도우미에 대해 회귀가 발생하지 않도록 테스트를 작성할 수 있다는 것입니다. 단점은 전역적으로 정의되며 앱 전체에서 도우미 이름이 고유한지 확인해야 한다는 것입니다.
Rails 커스텀 헬퍼 작성의 열렬한 팬이 아니라면 언제든지 Draper gem으로 View Model 패턴을 선택할 수 있습니다. 웹 앱을 이제 막 시작하는 경우 사용자 지정 도우미를 작성하여 천천히 시작하는 것이 좋습니다. 번거롭다면 다른 솔루션으로 전환하세요.
보기 건조
내가 Rails를 시작할 때 정말 좋아했던 점은 거의 믿을 수 없는 마크업을 쉽게 DRY할 수 있는 기능이었습니다. Rails는 부분을 생성할 수 있는 기능을 제공합니다. 즉, 어디서나 포함할 수 있는 재사용 가능한 코드 조각입니다. 예를 들어 여러 위치에서 노래를 렌더링하고 여러 파일에 동일한 코드가 있는 경우 노래를 부분적으로 만드는 것이 좋습니다.
아래와 같이 노래를 표시한다고 가정해 보겠습니다.
# app/views/songs/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= @song.title %>
</p>
<p>
<strong>Description:</strong>
<%= @song.description %>
</p>
<%= song_cta_link %>
<%= link_to 'Edit', edit_song_path(@song) %> |
<%= link_to 'Back', songs_path %>
그러나 동일한 마크업을 사용하여 다른 페이지에도 표시하려고 합니다. 그런 다음 app/views/songs/_song.html.erb
와 같이 밑줄 접두사가 있는 새 파일을 만들 수 있습니다. .
# app/views/songs/_song.html.erb
<p>
<strong>Title:</strong>
<%= @song.title %>
</p>
<p>
<strong>Description:</strong>
<%= @song.description %>
</p>
<%= song_cta_link(@song) %>
그런 다음 부분적으로 노래를 포함하려는 곳에서 다음을 수행하면 됩니다.
...
<%= render "song" %>
...
Rails는 _song
부분적으로 존재하며 렌더링합니다. 맞춤 도우미가 있는 예와 유사하게 인스턴스 변수 @song
를 제거하는 것이 가장 좋습니다. 부분적으로.
# app/views/songs/_song.html.erb
<p>
<strong>Title:</strong>
<%= song.title %>
</p>
<p>
<strong>Description:</strong>
<%= song.description %>
</p>
<%= song_cta_link(song) %>
그런 다음, 우리는 노래 변수를 부분에 전달하여 더 많이 재사용하고 다른 위치에 포함하기에 적합하도록 해야 합니다.
...
<%= render "song", song: @song %>
...
최종 생각
그것이 이 포스트에 대한 모든 사람들입니다. 요약하자면, 우리는 Rails View 영역에서 볼 수 있는 몇 가지 패턴과 안티 패턴을 살펴보았습니다. 다음은 몇 가지 참고 사항입니다.
- UI에서 복잡한 로직을 피하세요(View가 많은 파워리프팅을 하도록 하지 마십시오)
- View 도우미와 관련하여 Rails가 기본적으로 제공하는 기능에 대해 알아보세요.
- 맞춤형 도우미 및 부분으로 코드 구조화 및 재사용
- 인스턴스 변수에 너무 의존하지 마세요.
다음 포스트에서는 꽤 지저분해질 수 있는 Rails Controller 패턴과 안티 패턴에 대해 다룰 것입니다. 계속 지켜봐 주십시오.
다음 편까지, 건배!
추신 Ruby Magic 게시물이 언론에 공개되는 즉시 읽고 싶다면 Ruby Magic 뉴스레터를 구독하고 게시물을 놓치지 마세요!