Rails AASM로 상태 관리기능 만들기

Posted by negabaro kim on Saturday, May 5, 2018 Tags: css   5 minute read

AASM이란

Rails에서 상태관리 기능을 간단히 만들 수 있는 Gem이다.

예를들면 채팅어플에서 보낸메일을 상대방이 보지 못했을때(읽지않은 상태)와 상대방이 읽었을때(읽음 상태) 등의 상태를 구현가능하다.

이 포스트에서는 게시판형식에서 글을 등록할때 전체공개,그룹공개,비공개의 상태를 가지게 하는 예제를 만들어볼것이다.

AASM에서는 특정상태에 대한 정의는 state를 사용하고 A상태에서 B상태로 바뀌는 액션을 event를 사용한다. 예제를 보면 알기 쉬울것이다.

AASM인스톨(Gemfile)

# Gemfile
gem 'aasm'

상태관리용 컬럼추가

rails로 퀴즈어플 만들어보기(CRUD편)에서 만든 writing_quiz라는 모델에 상태관리용 컬럼을 추가해보자 이하와 같은 커맨드를 실행하면 자동으로 migrate파일이 만들어진다. default로는 aasm_state라는 컬럼이 만들어진다. 특별한 이유가 없으면 default그대로 쓰는게 좋을 것 같다.

rails generate aasm writing_quiz

result:

invoke  active_record
create    db/migrate/20180504075459_add_aasm_state_to_writing_quizzes.rb
insert    app/models/writing_quiz.rb

db:migrate로 실제 컬럼을 추가시킨다.

rake db:migrate

모델에 설정 추가

aasm_state컬럼을 추가한 모델에 이하 설정을 추가해주자.

#app/models/writing_quiz.rb
class WritingQuiz < ApplicationRecord
  include AASM

  aasm do
    state :draft, :initial => true  #default 비공개상태
    state :published  #전체공개 상태
    state :limited_public #제한된 공개상태 


    #비공개,제한공개상태에서 공개상태로 상태를 전환시키는 event
    event :publish do
      transitions :from => [:draft, :limited_public], :to => :published
    end
    
    #전체공개,제한공개상태에서 비공개 상태로 전환시키는 event
    event :draft do
      transitions :from => [:limited_public, :published], :to => :draft
    end
    
    #전체공개,비공개상태에서  제한공개 상태로 전환시키는 event
    event :limit_publish do
      transitions :from => [:draft, :published], :to => :limited_public
    end
    
 
  end
  
end

rails console에서 동작확인

여기까지 설정하면 Rails에서AASM을 사용할 준비가 끝났다. Rails console에서 동작확인을 해보자.

상태확인방법
wq = WritingQuiz.new
wq.drfat?  #현재상태는 비공개 상태입니까?라고 확인하는 커맨드 디폴트값이 draft이므로 true가 반환된다.
=> true
wq.publish?  #현재상태는 전체공개가 아니므로 false가 반환된다.
=> false 
상태 바꿔보기
wq.publish  #디폴트 비공개상태에서 전체공개로 상태를 변경
wq.drfat? #다시 비공개상태냐고 질의하면 현재는 전체공개상태이므로 false가 반환된다.
=> false
wq.limit_publish  #전체공개 상테에서 그룹공개 상태 로 변경
wq.draft? #그룹공개 상태이므로 false가 반환
=> false
wq.limit_public #그룹공개상태이므로 true가 반환됨.
=> true

컨트롤러/뷰 설정확인

다음은 Rails컨트롤러와 뷰에서 어떤식으로 사용하는지 살펴보자.

view 설정

<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">수정하기</h1>
    <%= form_for @wquiz do |w| %>
      <div class="form">
        <div class="form-body">
          <% @wquiz.errors.full_messages.each do |message| %>
            <div class="form-error">
              <%= message %>
            </div>
          <% end %>
            <div class=question>
              <%= w.label :question, "질문" %>
              <%= w.text_field :question %>
            </div>

            <div class="answer">
              <%= w.label :answer, "정답" %>
              <%= w.text_field :answer %>
            </div>

            <%= w.button "전체공개 등록",  name: 'ope[cmd]', value: 'publish' %>
            <%= w.button "그룹공개 등록",  name: 'ope[cmd]', value: 'limit_publish' %>
            <%= w.button "비공개 등록",  name: 'ope[cmd]', value: 'draft' %>

        </div>
      </div>
    <% end %>
  </div>
</div>

화면캡쳐

image

submit type의 버튼 3개에 서로다른 value값을 둬서 컨트롤러에서 어떤 버튼을 눌렀는지 판별할 수 있게 해둔것이 특징이다.

컨트롤러 설정

  before_action :set_aasm, only: [:update, :create]
  
  def set_aasm
       case params[:ope][:cmd]
        when 'publish'
          flash[:notice] = "전체공개 했습니다"
          @wquiz.publish! if !@wquiz.published? 
        when 'limit_publish'
          flash[:notice] = "그룹공개 했습니다."
          @wquiz.limit_publish! if !@wquiz.limited_public? 
        when 'draft'
          flash[:notice] = "비공개로 등록했습니다."
          @wquiz.draft! if !@wquiz.draft? 
        else
        end
  end

컨트롤러에서는 case,when문을 이용해서 어떤 버튼을 눌렀는지에 따라 특정 상태로 변경하고 있다. 그리고 신규등록과 수정 양쪽다 스테터스 변경할 수 있게끔 공통 메소드set_aasm을 만들었다.

모델에 정의한 AASM설정이 다른 스테터스로만의 변경만 가능하게 했으므로 예를들어 전체공개인 상태에서 전체공개로 스테터스를 변환하려 하면 에러가나므로 동일 스테터스일 경우 스킵하도록 했다.

동작확인

위 예제를 참고해서 등록,수정을 한뒤 rails log를 보면 이하와 같이 aasm_state값이 갱신되는걸 알 수 있다.

    Parameters: {"utf8"=>"✓", "authenticity_token"=>"qppuaWtSENBYUiUEGEcV9JODxWVCZZUoYbI4HOQjkKNLVWT0IW5m46lISoh5Kd5OSXYPpE6hz/22g8ldbNl0Rw==", "writing_quiz"=>{"question"=>"1+1은?", "answer"=>"귀여미"}, "ope"=>{"cmd"=>"draft"}}
   (0.3ms)  BEGIN
  SQL (13.5ms)  INSERT INTO `writing_quizzes` (`question`, `answer`, `created_at`, `updated_at`, `aasm_state`) VALUES ('1+1은?', '귀여미', '2018-05-04 12:34:39', '2018-05-04 12:34:39', 'draft')
   (23.7ms)  COMMIT