Ruby와 Rails에는 값의 존재 여부나 객체의 상태를 확인할 때 사용할 수 있는 여러 메서드가 있습니다. Ruby는 기본적으로 #nil?과 #empty?를 제공하고, Rails의 ActiveSupport는 여기에 #blank?와 #present?를 추가합니다. 이 메서드들은 각각 고유한 방식으로 동작하기 때문에, 각 메서드가 데이터를 어떻게 평가하는지 정확히 이해하는 것이 중요합니다. 잘못된 메서드를 선택하면 코드에서 예상치 못한 결과를 초래할 수 있습니다.
이 글에서는 이 네 가지 메서드를 다시 한번 정리해 보겠습니다. 각 메서드를 사용했을 때 어떤 조건이 참이 되고 거짓이 되는지, 어떤 종류의 객체에 적용할 수 있는지 살펴보고, 마지막에는 북마크해 두면 유용한 치트 시트까지 준비했습니다.
#nil?
#nil?은 Ruby의 Object 클래스에 정의된 메서드입니다. 모든 클래스가 이 클래스를 상속하기 때문에 #nil?은 어떤 객체에든 사용할 수 있습니다. nil(NilClass의 인스턴스)에 대해서는 true를 반환하고, 그 외의 모든 값에 대해서는 false를 반환합니다.
nil.nil?
=> true
true.nil?
=> false
5.nil?
=> false
"".nil?
=> false
[].nil?
=> false#empty?
#empty?는 문자열, 배열, 해시, 셋(Set)에 사용할 수 있는 메서드입니다. 객체의 길이가 0일 때 true를 반환합니다.
문자열에 사용하면 빈 문자열인 경우 true를 반환합니다.
"".empty?
=> true주의할 점은 공백 문자도 하나의 문자로 취급된다는 것입니다. 따라서 공백만 포함된 문자열에 #empty?를 호출하면 false가 반환됩니다. 이 때문에 텍스트 유효성 검사에는 최선의 선택이 아닐 수 있습니다. 예를 들어 이 메서드 하나만으로 사용자 입력을 검증한다면, 공백으로만 이루어진 입력까지 정상적인 값으로 처리하게 될 수 있습니다.
" ".empty?
=> false
"\t\n".empty?
=> false배열, 해시, 셋의 경우에는 요소가 하나도 없을 때 true를 반환합니다.
[].empty?
=> true
{}.empty?
=> true
require 'set'
Set.new.empty?
=> true반면 #empty?를 nil이나 이 메서드가 정의되어 있지 않은 다른 객체에 호출하면 NoMethodError 예외가 발생합니다. 따라서 안전하게 사용하려면 아래처럼 추가 검사를 넣어 프로그램이 크래시하지 않도록 방어 코드를 작성해야 합니다.
if !my_var.nil? && !my_var.empty?
# my_var 사용
end이런 경우에는 Rails의 #blank? 메서드가 더 나은 선택입니다. 바로 살펴보겠습니다.
#blank?
#blank?는 Rails(정확히는 ActiveSupport)가 제공하는 메서드로, 모든 객체에 사용할 수 있습니다.
문자열의 경우 빈 문자열뿐 아니라 공백 문자만 포함된 문자열에 대해서도 true를 반환합니다.
"".blank?
=> true
" ".blank?
=> true
"\n\t".blank?
=> true배열, 해시, 셋에는 #empty?와 동일하게 동작하여, 요소가 없으면 true를 반환합니다.
[].blank?
=> true
{}.blank?
=> true
Set.new.blank?
=> true
[nil].blank?
=> false
["", ""].blank?
=> false
person = {:firstName => "John", :lastName => "Doe"}
person.blank?
=> falsetrue에 대해서는 false를 반환하고, 모든 falsy 값(nil, false)에 대해서는 true를 반환합니다.
true.blank?
=> false
false.blank?
=> true
nil.blank?
=> true정리하면, #blank?는 모든 객체에서 호출할 수 있어 NoMethodError가 발생하지 않고, 공백만 있는 문자열도 걸러내기 때문에 데이터 검증 시 #empty?보다 훨씬 안전하고 편리합니다.
#present?
#present? 역시 Rails가 제공하는 메서드로, #blank?와 정확히 반대되는 동작을 합니다.
!my_var.blank? == my_var.present?
=> true몇 가지 예시를 보겠습니다.
"".present?
=> false
" ".present?
=> false
[].present?
=> false
nil.present?
=> false
true.present?
=> true
false.present?
=> false
{}.present?
=> false
person = {:firstName => "John", :lastName => "Doe"}
person.present?
=> true
5.present?
=> true앞서 #blank?에서 설명한 것과 같은 이유로, 검증 목적으로는 #empty?보다 이 메서드를 사용하는 것이 더 좋습니다.
마무리 및 치트 시트
이 메서드들은 이름이 비슷해서 헷갈리기 쉽습니다. 아래 치트 시트를 북마크해 두고 필요할 때마다 확인해 보세요.
| 값 | #nil? | #empty? | #blank? | #present? |
|---|---|---|---|---|
5 | false | NoMethodError | false | true |
"" | false | true | true | false |
" " | false | false | true | false |
"\t\n" | false | false | true | false |
[] | false | true | true | false |
["a"] | false | false | false | true |
{} | false | true | true | false |
{a: "b"} | false | false | false | true |
Set.new | false | true | true | false |
nil | true | NoMethodError | true | false |
true | false | NoMethodError | false | true |
false | false | NoMethodError | true | false |
결국 #nil?, #empty?, #blank?, #present? 중 무엇을 선택할지는 상황에 따라 달라집니다. 예를 들어 사용자를 호칭할 이름이 있는지 확인하는 용도라면 #present?가 적합할 수 있습니다. 다만 #blank?와 #present?는 모든 객체에 대해 불리언 값을 반환하기 때문에, 값의 타입이 잘못되었을 때도 에러를 발생시키지 않고 조용히 통과시켜 버그를 숨길 수 있다는 점은 유의해야 합니다.
Ruby에서 값을 확인하는 다양한 방법에 대한 기억을 새롭게 하는 데 도움이 되었기를 바랍니다. 궁금한 점이나 의견이 있다면 @AppSignal로 언제든지 알려주세요.