Active Record 유효성 검사
이 가이드는 Active Record의 유효성 검사 기능을 사용하여 데이터베이스에 저장되기 전에 객체의 상태를 검증하는 방법을 알려줍니다.
이 가이드를 읽고 나면 다음을 알 수 있습니다:
- 기본 제공 Active Record 유효성 검사 헬퍼를 사용하는 방법.
- 사용자 정의 유효성 검사 메서드를 만드는 방법.
- 유효성 검사 과정에서 생성된 오류 메시지를 처리하는 방법.
유효성 검사 개요
다음은 매우 간단한 유효성 검사의 예입니다:
class Person < ApplicationRecord validates :name, presence: true end
irb> Person.create(name: "John Doe").valid? => true irb> Person.create(name: nil).valid? => false
보시다시피, 우리의 유효성 검사는 name
속성이 없으면 Person
이 유효하지 않다는 것을 알려줍니다. 두 번째 Person
은 데이터베이스에 저장되지 않습니다.
더 자세히 살펴보기 전에, 유효성 검사가 애플리케이션의 큰 그림에서 어떻게 맞는지 이야기해 보겠습니다.
왜 유효성 검사를 사용해야 하나요?
유효성 검사는 데이터베이스에 유효한 데이터만 저장되도록 하는 데 사용됩니다. 예를 들어, 애플리케이션에서 모든 사용자가 유효한 이메일 주소와 우편 주소를 제공하도록 하는 것이 중요할 수 있습니다. 모델 수준의 유효성 검사는 데이터베이스에 유효한 데이터만 저장되도록 하는 가장 좋은 방법입니다. 이는 데이터베이스에 의존적이지 않고, 최종 사용자가 우회할 수 없으며, 테스트하고 유지 관리하기 편합니다. Rails는 일반적인 요구 사항에 대한 기본 제공 헬퍼를 제공하며, 사용자 정의 유효성 검사 메서드를 만들 수도 있습니다.
데이터를 데이터베이스에 저장하기 전에 데이터를 검증하는 다른 방법에는 네이티브 데이터베이스 제약 조건, 클라이언트 측 유효성 검사 및 컨트롤러 수준 유효성 검사가 있습니다. 장단점을 요약하면 다음과 같습니다:
- 데이터베이스 제약 조건 및/또는 저장 프로시저는 유효성 검사 메커니즘을 데이터베이스에 의존적으로 만들어 테스트와 유지 관리가 더 어려워질 수 있습니다. 그러나 데이터베이스가 다른 애플리케이션에서도 사용되는 경우 데이터베이스 수준에서 일부 제약 조건을 사용하는 것이 좋습니다. 또한 데이터베이스 수준의 유효성 검사는 (많이 사용되는 테이블의 고유성 등) 다른 방법으로 구현하기 어려운 것을 안전하게 처리할 수 있습니다.
- 클라이언트 측 유효성 검사는 유용할 수 있지만 단독으로 사용하기에는 일반적으로 신뢰할 수 없습니다. JavaScript로 구현된 경우 사용자의 브라우저에서 JavaScript가 비활성화되면 우회될 수 있습니다. 그러나 다른 기술과 결합하면 사용자에게 즉각적인 피드백을 제공하는 편리한 방법이 될 수 있습니다.
- 컨트롤러 수준의 유효성 검사는 유혹적일 수 있지만 종종 복잡해지고 테스트하고 유지 관리하기 어려워집니다. 가능한 한 컨트롤러를 단순하게 유지하는 것이 좋습니다. 그렇게 하면 장기적으로 애플리케이션을 더 편리하게 작업할 수 있습니다.
특정 경우에 이러한 방법을 선택하세요. Rails 팀의 의견으로는 모델 수준의 유효성 검사가 대부분의 상황에서 가장 적절합니다.
언제 유효성 검사가 발생하나요?
Active Record 객체에는 두 가지 종류가 있습니다: 데이터베이스 내부의 행에 해당하는 객체와 그렇지 않은 객체. new
메서드를 사용하여 새 객체를 만들면 해당 객체는 아직 데이터베이스에 속하지 않습니다. save
를 호출하면 해당 객체가 적절한 데이터베이스 테이블에 저장됩니다. Active Record는 new_record?
인스턴스 메서드를 사용하여 객체가 이미 데이터베이스에 있는지 여부를 결정합니다. 다음과 같은 Active Record 클래스를 고려해 보겠습니다:
class Person < ApplicationRecord end
bin/rails console
의 출력을 보면 다음과 같습니다:
irb> p = Person.new(name: "John Doe") => #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil> irb> p.new_record? => true irb> p.save => true irb> p.new_record? => false
새 레코드를 만들어 저장하면 데이터베이스에 SQL INSERT
작업이 전송됩니다. 기존 레코드를 업데이트하면 대신 SQL UPDATE
작업이 전송됩니다. 유효성 검사는 일반적으로 이러한 명령이 데이터베이스로 전송되기 전에 실행됩니다. 유효성 검사에 실패하면 객체가 유효하지 않은 것으로 표시되고 Active Record는 INSERT
또는 UPDATE
작업을 수행하지 않습니다. 이렇게 하면 데이터베이스에 유효하지 않은 객체가 저장되는 것을 방지할 수 있습니다. 객체가 생성될 때, 저장될 때 또는 업데이트될 때 특정 유효성 검사를 실행하도록 선택할 수 있습니다.
주의: 데이터베이스의 상태를 변경하는 방법이 많이 있습니다. 일부 메서드는 유효성 검사를 트리거하지만 다른 메서드는 그렇지 않습니다. 즉, 주의하지 않으면 유효성 검사를 거치지 않고 데이터베이스에 유효하지 않은 객체를 저장할 수 있습니다.
다음 메서드는 유효성 검사를 트리거하며, 객체가 유효한 경우에만 데이터베이스에 저장합니다:
뱅 버전(예: save!
)은 레코드가 유효하지 않으면 예외를 발생시킵니다. 뱅 버전이 아닌 경우(예: save
, update
) false
를 반환하고 create
는 객체를 반환합니다.
유효성 검사 건너뛰기
다음 메서드는 유효성 검사를 건너뛰고 객체를 데이터베이스에 저장합니다. 주의해서 사용해야 합니다. 메서드 문서를 참조하여 자세히 알아보세요.
decrement!
decrement_counter
increment!
increment_counter
insert
insert!
insert_all
insert_all!
toggle!
touch
touch_all
update_all
update_attribute
update_attribute!
update_column
update_columns
update_counters
upsert
upsert_all
또한 save
에 validate: false
를 전달하면 유효성 검사를 건너뛸 수 있습니다. 이 기술은 주의해서 사용해야 합니다.
save(validate: false)
valid?
및 invalid?
Active Record 객체를 저장하기 전에 Rails는 유효성 검사를 실행합니다. 이러한 유효성 검사에 오류가 발생하면 Rails는 해당 객체를 저장하지 않습니다.
유효성 검사를 직접 실행할 수도 있습니다. valid?
는 유효성 검사를 트리거하고 오류가 없으면 true
를, 그렇지 않으면 false
를 반환합니다. 위에서 보았듯이:
class Person < ApplicationRecord validates :name, presence: true end
irb> Person.create(name: "John Doe").valid? => true irb> Person.create(name: nil).valid? => false
Active Record가 유효성 검사를 수행한 후 실패한 경우 errors
인스턴스 메서드를 통해 액세스할 수 있는 오류계속해서 Active Record 유효성 검사 가이드를 번역하겠습니다.
errors
컬렉션에 액세스할 수 있습니다. 정의상 객체가 유효하려면 이 컬렉션이 비어 있어야 합니다.
참고로 new
로 인스턴스화된 객체는 기술적으로 유효하지 않더라도 오류를 보고하지 않습니다. 왜냐하면 유효성 검사는 자동으로 create
또는 save
메서드와 같이 객체를 저장할 때만 실행되기 때문입니다.
class Person < ApplicationRecord validates :name, presence: true end
irb> p = Person.new => #<Person id: nil, name: nil> irb> p.errors.size => 0 irb> p.valid? => false irb> p.errors.objects.first.full_message => "Name can't be blank" irb> p = Person.create => #<Person id: nil, name: nil> irb> p.errors.objects.first.full_message => "Name can't be blank" irb> p.save => false irb> p.save! ActiveRecord::RecordInvalid: Validation failed: Name can't be blank irb> Person.create! ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
invalid?
는 valid?
의 반대입니다. 유효성 검사를 트리거하고 오류가 발견되면 true
를, 그렇지 않으면 false
를 반환합니다.
errors[]
특정 속성의 유효성을 확인하려면 errors[:attribute]
를 사용할 수 있습니다. 이는 :attribute
의 모든 오류 메시지를 반환합니다. 해당 속성에 오류가 없으면 빈 배열이 반환됩니다.
이 메서드는 유효성 검사가 실행된 후에만 유용합니다. 왜냐하면 오류 컬렉션만 검사하고 유효성 검사 자체는 트리거하지 않기 때문입니다. ActiveRecord::Base#invalid?
메서드와 다른 점은 전체 객체의 유효성만 확인하지 않고 개별 속성의 오류만 확인한다는 것입니다.
class Person < ApplicationRecord validates :name, presence: true end
irb> Person.new.errors[:name].any? => false irb> Person.create.errors[:name].any? => true
유효성 검사 오류에 대해서는 유효성 검사 오류 작업 섹션에서 더 자세히 다룰 것입니다.
유효성 검사 헬퍼
Active Record는 클래스 정의 내에서 직접 사용할 수 있는 많은 사전 정의된 유효성 검사 헬퍼를 제공합니다. 이러한 헬퍼는 일반적인 유효성 검사 규칙을 제공합니다. 유효성 검사에 실패할 때마다 객체의 errors
컬렉션에 오류가 추가되며, 이는 검증 중인 속성과 연결됩니다.
각 헬퍼는 임의의 수의 속성 이름을 허용하므로 단일 코드 줄로 동일한 유형의 유효성 검사를 여러 속성에 추가할 수 있습니다.
모두 :on
및 :message
옵션을 허용하며, 이는 각각 유효성 검사가 실행되어야 하는 시기와 실패할 경우 errors
컬렉션에 추가될 메시지를 정의합니다. :on
옵션은 :create
또는 :update
중 하나의 값을 가질 수 있습니다. 각 유효성 검사 헬퍼에는 기본 오류 메시지가 있습니다. 이러한 메시지는 :message
옵션이 지정되지 않은 경우 사용됩니다. 각 사용 가능한 헬퍼를 살펴보겠습니다.
정보: 사용 가능한 기본 헬퍼 목록을 보려면 ActiveModel::Validations::HelperMethods
를 참조하세요.
acceptance
이 메서드는 양식이 제출될 때 사용자 인터페이스의 체크박스가 선택되었는지 확인합니다. 일반적으로 사용자가 애플리케이션의 이용 약관에 동의해야 하거나, 어떤 텍스트를 읽었음을 확인해야 하는 등의 경우에 사용됩니다.
class Person < ApplicationRecord validates :terms_of_service, acceptance: true end
이 검사는 terms_of_service
가 nil
이 아닌 경우에만 수행됩니다. 이 헬퍼의 기본 오류 메시지는 “must be accepted”입니다. message
옵션을 사용하여 사용자 정의 메시지를 전달할 수도 있습니다.
class Person < ApplicationRecord validates :terms_of_service, acceptance: { message: 'must be abided' } end
또한 :accept
옵션을 받을 수 있으며, 이는 허용되는 값을 결정합니다. 기본값은 ['1', true]
이며 쉽게 변경할 수 있습니다.
class Person < ApplicationRecord validates :terms_of_service, acceptance: { accept: 'yes' } validates :eula, acceptance: { accept: ['TRUE', 'accepted'] } end
이 유효성 검사는 웹 애플리케이션에 매우 특정적이며 이 ‘승인'은 데이터베이스에 기록될 필요가 없습니다. 필드가 없는 경우 헬퍼가 가상 속성을 만듭니다. 데이터베이스에 필드가 존재하는 경우 accept
옵션을 true
또는 이를 포함하도록 설정해야 하며, 그렇지 않으면 유효성 검사가 실행되지 않습니다.
confirmation
이 헬퍼는 두 개의 텍스트 필드가 정확히 동일한 내용을 받아야 할 때 사용해야 합니다. 예를 들어, 이메일 주소나 비밀번호를 확인하고 싶을 수 있습니다. 이 유효성 검사는 확인해야 할 필드 이름에 “_confirmation"이 추가된 가상 속성을 만듭니다.
class Person < ApplicationRecord validates :email, confirmation: true end
뷰 템플릿에서 다음과 같이 사용할 수 있습니다.
<%= text_field :person, :email %> <%= text_field :person, :email_confirmation %>
참고: 이 검사는 email_confirmation
이 nil
이 아닌 경우에만 수행됩니다. 확인을 필수로 하려면 확인 속성에 대한 presence 검사를 추가해야 합니다(이 가이드의 presence 섹션에서 자세히 다룹니다):
class Person < ApplicationRecord validates :email, confirmation: true validates :email_confirmation, presence: true end
또한 :case_sensitive
옵션을 사용하여 확인 제약 조건이 대소문자를 구분할지 여부를 정의할 수 있습니다. 이 옵션의 기본값은 true
입니다.
class Person < ApplicationRecord validates :email, confirmation: { case_sensitive: false } end
이 헬퍼의 기본 오류 메시지는 "doesn’t match confirmation”입니다. message
옵션을 사용하여 사용자 정의 메시지를 전달할 수도 있습니다.
일반적으로 이 유효성 검사기를 사용할 때는 :if
옵션과 결합하여 초기 필드가 변경된 경우에만 “_confirmation” 필드를 검증하고 매번 레코드를 저장할 때 검증하지 않도록 하는 것이 좋습니다. 조건부 유효성 검사에 대해서는 나중에 더 자세히 다룹니다.
class Person < ApplicationRecord validates :email, confirmation: true validates :email_confirmation, presence: true, if: :email_changed? end
comparison
이 검사는 두 가능한 값 간의 비교를 검증합니다.
class Promotion < ApplicationRecord validates :end_date, comparison: { greater_than: :start_date } end
이 헬퍼의 기본 오류 메시지는 “failed comparison”입니다. message
옵션을 사용하여 사용자 정의 메시지를 전달할 수도 있습니다.
다음과 같은 옵션이 지원됩니다:
:greater_than
- 값이 지정된 값보다 커야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be greater than %{count}”입니다.:greater_than_or_equal_to
- 값이 지정된 값보다 크거나 같아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be greater than or equal to %{count}”입니다.:equal_to
- 값이 지정된 값과 같아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be equal to %{count}”입니다.:less_than
- 값이 지정된 값보다 작아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be less than %{count}”입니다.:less_than_or_equal_to
- 값이 지정된 값보다 작거나 같아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be less than or equal to %{count}”입니다.:other_than
- 값이 지정된 값과 달라야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be other than %{count}”입니다.
참고: 비교 옵션을 하나 이상 제공해야 합니다. 각 옵션은 값, 프로시저 또는 기호를 허용합니다. Comparable을 포함하는 모든 클래스를 비교할 수 있습니다.
format
이 헬퍼는 :with
옵션을 사용하여 지정된 정규 표현식과 일치하는지 여부로 속성 값을 검증합니다.
class Product < ApplicationRecord validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" } end
반대로 :without
옵션을 사용하면 지정된 정규 표현식과 일치하지 않아야 합니다.
어느 경우든 제공된 :with
또는 :without
옵션은 정규 표현식이거나 하나의 정규 표현식을 반환하는 프로시저 또는 람다여야 합니다.
기본 오류 메시지는 “is invalid”입니다.
경고. 문자열의 시작과 끝을 일치시키려면 \A
및 \z
를 사용하고, 줄의 시작/끝을 일치시키려면 ^
및 $
를 사용합니다. ^
및 $
를 잘못 사용하는 경우가 많아 제공된 정규 표현식에 이러한 앵커를 사용하는 경우 multiline: true
옵션을 전달해야 합니다. 대부분의 경우 \A
및 \z
를 사용해야 합니다.
inclusion
이 헬퍼는 속성 값이 주어진 집합에 포함되어 있는지 검증합니다. 이 집합은 임의의 열거 가능한 객체일 수 있습니다.네, 계속해서 Active Record 유효성 검사 가이드를 번역하겠습니다.
class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" } end
inclusion
헬퍼에는 값이 허용되는 집합을 지정하는 :in
옵션이 있습니다. :in
옵션에는 동일한 목적으로 사용할 수 있는 별칭인 :within
이 있습니다. 이전 예에서는 속성 값을 포함하는 사용자 정의 메시지를 보여주기 위해 :message
옵션을 사용했습니다. 메시지 옵션에 대한 전체 옵션은 메시지 문서를 참조하세요.
이 헬퍼의 기본 오류 메시지는 “is not included in the list”입니다.
exclusion
inclusion
의 반대는 exclusion
입니다!
이 헬퍼는 속성 값이 주어진 집합에 포함되지 않도록 검증합니다. 이 집합은 임의의 열거 가능한 객체일 수 있습니다.
class Account < ApplicationRecord validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." } end
exclusion
헬퍼에는 값이 허용되지 않는 집합을 지정하는 :in
옵션이 있습니다. :in
옵션에는 동일한 목적으로 사용할 수 있는 별칭인 :within
이 있습니다. 이 예에서는 속성 값을 포함하는 사용자 정의 메시지를 보여주기 위해 :message
옵션을 사용했습니다. 메시지 인수에 대한 전체 옵션은 메시지 문서를 참조하세요.
기본 오류 메시지는 “is reserved”입니다.
전통적인 열거형(Array 등) 대신 프로시저, 람다 또는 기호를 반환하는 열거형을 제공할 수 있습니다. 숫자, 시간 또는 날짜 범위인 경우 Range#cover?
를 사용하여 테스트하고, 그렇지 않은 경우 include?
를 사용합니다. 프로시저 또는 람다를 사용할 때 유효성 검사 중인 인스턴스가 인수로 전달됩니다.
length
이 헬퍼는 속성 값의 길이를 검증합니다. 다양한 옵션을 제공하므로 다른 방식으로 길이 제약 조건을 지정할 수 있습니다:
class Person < ApplicationRecord validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: 6..20 } validates :registration_number, length: { is: 6 } end
가능한 길이 제약 옵션은 다음과 같습니다:
:minimum
- 속성의 길이가 지정된 길이 미만일 수 없습니다.:maximum
- 속성의 길이가 지정된 길이를 초과할 수 없습니다.:in
(또는:within
) - 속성 길이가 지정된 간격에 포함되어야 합니다. 이 옵션의 값은 범위여야 합니다.:is
- 속성 길이가 지정된 값과 같아야 합니다.
기본 오류 메시지는 수행되는 길이 검사 유형에 따라 다릅니다. :wrong_length
, :too_long
및 :too_short
옵션과 길이 제약 조건과 관련된 숫자를 나타내는 %{count}
자리 표시자를 사용하여 이러한 메시지를 사용자 정의할 수 있습니다. 또한 :message
옵션을 사용하여 오류 메시지를 지정할 수 있습니다.
class Person < ApplicationRecord validates :bio, length: { maximum: 1000, too_long: "%{count} characters is the maximum allowed" } end
기본 오류 메시지는 복수형(예: “is too short (minimum is %{count} characters)”)입니다. 따라서 :minimum
이 1인 경우 사용자 정의 메시지를 제공하거나 presence: true
를 대신 사용해야 합니다. :in
또는 :within
의 하한이 1인 경우에도 사용자 정의 메시지를 제공하거나 presence
를 먼저 호출해야 합니다.
참고: :minimum
및 :maximum
옵션을 함께 사용할 수 있는 것 외에는 한 번에 하나의 제약 옵션만 사용할 수 있습니다.
numericality
이 헬퍼는 속성에 숫자 값만 있는지 검증합니다. 기본적으로 선택적 부호 다음에 정수 또는 부동 소수점 숫자가 오는 것을 허용합니다.
정수 숫자만 허용하려면 :only_integer
를 true
로 설정하세요. 그러면 다음과 같은 정규 표현식을 사용하여 속성 값을 검증합니다.
/\A[+-]?\d+\z/
그렇지 않으면 값을 Float
로 변환하려고 시도합니다. Float
은 열의 정밀도 값 또는 최대 15자리를 사용하여 BigDecimal
로 캐스팅됩니다.
class Player < ApplicationRecord validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end
:only_integer
의 기본 오류 메시지는 “must be an integer”입니다.
:only_integer
외에도 이 헬퍼는 :only_numeric
옵션을 허용하며, 이는 값이 Numeric
인스턴스여야 하고 문자열인 경우 구문 분석을 시도합니다.
참고: 기본적으로 numericality
는 nil
값을 허용하지 않습니다. allow_nil: true
옵션을 사용하여 허용할 수 있습니다. 정수 및 float 열의 경우 빈 문자열이 nil
로 변환된다는 점에 유의하세요.
어떤 옵션도 지정되지 않은 경우 기본 오류 메시지는 “is not a number”입니다.
허용 가능한 값에 대한 추가 제약 조건을 추가할 수 있는 많은 옵션이 있습니다:
:greater_than
- 값이 지정된 값보다 커야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be greater than %{count}”입니다.:greater_than_or_equal_to
- 값이 지정된 값보다 크거나 같아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be greater than or equal to %{count}”입니다.:equal_to
- 값이 지정된 값과 같아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be equal to %{count}”입니다.:less_than
- 값이 지정된 값보다 작아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be less than %{count}”입니다.:less_than_or_equal_to
- 값이 지정된 값보다 작거나 같아야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be less than or equal to %{count}”입니다.:other_than
- 값이 지정된 값과 달라야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be other than %{count}”입니다.:in
- 값이 지정된 범위에 포함되어야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be in %{count}”입니다.:odd
- 값이 홀수여야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be odd”입니다.:even
- 값이 짝수여야 함을 지정합니다. 이 옵션의 기본 오류 메시지는 “must be even”입니다.
presence
이 헬퍼는 지정된 속성이 비어 있지 않도록 검증합니다. [Object#blank?
][]