이 글에서는 acts-as-taggable-on 젬(gem)을 사용하여 태깅(Tagging)을 구현하는 방법을 소개한다.
설치
vi Gemfiel
gem 'acts-as-taggable-on'
bundle install
rake acts_as_taggable_on_engine:install:migrations
rake db:migrate
지금까지의 절차에 의해서 tags와 taggings 테이블이 생성된다. 만약 Tag와 Tagging 모델이 필요하면 별도로 생성되어야 한다.
rails g model Tag --no-migration
rails g model Tagging --no-migration
셋업
vi app/models/blog.rb

class Blog < ActiveRecord::Base
  acts_as_taggable
end
Tagging을 적용할 모델에서 acts_as_taggable()을 호출한다.
사용하기
아래는 tag_list=()를 이용해서 태그를 생성하는 예를 보여준다. tag_list=()는 기존에 tag_list값이 있더라도 새로운 값을 생성한다.

@blog.tag_list = "awesome, slick, hefty"
@blog.save
@blog.reload
@blog.tags
기존의 태그에 새로운 값을 추가, 삭제하고 싶을때는 tag_list.add()tag_list.remove()를 사용한다. 이 메쏘드는 기본적으로 Array를 인자로 받는데 parse: true 옵션에 의해 ","로 구분된 String을 그대로 받아서 처리할 수 있다.

@blog.tag_list.add("awesome", "slick", "hefty")
@blog.tag_list.remove("awesome", "slick")
@blog.tag_list.add("awesome, slick, hefty", parse: true)
@blog.tag_list.remove("awesome, slick", parse: true)
태그값을 조회하기 위해서는 아래 방법을 사용한다.

@blog.tag_list
Blog.tagged_with("awesome")
Blog.tag_counts
태그 입력 폼
제일 기본적인 형태는 콤마(",")로 분리된 태그값들을 입력받는 방식이다.
vi app/views/blogs/_form.html.erb

<%= f.label :tag_list, "Tags" %>
<%= f.text_field :tag_list, value: blog.tag_list.join(", ") %>
이 필드에 의해서 전달되는 params의 형태는 {"blog" => {"tag_list" => "xxx, yyy, zzz"}} 이므로 콘트롤러에는 :tag_list에 대한 Strong parameter 지정이 필요하다.
vi app/controller/blogs_controller.rb

def blog_params
  params.require(:blog).permit(:title, :tag_list)
end
또 다른 방안으로는 체크박스를 통해 태그를 입력받는 경우를 생각해 볼 수 있는데 이 경우 폼의 형태는 다음과 같다.

<% Blog::TAGS.sort.in_groups_of(4).each do |tags| %>
  <div class='row'>
    <% tags.each do |tag| %>
      <% if tag.present? %>
        <%= check_box_tag "blog[tag_list][]", tag,
            blog.tag_list.map(&:downcase).include?(tag.downcase) %>
            <%= "#{tag}" %>
        <% end %>
      <% end %>
    <% end %>
  </div>
<% end %>
이때 전달되는 params는 {"blog" => {"tag_list" => ["xxx", "yyy", "zzz"]}}로써 앞서의 경우와 형태가 다르다. 따라서 Strong parameter를 아래와 같이 수정한다.

def blog_params
  params.require(:blog).permit(:title, tag_list: [])
end
태그로 서치하기
뷰(View)에 다음과 같은 태그 링크들을 준비한다.
vi app/views/blogs/index.html.erb

<% Blog.all_tag_counts.sort_by(&:name).each do |tag| %>
  <%= link_to tag.name, tag_path(tag.name) %>
<% end %>
# 또는 <%= raw(Blog.all_tag_counts.sort_by(&:name).map { |tag| link_to tag.name, '#' }.join(', ')) %>
tag_path()가 동작하기 위해서 routes.rb에 다음 엔트리를 추가한다.
get "tags/:tag", to: "blogs#index", as: :tag
마지막으로 콘트롤러에서 tagged_with()를 사용해서 검색을 수행한다. 이 메쏘드는 ActiveRecord_Relation 오브젝트를 리턴하기 때문에 기타 ActiveRecord 메쏘드들을 체인으로 연결해서 사용할 수 있다.
vi app/controllers/blgos_controller.rb

def index
  if params[:tag]
    @blogs = Blog.tagged_with(params[:tag]).order(released_on: :desc)
  else
    @blogs = Blog.order(released_on: :desc)
  end
end