2013년 12월 19일 목요일

[ROR] model에서 instance variable의 의미

안녕하세요 belhyun입니다. 오늘 개발을 하면서 model에서 접근할 수 있는 변수가 필요했습니다. 실제 구현은 다음과 같이 되었습니다.

class UserTweet
  include Mongoid::Document
  field :user_desc, type:String
  field :type, type:String
  field :tweet_uuid,  type:String
  belongs_to :tweet
  scope :tweet_uuid_exists, ->(tweet_uuid){ where(tweet_uuid: tweet_uuid) }
  scope :type_exists, ->(type){ where(type: type) }
  scope :user_desc_exists, ->(user_desc){ where(user_desc: user_desc) }

  def self.params=(params)
   @params = params
  end

  def self.params
    @params
  end

  def self.criteria
    UserTweet.where(user_desc: params[:user_desc],
                    type: params[:type],
                    tweet_uuid: params[:tweet_uuid])
  end

  def self.save
    if !criteria.exists?
      tweet = Tweet.find_by(uuid: params[:tweet_uuid])
      tweet.user_tweets.create(Hash[params])
      _success
    else
      _fail('already requested')
    end
  end

  def self.delete
    if criteria.exists?
       criteria.delete
      _success
    else
      _fail('resource not exists')
    end
  end

  private
  def self._success
    {:result => 1, :msg => 'success'}
  end

  private
  def self._fail(msg)
    {:result => 0, :msg => msg}
  end
end

위의 코드를 보시면 아시겠지만 params의 setter/getter 함수에서 인스턴스 변수를 사용했습니다. 실제 컨트롤러에서는 다음과 같이 호출했습니다.

      def set_params
        UserTweet.params = params.except(:action, :controller)
      end 
그럼 이제부터 model에서 instance variable이 갖는 의미를 알아보도록 하겠습니다.
먼저 @의 의미부터 알아봐야 될 것 같습니다.
만약 variable, @variable 2 가지의 변수가 있다고 해보겠습니다.
각각의 변수가 가지는 의미는 다음과 같습니다.
variable : 지역변수, 현재 블락에서만 사용 가능하다.
@variable : 인스턴스 변수 클래스내의 모든 메소드에서 사용 가능하다.
테스트를 해보겠습니다. 저 위의 코드에서 실제로 @params를 제거하면 "undefined method" 에러가 발생합니다. 실제로 attr_accessor 또한 인스턴스 gettter/setter를 생성시키므로 attr_accessor을 명시한다고 해도 같은 에러가 발생합니다. 따라서 params의 getter/settter에서 params 인스턴스 변수를 생성시킴으로써 모든 메소드에서 해당 변수에 접근할 수 있게 됩니다.
그럼 model에서 인스턴스 변수가 갖는 의미를 알아보겠습니다. model에서도 같은 의미를 사용될 수 있는데 저렇게 사용되면 각 객체의 변수라기 보다는 클래스의 변수라고 보면 됩니다. 그렇기 때문에 위 처럼 사용해도 문제가 없습니다.
그럼 상단의 코드를 좀 더 깔끔하게 수정해보겠습니다.

class UserTweet
  class << self
    attr_accessor :params 
  end 
params에 대한 getter/settter를 제거하고 위와 같이 변경하였습니다.
저렇게 변경해도 에러가 발생하지 않고 상단코드와 똑같은 의미를 가지게 됩니다.
그럼 저 코드를 분석해 보도록 하겠습니다.
상단의 코드는 params attribute_accessor를 인스턴스 레벨이 아닌 클래스 레벨에 추가하게 됩니다.

class << self는 self 싱글턴 클래스를 가져오게 되고 그러므로 self 안에 정의된 attr_accessor이 self 객체에 재 정의됩니다.  이 문법은 특히 메타클래스라 부릅니다. 그러므로 이 문법은 상단의 gettter/setter와 일치하게 됩니다.

감사합니다.

댓글 없음:

댓글 쓰기