Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Rails에서 입력형식 변경시 유니크값 체크하기

요즘 Ruby on Rails 온라인 코스를 하면서 보통 Homework의 난이도가 그렇게 높지 않았었는데 이번에는 상당히 어려웠습니다. 물론 이부분은 Ruby에 대해서 배울때는 따로 루비를 공부하고 있었던 탓도 있었고 지금은 Rails는 따로 공부하고 있지 못하고 있어서 이기도 합니다. 어쨌든 그래도 약간 고민하고 있으면 할 수 있었는데 이번에는 장시간동안 고생을 했습니다. Homework와 관련된 것이라 따로 언급하지는 않겠습니다.



어쨌든 레일즈의 Active Record에는 유효성검증을 위해서 제공하는 기능들이 있습니다. 웹개발에서 유효성(Validation) 검증은 필수적이라고 하는데 이걸 편하게 할 수 있도록 validates_라는 이름으로 시작하는 유효성 검증 헬퍼를 제공하고 있습니다. 미리 약속된 이 헬퍼를 사용하면 자동으로 유효성검증을 실행합니다. 또한 이 검증외에 기능을 수행하기 위해서 각 단계별로 콜백함수 또한 제공하고 있습니다. before_validation, before_save같은 함수이고 이걸 정의해 놓으면 해당 단계에서 해당 함수가 실행이 됩니다.

Homewor의 제약조건은 간단했습니다. ActiveRecord에 credit_card라는 컬럼이 있는데 이 컬럼은 유일한 값이어야 하고 입력밧은 0000 0000 0000 0000나 0000-0000-0000-0000의 2가지 형식만 입력받아야 하고 필드에 저장은 0000000000000000와 같은 형식으로 저장이 됩니다. 콜백함수는 before_create, before_save 2가지를 사용해서 구현하는 것이었습니다.

아주 간단하게 생각하면 validates_uniqueness_of로 credit_card를 유니크로 지정하고 validates_format_of를 정의하여 정규식으로 입력형식을 지정해 주면 되는 것이고 before_create, before_save에서 입력받은 형식을 컨버팅해주고 저장하면 되는 것이었습니다. 하지만 막상 구현하려고 하니까 문제가 생겼습니다.


validates_uniqueness_of:credit_card,
    :message=>"credit card is unique."

validates_format_of:credit_card,
    :with=>/[0-9]{4}[-| ][0-9]{4}[-| ][0-9]{4}[-| ][0-9]{4}/,
    :message=>"Invalid credit_card format"

콜백함수인 before_create, before_save보다 validates의 실행시점이 더 빠르기 때문에 validates_uniqueness_of가 실행되는 시점에서는 디비에 들어있는 값들은 입력값의 형식이 변환되었기 때문에 유효성검증의 기능을 전혀 하지 못했습니다. validates_format_of에는 configration option인 실행시점을 바꿀 수 있는 :on이 있지만 :on은 validates_uniqueness_of에는 존재하지 않았습니다. API문서에도 Rails 2.1에도 나오지 않습니다. 다만 제가 가진 책인 Rails 1.2기반인데 여기서는 :on 옵션이 나와있는데 중간에 없어진 것인지는 잘 모르겠지만 일단 실행시점을 바꾸는 동작을 동작되지 않았습니다.

결국 오랜 고민을 했지만 Unique확인을 유효성 검증 헬퍼가 아닌 콜백함수에서 실행할 수밖에 없었습니다. 제가 선택한건 before_create였습니다. before_save에서 데이터 형식의 변환을 실행하고 before_create에서 변환된 값을 가지고 유일값인지 아닌지를 체크했습니다.


def before_create
    if User.find_by_credit_card(self.credit_card)
        errors.add_to_base("credit_card is not unique")
        return false
    end
end

어쨌든 동작은 원하는대로 되긴 했는데 이게 맞는지도 잘 모르겠습니다.(이래서 좀더 공부를 해야되는데요.. ㅠ..ㅠ) ActiveRecord의 ORM기능을 이용해서 credit_card함수를 현재값으로 Select해서 값이 있을 경우 errors에 오류를 추가하고 false를 반환합니다. errors만 추가할 경우에는 에러만 추가되고 실제 값을 저장하는 것에는 상관이 없었습니다. raise를 이용해서 ActiveRecord의 오류를 발생시키는 것도 시도했었지만 return false를 하는 것만으로도 디비저장을 멈출 수 있었습니다.(저도 정확히 알지 못하는 상태의 포스팅이라 약간은 찜찜하군요. )
2009/06/21 23:14 2009/06/21 23:14