태그 달기
태그 달기
각 post
에 태그를 붙여 보자. ruby-toolbox.com에서 태그관련 젬을 검색해 보면 단연코 acts-as-taggable-on
이라는 젬의 사용빈도가 가장 높다.
Gemfile
열고 아래와 같이 젬을 추가하고,
gem 'acts-as-taggable-on'
번들 인스톨한 후,
$ bin/bundle install
로컬 웹서버를 다시 부팅한다.
다음은 이 젬의 작동을 위해 몇가지 마이그레이션 파일을 아래와 같이 생성한다.
$ bin/rake acts_as_taggable_on_engine:install:migrations
그리고 마이그레이션 작업을 한다.
$ bin/rake db:migrate
이제 태그를 붙이고자 하는 Post
모델 클래스 파일(app/models/post.rb
)을 열고 아래와 같이 코드를 추가한다.
class Post < ActiveRecord::Base
acts_as_taggable
...
end
이로써 Post
모델에 대해서 tag_list
속성 메소드를 사용할 수 있게 된다. 이 속성을 폼 데이터로 입력받기 위해서는 strong parameter
로 등록해 주어야 한다. 이를 위해서 posts_controller.rb
파일(app/controllers/posts_controller.rb
)을 열고 아래와 같이 post_params
메소드에 :tag_list
속성을 추가한다.
def post_params
params.require(:post).permit(:title, :content, :picture, :picture_cache, :tag_list)
end
실제로 :tag_list
속성을 폼으로부터 입력받기 위해서 post
폼 partial
템플릿 파일(app/views/posts/_form.html.erb
)을 열고 아래와 같이 추가한다.
<%= simple_form_for([@bulletin, @post]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :content, input_html: { rows: 10 } %>
<% if @post.bulletin.post_type == "gallery" %>
<%= f.input :picture, as: :file %>
<%= f.hidden_field :picture_cache %>
<% end %>
<%= f.input :tag_list, placeholder: '하나 이상의 태그는 콤마(,)로 구분하여 입력하세요.' %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
태그는 한글도 가능하고, 태그 사이에 공백도 가능하다.
이와 같이 입력된 태그를 보여 주기위해 posts#show
액션 뷰 템플릿 파일(app/views/posts/show.html.erb
)을 열고 아래와 같이 추가한다.
...
<tr>
<th>Tag list</th>
<td><%= tag_icons @post.tag_list %></td>
</tr>
...
여기에서 사용한 tag_icons
헬퍼 메소드는 어플리케이션 헬퍼 파일(app/helpers/application_helper.rb
)에 아래와 같이 정의한다.
def tag_icons(tag_list)
tag_list.map do | tag |
"<a href='/posts?tag=#{CGI::escape(tag)}' class='tag'>#{tag}</a>"
end.join(', ').html_safe
end
여기서 사용한 CGI::escape()
메소드는 태그에서 사용할 수 있는 특수문자를 이스케이핑하기 위한 것이다.
posts#show
액션 뷰 템프릿 파일에 갤러리 게시판의 경우 업로드된 이미지를 보여 줄 필요가 있다. 이를 위해서 @post.bulletin.post_type
이 gallery
일 경우 아래와 같이 추가해 준다.
...
<% if @post.bulletin.post_type == "gallery" %>
<tr>
<th>Picture</th>
<td><%= image_tag @post.picture_url %></td>
</tr>
<% end %>
...
이제는 posts#index
액션 뷰 템플릿 파일에서 태그를 표시하도록 하자. 이 때는 여건상 블로그형과 갤러리형 게시판에서만 태그를 표시하도록 하자.
우선 app/views/posts/post_types/_blog.html.erb
파일을 열고 아래와 같이 추가한다.
<% if post.tag_list.size > 0 %>
<div class='tag_list'><%= fa_icon('tags') + " " + tag_icons(post.tag_list) %></div>
<% end %>
app/views/posts/post_types/_gallery.html.erb
파일에도 적당한 위치에 동일한 내용을 추가한다.
태그 존재할 경우만 보여주기 위해 if
조건문을 사용하였다. tag_list
CSS 클래스를 정의해 주기 위해서 app/assets/stylesheets/posts.scss
파일을 열고 아래와 같이 추가해 준다.
...
.tag_list {
margin-bottom: .5em;
}
...
또한 이 CSS 파일에, tag_icons()
헬퍼 메소드에서 생성하는 태그에 테두리를 추가하기 위해서, 스타일을 아래와 같이 추가한다.
...
a.tag {
border:1px solid #eaeaea;
border-radius:3px;
background-color: #f4f4f4;
padding:.1em .2em;
&:hover {
text-decoration: none;
color: white;
background-color: #265484;
border:1px solid #265484;
}
}
각 태그에는 해당 태그로 검색할 수 있도록 <a>
링크 태그의 href
속성으로 /posts?tag=...
와 같이 지정했다. 즉, posts#index
액션을 호출시에 :bulletin_id
파라미터 없이 :tag
파라미터만 넘겨 주게 된다. 따라서 모든 posts
객체에 대해서 해당 태그를 가진 것들을 쿼리하게 된다.
이를 위해서는 posts_controller.rb
파일(app/controllers/posts_controller.rb
)에서 index
, set_bulletin
, set_post
메소드를 아래와 같이 수정해야 한다.
...
def index
if params[:bulletin_id]
@posts = @bulletin.posts.all
else
if params[:tag]
@posts = Post.tagged_with(params[:tag])
else
@posts = Post.all
end
end
end
...
private
def set_bulletin
@bulletin = Bulletin.find(params[:bulletin_id]) if params[:bulletin_id]
end
def set_post
if params[:bulletin_id]
@post = @bulletin.posts.find(params[:id])
else
@post = Post.find(params[:id])
end
end
...
index
액션에서 사용한 Post.tagged_with()
클래스메소드는 acts-as-taggable-on
젬이 지원한 메소드로 임의의 태그를 넘겨 주면 해당 태그를 포함하는 post
객체들을 반환해 준다.
이제 태그로 검색한 결과를 보여 주기 위해서 posts#index
액션 뷰 템플릿 파일(app/views/posts/index.html.erb
)을 수정해야 한다.
<% if params["bulletin_id"] %>
<%= render "posts/post_types/#{@bulletin.post_type}" %>
<% else %>
<% if params[:tag] %>
<h2>Posts tagged with "<%= params[:tag] %>" <small>( <%= @posts.size %> )</small></h2>
<% else %>
<h2>All Posts <small>( <%= @posts.size %> )</small></h2>
<% end %>
<ul id='posts_tagged'>
<%= render @posts %>
</ul>
<% end %>
그리고 <%= render @posts %>
에서 사용할 _post.html.erb
파일을 app/views/posts/
디렉토리에 생성하고 아래와 같이 추가한다.
<li>
<span class='label label-default'><%= post.try(:bulletin).try(:title) %></span>
<%= link_to post.title, [post.bulletin, post] %>
<%= time_ago_in_words(post.created_at) %> ago
<%= fa_icon('tags') + ' ' + tag_icons(post.tag_list) %>
</li>
app/assets/stylesheets/posts.scss
파일을 열고 아래의 내용을 추가한다.
...
ul#posts_tagged {
margin-top:2em;
li {
margin-bottom: .5em;
}
}
그러나 한가지 문제가 발생한다.
태그를 생성할 때는 문제가 없지만, 태그수정을 위해 posts#edit
액션을 호출하면, Tag list
입력란의 태그들 사이에 구분문자(쉼표)가 보이지 않는다. 이런 문제는 디자인상의 보안 문제로 변경이 된 것이라고 한다. 해결책은 커스텀 input을 작성하는 것이라고 해서 post.rb
클래스 파일에 아래와 같이 두개의 메소드를 추가해 주었다.
def tag_list_fixed
tag_list.to_s
end
def tag_list_fixed=(tag_list_string)
self.tag_list = tag_list_string
end
그리고 posts
컨트롤러에서 strong parameter
지정을 아래와 같이 수정한다. (:tag_list
를 tag_list_fixed
로 수정했다)
def post_params
params.require(:post).permit(:title, :content, :picture, :picture_cache, :tag_list_fixed)
end
또한 views/posts/_form.html.erb
파일에서 :tag_list
속성을 :tag_list_fixed
로 수정했다.
<%= f.input :tag_list_fixed, placeholder: '하나 이상의 태그는 콤마(,)로 구분하여 입력하세요.' %>
이제 수정시에도 태그 구분문자(쉼표)가 제대로 보일 것이다.
이상으로 태그 달기를 마치도록 하겠다.
Git소스 https://github.com/rorlakr/rcafe/tree/chapter_05_15