๐ก ์๋ณธ๊ธ : https://blog.appsignal.com/2021/02/10/ruby-on-rails-view-patterns-and-anti-patterns.html
Ruby on Rails View Patterns and Anti-patterns | AppSignal Blog
Rails views are sometimes amazing and fast, and at other times, they can have all sorts of issues. If you want to increase confidence over how you handle your views, then this blog post is for you.
blog.appsignal.com
Ruby On Rails ํจํด๊ณผ ์ํฐํจํด ์๋ฆฌ์ฆ์ ์ธ ๋ฒ์งธ ํธ์ ์ค์ ๊ฑธ ํ์ํฉ๋๋ค. ์ด์ ๊ฒ์๋ฌผ์์ ์ฐ๋ฆฌ๋ Rails ๋ชจ๋ธ๊ณผ ๊ด๋ จํ ํจํด๊ณผ ์ํฐํจํด๊ณผ ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ์ ๋ํด์ ๋ค๋ฃจ์์ต๋๋ค. ์ด๋ฒ ๊ธ์์๋ Rail์ view์ ๊ด๋ จ๋ ๋ช ๊ฐ์ง ํจํด๊ณผ ์ํฐํจํด์ ๋ํด์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
Rails View๋ ๋๋ก๋ ์๋ฒฝํ๊ณ ๋น ๋ฅด๊ฒ ๋์ํ๊ธฐ๋ ํ์ง๋ง, ์จ๊ฐ ์ข ๋ฅ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. View๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์์ ๊ฐ์ ๋์ด๊ณ ์ถ๊ฑฐ๋ ์ด ์ฃผ์ ์ ๋ํด ๋ ์์ธํ ์๊ณ ์ถ๋ค๋ฉด ์ด ๋ธ๋ก๊ทธ ๊ฒ์๋ฌผ์ด ๋์์ด ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ผ ๋ฐ๋ก ์์ํ๊ฒ ์ต๋๋ค.
์์๋ค์ํผ Rails ํ๋ ์์ํฌ๋ ๊ตฌ์ฑ์ ๋ํ ๊ท์น์ ๋ฐ๋ฆ ๋๋ค. ๊ทธ๋ฆฌ๊ณ Rails๋ ๋ชจ๋ธ-๋ทฐ-์ปจํธ๋กค๋ฌ(MVC) ํจํด์ ์ค์์ํ๊ธฐ ๋๋ฌธ์ ์ด ๋ชจํ ๋ ์์ฐ์ค๋ฝ๊ฒ View ์ฝ๋์๋ ์ ์ฉ๋ฉ๋๋ค. ์ฌ๊ธฐ์๋ ๋งํฌ์ (ERB ๋๋ Slim ํ์ผ), JavaScript ๋ฐ CSS ํ์ผ์ด ํฌํจ๋ฉ๋๋ค. ์ธ๋ป ๋ณด๊ธฐ์๋ View ๋ ์ด์ด๊ฐ ๋งค์ฐ ๊ฐ๋จํ๊ณ ์ฝ๋ค๊ณ ์๊ฐํ ์ ์์ง๋ง ์์ฆ์๋ View ๋ ์ด์ด์ ์ฌ๋ฌ๊ฐ์ง ๊ธฐ์ ์ด ํผ์ฌ๋์ด ์๋ค๋ ์ ์ ๋ช ์ฌํ์ธ์.
View์๋ JavaScript, HTML, CSS๊ฐ ์ฌ์ฉ๋ฉ๋๋ค. ์ด ์ธ๊ฐ์ง ๊ธฐ์ ์ ํผ๋๊ณผ ์ฝ๋์ ๋ฌด์ง์๋ฅผ ์ด๋ํ์ฌ ํฐ ์๋ฏธ๊ฐ ์๋ ๊ตฌํ์ผ๋ก ์ด์ด์ง์ ์์ต๋๋ค. ๋คํํ๋ ์ค๋์ Rails View ๊ณ์ธต์ ๋ช ๊ฐ์ง ์ผ๋ฐ์ ์ธ ๋ฌธ์ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
Powerlifting Views
์ด ์ค์๋ ์์ฃผ ๋ฐ์ํ์ง ์์ง๋ง,, ๋ฐ์ํ๋ฉด ๋์ ๊ฑฐ์ฌ๋ฆฌ๋ ์ค์์ ๋๋ค. ๋๋๋ก ์ฌ๋๋ค์ ๋๋ฉ์ธ ๋ก์ง์ด๋ ์ฟผ๋ฆฌ๋ฅผ View ๋ด๋ถ์ ์ง์ ๋ฃ๋ ๊ฒฝํฅ์ด ์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด View ๋ ์ด์ด๊ฐ ๋ฌด๊ฑฐ์ด ์์ ์ด๋ ํ์๋ฆฌํํ (Powerlifting)์ ์ํํ๊ฒ ๋ฉ๋๋ค. ํฅ๋ฏธ๋ก์ด ์ ์ Rails๋ฅผ ์ฌ์ฉํ๋ฉด ์ค์ ์ด๋ฐ ์ผ์ด ์ฝ๊ฒ ์ผ์ด๋ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. ์ด์ ๊ด๋ จํ์ฌ ‘์์ ๋ง’์ด ์๊ธฐ ๋๋ฌธ์ View ๋ ์ด์ด๊ฐ ์ํ๋ ๋ฌด์์ด๋ ๋ค ํ ์๊ฐ ์์ต๋๋ค.
MVCํจํด์ View ๋ ์ด์ด์ ์ ์์ ๋ฐ๋ผ ํด๋น ๋ ์ด์ด์๋ ํ๋ฆฌ์ ํ ์ด์ (ํํ)ํ๋ ๋ก์ง์ด ํฌํจ๋์ด์ผ ํฉ๋๋ค. ๋๋ฉ์ธ ๋ก์ง์ด๋ ๋ฐ์ดํฐ ์ฟผ๋ฆฌ์๋ ์ ๊ฒฝ ์ฐ์ง ์์์ผ ํฉ๋๋ค. Rails์๋ ๋ฃจ๋น ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ ERB ํ์ผ(์๋ฒ ๋๋ ๋ฃจ๋น)๊ฐ ์ ๊ณต๋๋ฉฐ, ์ด ํ์ผ์ HTML๋ก ํ๊ฐ๋ฉ๋๋ค. index ํ์ด์ง์ ๋ ธ๋๋ฅผ ๋์ดํ๋ ์น์ฌ์ดํธ์ ์๋ฅผ ์๊ฐํด๋ณด๋ฉด, View ๋ก์ ์ app/views/songs/index.html.erb์ ์์ ๊ฒ์ ๋๋ค.
ํ์๋ฆฌํํ (Powerlifting)์ด ๋ฌด์์ ์๋ฏธํ๊ณ ๋ฌด์์ ํ์ง ๋ง์์ผ ํ๋์ง์ ๋ํด ์ค๋ช ํ๊ธฐ ์ํด ๋ค์ ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค:
# 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>
์ฌ๊ธฐ์ ํฌ๊ฒ ๋ณด์ฌ์ง๋ ์ํฐํจํด์ ๋งํฌ์ ์์ ๋ฐ๋ก Song์ ๊ฐ์ ธ์ค๋ “Song.where(published: true).order(:title)” ์ฝ๋๋ถ๋ถ์ ๋๋ค. ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์ฑ ์์ Controller ๋๋ Service์ ์์ํด์ผ ํ๋๋ฐ ๊ฐ๋ Controller์์ ์ผ๋ถ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํ ํ์ ๋์ค์ View์์ ๋ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒฝ์ฐ๋ฅผ ๋ด ๋๋ค. ์ด๋ ์๋ชป๋ ์ค๊ณ์ด๋ฉฐ ์ฟผ๋ฆฌ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ ๋ถํ๋ฅผ ์ฃผ๊ธฐ ๋๋ฌธ์ ์น์ฌ์ดํธ ์๋๊ฐ ๋๋ ค์ง๋๋ค.
๋ฐ๋ผ์ ์์ ๊ฐ์ ๋ฐฉ์ ๋์ ์ Controller ์ก์ ์์ “@Song”์ ์ธ์คํด์ค ๋ณ์๋ฅผ ๋ ธ์ถํ๊ณ ๋ค์๊ณผ ๊ฐ์ด ๋งํฌ์ ์์ ํธ์ถํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
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>
์ด ์์ ๋ ์๋ฒฝํ์ง๋ ์์ต๋๋ค. Controller ์ฝ๋๋ฅผ ๋ ์ฝ๊ธฐ ์ฝ๊ฒ ๋ง๋ค๊ณ SQL ํ์คํ ์ฝ๋๋ฅผ ํผํ๊ณ ์ถ๋ค๋ฉด ์ด์ ๋ธ๋ก๊ทธ(๋ฒ์ญ๋ณธ)๋ฅผ ํ์ธํ์๊ธฐ ๋ฐ๋๋๋ค. ๋ํ View ๋ ์ด์ด์์ ๋ก์ง์ ์ ์ธํ๋ฉด ๋ค๋ฅธ ์ฌ๋๋ค์ด ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํด๊ฒฐ์ฑ ์ ๊ตฌ์ถํ๋ ค๋ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๋๋ค.
Rails๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ํ์ฉํ๊ธฐ
์ฌ๊ธฐ์๋ ์งง๊ฒ ์ค๋ช ํ๊ฒ ์ต๋๋ค. ํ๋ ์์ํฌ๋ก์ Ruby On Rails๋ ํนํ View ๋ด๋ถ์ ๊น๋ํ Helper๊ฐ ๋ง์ด ํฌํจ๋์ด ์์ต๋๋ค. ์ด ๋ฉ์ง ์์ Helper๋ฅผ ์ฌ์ฉํ๋ฉด 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์ ์ฌ์ฉํ๋ฉด ์๋ ์คํฌ๋ฆฐ์ท๊ณผ ๊ฐ์ด ์๋ก์ด ๋ ธ๋๋ฅผ ๋ฑ๋กํ ์ ์๋ Form์ ์ป์์ ์์ต๋๋ค.
ํ์ง๋ง Rails๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, Rails๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ผ๋ฐ HTML์ ์์ฑํ ํ์๋ ์๊ณ ์์ฑํด์๋ ์๋ฉ๋๋ค. form_with view helper๋ฅผ ์ฌ์ฉํ๋ฉด 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 %>
“form_with”์ HTML์ ์์ฑํ๋ ๊ฒ ์ธ์๋ CSRF ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๋ ์ธ์ฆ ํ ํฐ๋ ์์ฑํฉ๋๋ค. ๋ฐ๋ผ์ ๊ฑฐ์ ๋ชจ๋ ๊ฒฝ์ฐ์ ์ง์ ๋ ํฌํผ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข์ผ๋ฉฐ, ์ด๋ Rails ํ๋ ์์ํฌ์ ์ ์๋ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ผ๋ฐ HTML Form์ ์ ์ถํ๋ ค๊ณ ํ๋ฉด ์์ฒญ๊ณผ ํจ๊ป ์ ์ถ๋ ์ ํจํ ํ ํฐ์ด ์๊ธฐ ๋๋ฌธ์ ์คํจํฉ๋๋ค.
CSRF(Cross Site Request Forgery)๋?
์น ์ดํ๋ฆฌ์ผ์ด์ ์ทจ์ฝ์ ์ค ํ๋๋ก, ์ฌ์ฉ์๊ฐ ์์ ์ ์์ง์๋ ๋ค๋ฅด๊ฒ ๊ณต๊ฒฉ์๊ฐ ์๋ํ ํ์๋ฅผ ํน์ ์น์ฌ์ดํธ์ ์์ฒญํ๊ฒ ๋ง๋๋ ๊ณต๊ฒฉ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธํ ์ํ์์ ํ์ด์ค๋ถ์ ๊ธ์ ์ฐ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๋ ํผ์ฑ ์ฌ์ดํธ์ ์ ์ํ๋ฉด, ๊ณต๊ฒฉ์๊ฐ ๋ง๋ ๊ด๊ณ ์ฑ ๊ธ์ด ํ์ด์ค๋ถ์ ๊ฒ์๋ ์ ์์ต๋๋ค. ์ด๋ฌํ ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด, ์๋ฒ์์๋ ์์์ ๋์ ๊ฐ์ ์์ฑํ์ฌ ์ธ์ ์ ์ ์ฅํ๊ณ , ์์ฒญ๋ง๋ค ์ด ๊ฐ์ ์ ๋ฌํฉ๋๋ค. ์๋ฒ๋ ์์ฒญ์ ๋ฐ์ ๋๋ง๋ค ์ธ์ ์ ์ ์ฅ๋ ๊ฐ๊ณผ ์์ฒญ์ ์ ๋ฌ๋ ๊ฐ์ด ์ผ์นํ๋์ง ๊ฒ์ฆํ์ฌ, ์์กฐ ์์ฒญ์ ๊ฑฐ๋ถํฉ๋๋ค. ์ด๋ฌํ ๋์ ๊ฐ์ Security Token์ด๋ผ๊ณ ๋ถ๋ฆ ๋๋ค.
“form_with”, “label”, “text_area”, “submit” ํฌํผ ์ธ์๋ Rails์๋ View ํฌํผ๊ฐ ๋ ๋ง์ด ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋ฉ๋๋ค. ์ด๋ฌํ View ํฌํผ๋ ์ฌ๋ฌ๋ถ๋ค์ ๊ฐ๋ฐ ์ถ์ ๋ ์ฝ๊ฒ ๋ง๋ค์ด ์ฃผ๊ธฐ ์ํด ์กด์ฌํ๋ฏ๋ก ๋ ์ ์์๋ฌ์ผ ํฉ๋๋ค. ์ฌ์คํ์ค ํ๋๋ “link_to” ์ ๋๋ค.
<%= link_to "Songs", songs_path %>
์๋์ ๊ฐ์ด HTML์ด ์์ฑ๋ฉ๋๋ค.
<a href="/songs">Songs</a>
๊ฐ ํฌํผ์ ๋ํด์ ์์ธํ ์ค๋ช ํ๋ค ๋ณด๋ฉด ์ด๊ธ์ด ๋๋ฌด ๊ธธ์ด์ง๊ณ , ๋ชจ๋ ํฌํผ๋ฅผ ์ดํด๋ณด๋ ๊ฒ์ด ์ค๋์ ์ฃผ์ ๋ ์๋๋ฏ๋ก ์์ธํ ์ค๋ช ํ์ง ์๊ฒ ์ต๋๋ค. ๊ถ๊ธํ์๋ค๋ฉด **Rails Action View helpers guide**๋ฅผ ์ฐธ๊ณ ํด์ ํ์ํ ํฌํผ๋ฅผ ์ ํํ๋๊ฒ์ด ์ข์ต๋๋ค.
View ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ณ ์ ๋ฆฌํ๊ธฐ
์๋ฒฝํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด ์๋ค๊ณ ์์ํด ๋ด ์๋ค. ์๋ฒฝํ ์ฌ์ฉ ์ฌ๋ก์๋ if-else๋ฌธ์ด ์๊ณ Controller์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ HTML ํ๊ทธ ์ฌ์ด์ ๋ฃ๋ ์์ํ ์ฝ๋๋ง ์์ต๋๋ค. ์ด๋ฌํ ์ข ๋ฅ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ํด์ปคํค์ด๋ ๊ฟ์์์๋ ์กด์ฌํ ์ ์์ง๋ง, ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์๋ View๋ฅผ ๋๋๋งํ ๋ ์ฌ๋ฌ๊ฐ์ง ๋ถ๊ธฐ์ ์กฐ๊ฑด์ด ์กด์ฌํฉ๋๋ค.
ํ์ด์ง์ ์ผ๋ถ๋ฅผ ํ์ํ๋ ๋ก์ง์ด ๋๋ฌด ๋ณต์กํด์ง๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น์? ์ผ๋ฐ์ ์ธ ๋๋ต์ ์ต์ JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ณต์กํ ๊ฒ์ ๊ตฌ์ถํ๋ ๊ฒ์ ๋๋ค. ํ์ง๋ง ์ด ๊ฒ์๋ฌผ์ Rails View์ ๊ดํ ๊ฒ์ด๋ฏ๋ก ๊ทธ ์์ ์๋ ์ ํ์ง๋ค์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
After-Market (Custom) Helpers
Song ์๋์ CTA(call-to-action) ๋ฒํผ์ ํ์ํ๊ณ ์ถ๋ค๊ณ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค. ํ์ง๋ง ํ ๊ฐ์ง ์ ์ฑ ์ด ์์ต๋๋ค. Song์ ๋ค์ด๋ก๋ 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>
...
์์ ์์์์ ๊ณ ๋ฆฝ๋ ํ๋ ์ ํ ์ด์ ๋ก์ง์ผ๋ก๋ง ๋ณธ๋ค๋ฉด ๊ทธ๋ ๊ฒ ๋์์ง ์์๊ฑฐ ๊ฐ์ต๋๋ค. ๊ทธ์ตธ? ํ์ง๋ง ์ด๋ฌํ ์กฐ๊ฑด๋ถ ๋๋๋ง์ด ๋ง์์ง๋ค๋ฉด ์ฝ๋์ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๋๋ค. ํนํ ์กฐ๊ฑด์ด ๋ง์ผ๋ฉด ์ด๋๊ฐ์์ ์ ๋๋ก ๋๋๋ง ๋์ง ์์ ๊ฐ๋ฅ์ฑ๋ ๋์์ง๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ํ ๊ฐ์ง ๋ฐฉ๋ฒ์ ๋ณ๋์ ํฌํผ๋ก ์ถ์ถํ๋ ๊ฒ์ ๋๋ค. ๋คํํ๋ Rails๋ ์ฌ์ฉ์ ์ ์ ํฌํผ๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. “app/helpers”์์ ๋ค์๊ณผ ๊ฐ์ด “SongHelper”๋ฅผ ๋ง๋ค์ ์์ต๋๋ค.
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์ show ํ์ด์ง๋ฅผ ์ด์ด๋ ์ฌ์ ํ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ์ป์์๊ฐ ์์ต๋๋ค. ํ์ง๋ง ์ด ์์ ๋ฅผ ์กฐ๊ธ ๋ ๊ฐ์ ํ ์ ์์ต๋๋ค. ์์ ์์ ์์๋ ์ธ์คํด์ค ๋ณ์์ธ “@Song”์ ์ฌ์ฉํ์ต๋๋ค. ๋ ธ๋๊ฐ 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
view์์ ์๋์ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค
app/views/songs/show.html.erb
...
<%= song_cta_link(@song) %>
...
์ด๋ ๊ฒ ํ๋ฉด View์์ ์ด์ ๊ณผ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๋ค. ํฌํผ๋ฅผ ์ฌ์ฉํ ๋์ ์ฅ์ ์ ํฌํผ์ ๋ํ ํ ์คํธ๋ฅผ ์์ฑํด์ ํฅํ ํฌํผ์ ๊ด๋ จ๋ ํ๊ท๊ฐ ๋ฐ์ํ์ง ์๋๋ก ํ ์ ์๋ค๋ ์ ์ ๋๋ค. ๋จ์ ์ ํฌํผ๊ฐ ์ ์ญ์ ์ผ๋ก ์ ์๋๋ฏ๋ก ์ฑ ์ ์ฒด์์ ํฌํผ ์ด๋ฆ์ด ๊ณ ์ ํ์ง ํ์ธํด์ผ ํ๋ค๋ ์ ์ด๋น๋ค.
Rails ์ฌ์ฉ์ ํฌํผ๋ฅผ ์์ฑํ๋ ๊ฒ์ ์ข์ํ์ง ์๋ ๋ค๋ฉด draper gem์ ์ฌ์ฉํด View Modelํจํด์ ์ฌ์ฉํด๋ณผ์๋ ์์ต๋๋ค. ์ฌ๊ธฐ์์ ์์ ๋ง์ View Model ํจํด์ ์ง์ ๋ง๋ค์ ์์ผ๋ฉฐ, ๊ทธ๋ ๊ฒ ๋ณต์กํ์ง ์์ต๋๋ค. ์น์ฑ์ ๋ง ์์ํ๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉ์ ์ ์ ํฌํผ๋ฅผ ์์ฑํ์ฌ ์ฒ์ฒํ ์์ํ๊ณ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ฉด ๋ค๋ฅธ ํด๊ฒฐ์ฑ ์ผ๋ก ์ ํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
DRY up Your Views
Rails๋ฅผ ์์ํ๋ฉด์ ์ ๋ง ๋ง์์ ๋ค์๋ ์ ์ ๋งํฌ์ ์ ์ฝ๊ฒ ์ ๋ฆฌํ ์ ์๋ค๋ ์ ์ด์์ต๋๋ค. Rails๋ ์ด๋์๋ ํฌํจ๋ ์ ์๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ฝ๋ ์กฐ๊ฐ์ธ partials๋ฅผ ์์ฑํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์๋ฅผ ๋ค์ด ์ฌ๋ฌ ๊ณณ์์ Song์ ๋๋๋งํ๊ณ ์๊ณ ์ฌ๋ฌ ํ์ผ์ ๋์ผํ ์ฝ๋๊ฐ ์๋ ๊ฒฝ์ฐ Song partial์ ๋ง๋๋ ๊ฒ์ด ์ข์ต๋๋ค.
์๋์ ๊ฐ์ด ๋ ธ๋๋ฅผ ํ์ํ๋ค๊ณ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค.
# 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) %>
๊ทธ๋ฆฌ๊ณ ์ด๋์์๋ ์ํ๋ ๊ณณ์ Song partial์ ์๋์ ๊ฐ์ด ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
...
<%= render "song" %>
...
“_Song” partial์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ์๋์ผ๋ก ์กฐํํ ํ์ ๋๋๋ง์ ํฉ๋๋ค. ์ฌ์ฉ์ ์ ์ ํฌํผ๋ฅผ ์ฌ์ฉํ ์์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก partial์์ ์ธ์คํด์ค ๋ณ์์ “@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) %>
๊ทธ๋ฐ ๋ค์ song์ ๋ณ์๋ฅผ partial์ ์ ๋ฌํ์ฌ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๊ณ ๋ค๋ฅธ ๊ณณ์ ํฌํจ์ํค๊ธฐ์ ์ ํฉํ๋๋ก ๋ง๋ค์ด์ผ ํฉ๋๋ค.
...
<%= render "song", song: @song %>
...
๋ง์ง๋ง ์๊ฐ
์ด ํฌ์คํ ์ ์ฌ๊ธฐ๊น์ง์ ๋๋ค. ์์ฝํ์๋ฉด Rails View ์์ญ์์ ๋ณผ ์ ์๋ ๋ช ๊ฐ์ง ํจํด๊ณผ ์ํฐํจํด์ ์ดํด๋ดค์ต๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ์์ ์ ๋๋ค:
- UI์์ ๋ณต์กํ ๋ก์ง ํผํ๊ธฐ(View๊ฐ ๋ง์ Powerlifting์ ์ํํ์ง ์๋๋ก ๋ง๋ค๊ธฐ.)
- Rails๊ฐ View ํฌํผ์ ๊ด๋ จ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ๋ํด ์์๋ณด์ธ์.
- ์ฌ์ฉ์ ์ง์ ํฌํผ ๋ฐ partial ์ฝ๋๋ฅผ ๊ตฌ์กฐํํ๊ณ ์ฌ์ฌ์ฉ ํ์ธ์.
- ์ธ์คํด์ค ๋ณ์์ ๋๋ฌด ๋ง์ด ์์กดํ์ง ๋ง์ธ์.
๋ค์ ๊ฒ์๋ฌผ์์๋ ์๋นํ ์ง์ ๋ถํด์ง์ ์๋ Rails Controller ํจํด๊ณผ ์ํฐ ํจํด์ ๋ํด ๋ค๋ฃฐ ๊ฒ์ ๋๋ค. ๊ธฐ๋ํด์ฃผ์ธ์.
๋ค์ ์๊ฐ๊น์ง ํ์ดํ !
PS. Ruby Magic ๊ฒ์๋ฌผ์ด ๋ณด๋๋๋ ๋๋ก ์ฝ๊ณ ์ถ์ผ์๋ฉด Ruby Magic ๋ด์ค๋ ํฐ๋ฅผ ๊ตฌ๋ ํ์๊ณ ๋จ ํ๋์ ๊ฒ์๋ฌผ๋ ๋์น์ง ๋ง์ธ์!