모델의 특정 상태를 저장하고자 할 때 각각의 상태를 정수로 할당한 후 문자열로 매핑하면 매우 편리하다.
이러한 상태 저장방식은 실무에서 많이 사용하는데 레일스 4.1버전부터 도입된 enum형
을 사용하면 여러가지로 편리한다.
이글에서는 ActiveRecord
의 enum형
을 사용하는 방법에 대해서 알아 보기로 한다.
이해를 돕기 위해서, 논문 정보를 정의하는 Paper
라는 모델을 예로 들어 보자.
우선 EnumTest
라는 레일스 프로젝트를 생성하다.
$ rails new EnumTest && cd EnumTest create create README.rdoc create Rakefile create config.ru create .gitignore create Gemfile create app create app/assets/javascripts/application.js create app/assets/stylesheets/application.css create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/views/layouts/application.html.erb create app/assets/images/.keep create app/mailers/.keep create app/models/.keep create app/controllers/concerns/.keep create app/models/concerns/.keep ...
이제, 커맨드라인에서 아래와 같이 3개의 속성을 가지는 Paper
모델을 scaffolding
하자.
$ rails g scaffold Paper title summary:text status:integer invoke active_record create db/migrate/20150713110048_create_papers.rb create app/models/paper.rb invoke test_unit create test/models/paper_test.rb create test/fixtures/papers.yml invoke resource_route route resources :papers invoke scaffold_controller create app/controllers/papers_controller.rb invoke erb ...
위에서 생성된 마이그레이션 파일(db/migrate/20150713110048_create_papers.rb)을 에디터로 열고, 아래와 같이 status
속성의 기본값을 0
으로 지정한 후 마이그레이션 하자.
class CreatePapers < ActiveRecord::Migration def change create_table :papers do |t| t.string :title t.text :summary t.integer :status, default: 0 t.timestamps null: false end end end
Paper
클래스 파일(app/models/paper.rb)를 에디터로 열고, 아래와 같이 status
속성을 enum형
으로 선언한다.
class Paper < ActiveRecord::Base enum status: { draft:0, confirmed:1, working:2, published:3 } # 또는 # enum status: %w( draft confirmed working published ) end
다음은, 커맨드라인에서 rails console
명령을 실행한 후 빈 객체를 생성하자.
irb(main)> new_paper = Paper.new => <Paper id: nil, title: nil, summary: nil, status: 0, created_at: nil, updated_at: nil>
그리고 status
속성값을 확인해 보자.
irb(main)> new_paper.status => "draft"
어떻게 해서 "draft"
값이 반환되는 것일까? 눈치가 빠른 독자는 이미 이해를 했을 것이다.
우리는 위의 마이그레이션 파일에서 status
속성의 디폴트 값을 0
으로 지정했었다. 따라서 새로운 레코드가 생성될 때 status
의 값은 정수 0
을 가지게 되는데, Paper
클래스에서 status
속성을 enum형
으로 지정했었다. 한가지 유의해야 할 것은 맨처음 키는 디폴트 값으로 사용할 수 있도록 0
값으로 매핑하는 것이 좋다는 것이다. 여기에서도 맨처럼 값 0
에 “draft” 키를 매핑해 놓았다. 따라서 당연히 디폴트 값 0
으로 매핑된 “draft” 값이 반환하게 되는 것이다.
enum형
으로 선언할 경우 우리는 두가지 이점을 가질 수 있다.
하나는 키에 !
를 추가하여 setter
메소드로, ?
를 추가하여 getter
메소드로 사용할 수 있게 된다는 것이다.
위에서 생성한 빈 객체인 new_paper
에 working!
을 호출하면 status
속성에 2
값을 할당한 후 저장하게 된다.
irb(main)> new_paper.working! (0.2ms) BEGIN SQL (0.2ms) INSERT INTO `papers` (`status`, `created_at`, `updated_at`) VALUES (2, '2015-07-13 10:04:37', '2015-07-13 10:04:37') (0.8ms) COMMIT => true
이제 새로 추가된 paper
객체를 불러와 status
를 confirmed
상태로 변경해 보자.
irb(main)> paper = Paper.first irb(main)> paper.confirmed! (0.2ms) BEGIN SQL (0.3ms) UPDATE `papers` SET `status` = 1, `updated_at` = '2015-07-13 10:38:25' WHERE `papers`.`id` = 1 (1.1ms) COMMIT => true
다음은 변경된 상태를 확인해 보자.
irb(main)> paper.confirmed? => true
true
값이 반환되어 제대로 변경된 것을 확인할 수 있다. 그렇다면 paper.working?
와 같이 호출하면 어떤 값이 반환될까 상상해 보자.
irb(main)> paper.working? => false
false
값을 예상했다면 지금까지 설명한 내용을 제대로 이해한 것이다.
또 하나의 이점은 키 이름을 해당 모델 클래스의 scope
클래스 메소드로 바로 사용할 수 있게 된다는 것이다. 아래를 보자.
irb(main)> Paper.confirmed Paper Load (0.3ms) SELECT `papers`.* FROM `papers` WHERE `papers`.`status` = 1 => #<ActiveRecord::Relation [#<Paper id: 1, title: "", summary: "", status: 1, created_at: "2015-07-13 05:15:48", updated_at: "2015-07-13 10:38:25">]>
이것은 Paper.where(status: 1)
를 호출할 것과 같은 결과를 보인다. 이것은 아래와 같이 모델 scope
를 지정한 경우와도 동일하 결과를 보여 준다.
class Paper < ActiveRecord::Base scope :confirmed, -> { where(status: 1) } end
이번에는 작성 중인 논문들을 불러와 보자.
irb(main)> Paper.working Paper Load (0.4ms) SELECT `papers`.* FROM `papers` WHERE `papers`.`status` = 2 => #<ActiveRecord::Relation []>
당연한 결과일 것이다. 아직 2
번 상태(working)로 지정한 논문은 없기 때문이다.
이상으로 ActiveRecord::Enum 데이터형을 활용해서 모델 상태를 효과적으로 관리하는 방법에 대해서 간단하게 알아 보았다.