티스토리 뷰

이 글은 Rails 5.0 Guide 기준으로 작성됩니다.
https://guides.rubyonrails.org/v5.0/active_record_callbacks.html

 

 

  • Active Record Callbacks

Active Record 객체 내부 동작에 있어 콜백이 무엇이고, 콜백이 어떻게 돌아가는지 알아보겠습니다.

 

 

  • The Object Life Cycle

Rails Application이 정상적으로 작동된다면 객체의 생성, 업데이트, 삭제가 됩니다. Active Record는 객체 내부에 life cycle라는게 돌아가는데, life cycle이 중간에 트리거로서(생성/수정/삭제/Load) 어느 이벤트가 발생 전(before) 혹은 후(after)에 콜백을 제어할 수 있습니다.

 

 

  • Callbacks Overview

콜백은 객체 내부의 Life cycle이 돌아가면서 특정되는 순간에 호출되는 메소드 입니다. 콜백이 호출되면 데이터베이스에서 Active Record를 통해 모델 내 데이터베이스에서 뭔가 작업이 시작될 시 실행될 콜백 코드를 작성할 수 있습니다.

 

콜백 작성

콜백은 Model 내에서 아래와 같이 표현을 할 수 있습니다.

 

1) 메소드 작성

class Comment < ApplicationRecord
  before_validation :tiitle_first_word_uppercase

  protected
  def tiitle_first_word_uppercase
    self.title = title.titleize
  end
end

 

2) 블록 단위 작성

class Bulletin < ApplicationRecord
  before_create do
    self.title = title.titleize
  end
end

 

 

  • Available Callbacks

콜백에 지원되는 문법은 다음과 같습니다.

 

콜백 : 객체 생성

before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback

 

콜백 : 객체 수정

before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
after_commit/after_rollback

 

콜백 : 객체 삭제

before_destroy
around_destroy
after_destroy
after_commit/after_rollback

 

 참고  삭제 관련 콜백에 있어, delete는 callback에 영향을 받지않는 메소드 입니다.

## app/models/bulletin.rb

class Bulletin < ApplicationRecord
  before_destroy :check_remove

  private
  def check_remove
    puts "데이터가 삭제 전, 해당 메소드를 거치게 됩니다."
  end
end

 

콜백 : after_initialize, after_find, after_touch

1) after_initialize

Model을 활용한 데이터 탐색/삭제/수정/조회 등 작업 후, after_initialize 블록 내의 코드가 실행됩니다.

 

2) after_find

Model을 활용한 데이터 탐색 관련 메소드를 활용 시, SQL 이벤트 발동 후 after_find 블록 내의 코드가 실행됩니다.

## after_find 콜백에 활용 가능한 탐색 관련 메소드

all
first
find
find_by
find_by_*
find_by_*!
find_by_sql
last



## after_find 콜백에 활용 불가능한 탐색 관련 메소드

update_all
find_or_create_by
delete_all
... (등등) ...

 

3) after_touch

touch 메소드 데이터가 존재할 경우 updated_at이 touch된 시간을 기준으로 시간이 수정되는데, touch 되면서 after_touch 블록 내의 코드가 실행됩니다.

 

해당 메소드는 테이블 간 연관관계를 정의하는  belongs_to  문법과도 함꼐 사용을 할 수 있습니다.

class Employee < ApplicationRecord
  belongs_to :company, touch: true
  after_touch do
    puts 'An Employee was touched'
  end
end
 
class Company < ApplicationRecord
  has_many :employees
  after_touch :log_when_employees_or_company_touched
 
  private
  def log_when_employees_or_company_touched
    puts 'Employee/Company was touched'
  end
end

 

 

  • Running Callbacks

콜백을 작동시키기 위해 지원되는 메소드는 다음과 같습니다.

 

콜백 트리거 메소드

create
create!
destroy
destroy!
destroy_all
save
save!
save(validate: false)
toggle!
update_attribute
update
update!
valid?

 

 

  • Skipping Callbacks

콜백 트리거를 지원하지 않는 메소드는 아래와 같습니다.

 

콜백을 무시하는 메소드

decrement
decrement_counter
delete
delete_all
increment
increment_counter
toggle
touch
update_column
update_columns
update_all
update_counters

 

 

  • Halting Execution

어떤 작업의 트랜잭션에 대해 막고자 할 경우, 쓰기 좋은 인터럽트 방법입니다. (대표적인 활용예시로 before_destroy 에 쓰입니다.)

 

 

1. destroy 메소드를 통해 삭제가 되기 전, 데이터를 검증(validation)하는 과정이 있습니다.

 

2. 한국어로 쓰여진 데이터를 배제하고 데이터 검증하는데에 있어 destroy 메소드를 통해 데이터를 삭제를 하고싶은 상황이 있습니다.

 

3. 참고로 다음과 같은 코드는 에러 메세지를 남기더라도, 결국 삭제가 이루어진다는 문제점이 있습니다.

class Bulletin < ApplicationRecord
  before_destroy :use_korean

  private
  def use_korean
    if title.match(/\A[가-힣]+\z/)
      errors.add(:title, "can't be delete hanguel content")
    end
  end
end

 

4. 만약 데이터 삭제에 있어 유효성 규칙을 위반할 경우, 데이터가 삭제되는 것을 막는 방법(prohibit commit)은  throw(:abort)  코드를 추가하는 겁니다.

class Bulletin < ApplicationRecord
  before_destroy :use_korean

  private
  def use_korean
    if title.match(/\A[가-힣]+\z/)
      errors.add(:title, "can't be delete hanguel content")
      + throw(:abort)
    end
  end
end

위와같이 throw 조건을 두어 처리할 경우,  throw(:abort)  코드를 만나는 시점에서 SQL 내부에서는 rollback(데이터 작업 실패)와 함께 작업을 종료할겁니다.

 

 참고  after_* 에는 raise(rollback) 이 발생하지 않습니다.

 부록  Active Record Callbacks can not be canceled after_xx

 

 

  • Relational Callbacks

만약, 연계된 데이터에 있어 부모데이터가 삭제될 경우, 자식데이터도 삭제되어야 하는 상황이 있습니다.

 예시  본 댓글이 삭제된다면, 대댓글도 함께 삭제

 

Rails에서는 위와같은 상황을 위해 dependent: :destroy 옵션을 제공해주고 있으며, 사용 예시는 아래와 같습니다.

class Bulletin < ApplicationRecord
  has_many :comments, dependent: :destroy
end
## [option] 필요시에는 dependency 관계에 있어, 자식 모델에서 after_destroy 와도 함께 사용 가능합니다.

class Comment < ApplicationRecord
  after_destroy :log_destroy_action
 
  def log_destroy_action
    puts "All Comment destroyed'
  end
end

 

 

  • Conditional Callbacks

과거 Chapter 4 때와 동일하게 callback 발동조건에 대해서도 조건을 줄 수 있습니다.

 

주로 :if, :unless 문법을 통해 조건을 줄 수 있고, 만약 callback을 발동시키지 않을 조건을 주고 싶다면 :unless 문법을 걸어주면 됩니다.

조건문 표현에 있어도 Validation 때와 동일하게 :string, :method, Proc와 추가적으로 Array로 조건을 표현할 수 있습니다.

 

 

  • Callback Classes

만약 콜백 처리에 있어 현재 Model에서 처리하는게 아닌 다른 Model로 데이터를 넘겨서 callback 검증 및 처리를 하고 싶을 경우, 다음과 같이 따로 모델파일을 만든 후, 해당 모델파일 안에 인스턴스 메소드 표현을 통해 처리할 수 있습니다.

## app/models/bulletin.rb 

class Bulletin < ApplicationRecord
  before_destroy ProhibitDestroy
end
## app/models/prohibit_destroy.rb

class ProhibitDestroy
  def self.before_destroy(data)
    puts "data(object) : #{data}"
    puts "data(object.title) : #{data.title}"

    if data.title.match(/\A[가-힣]+\z/)
      throw(:abort)
    end
  end
end

 

 

  • Transaction Callbacks

트랜잭션 콜백은 데이터베이스의 트랜잭션 작업 완료를 기반으로 어떤 코드가 작동되게 하는 트리거 역할로 쓰입니다. :after_commit, :after_rollback

해당 콜백은 데이터베이스 변경이 커밋되거나 롤백될 때에는 실행이 되지 않는다는 점을 제외하고는 일반적인 콜백과 거의 동일합니다. 해당 콜백은 Active Record Model이 데이터베이스 트랜잭션의 외부 시스템과 상호 작용해야 할 때 가장 유용합니다.

 

 

:after_commit을 기반으로 활용할 수 있는 트랜잭션 콜백 문법은 아래와 같습니다.

after_create_commit
after_update_commit
after_destroy_commit

 

 

  • 자료 참고

1. Active Record Callbacks can not be canceled after_xx

2. rails 5의 before_destroy 콜백 throw ( - abort) 및 레코드 수정

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함