Javascript

JavaScript Truthy와 Falsy 완벽 정리: 헷갈리는 타입 변환, 이제 끝내자!

드리프트2 2025. 1. 8. 21:06

JavaScript Truthy와 Falsy 완벽 정리: 헷갈리는 타입 변환, 이제 끝내자!

JavaScript를 처음 접했을 때, truthy와 falsy 개념이 단순하다고 생각했는데, 조금만 깊게 들어가면 상황이 복잡해지는 걸 느꼈습니다.

 

"false는 falsy, true는 truthy다."라는 기본 개념은 쉽지만, edge case(경계 사례)는 정말 헷갈리는데요.

 

이번 글에서는 JavaScript의 truthy와 falsy 개념을 깊게 파헤치고, 타입 강제 변환(type coercion)으로 생길 수 있는 문제들을 예제로 정리해 보겠습니다.


Truthy와 Falsy란?

JavaScript에서는 특정 값들이 "참(true)"처럼 평가되거나 "거짓(false)"처럼 평가됩니다.

 

이를 각각 truthy와 falsy라고 부르는데요. falsy 값은 딱 8가지뿐이고, 나머지는 모두 truthy 값입니다.


JavaScript의 Falsy 값 8가지

다음은 JavaScript에서 falsy로 평가되는 값들입니다:

false       // 불리언 false
0           // 숫자 0
-0          // 숫자 음수 0
0n          // BigInt 0
""          // 빈 문자열
null        // 아무 값도 없음
undefined   // 초기화되지 않은 값
NaN         // Not a Number

⚠️ 특별한 경우: document.all

document.all은 JavaScript에서 약간 특이한 존재인데요. 과거 Internet Explorer에서 DOM 요소에 접근하기 위해 사용되던 API입니다. 이 값은 객체(object)이지만, JavaScript는 역사적 이유로 이 값을 falsy로 취급합니다.


헷갈리는 Edge Case들

JavaScript의 타입 강제 변환(type coercion)은 종종 예기치 못한 결과를 만들어냅니다.

 

대표적인 예제를 살펴볼까요?

1️⃣ 배열의 비교

// 배열
[] == false       // true - 빈 배열은 truthy지만, 비교 시 0으로 변환
[1, 2] == true    // false - 비어있지 않은 배열은 truthy지만 true와 같지 않음
  • [] == falsetrue로 평가되는 이유는 JavaScript가 배열을 먼저 원시 값(primitive value)으로 변환하기 때문입니다. 빈 배열([])은 빈 문자열("")로 변환되고, 이 빈 문자열은 숫자 0으로 변환됩니다. 결과적으로 0 == falsetrue가 됩니다.
  • 반대로, [1, 2] == truefalse로 평가됩니다. [1, 2]는 문자열 "1,2"로 변환되고, 이를 숫자로 변환하려고 하면 NaN이 됩니다. NaN == truefalse로 평가되죠.

2️⃣ 객체의 비교

// 객체
{} == false           // false - 빈 객체는 truthy
new Boolean(false) == false  // true - 그러나 객체 자체는 truthy!
  • {}(빈 객체)는 truthy한 값입니다. 그러나 == 비교에서 빈 객체는 [object Object]라는 문자열로 변환됩니다. 이 문자열은 숫자로 변환할 수 없어서 NaN이 되고, NaN == falsefalse가 됩니다.
  • new Boolean(false)는 불리언 값 false를 감싸는 객체를 생성하는데요. 이 객체는 truthy한 값이지만, == 비교에서 false와 같게 평가됩니다. 이처럼 감싸진 객체와 원시 값의 비교는 혼란을 줄 수 있으니 주의해야 합니다.

3️⃣ 문자열의 비교

// 문자열
"0" == false    // true - 문자열 "0"은 숫자 0으로 변환
"false" == false // false - 문자열 "false"는 숫자로 변환되지 않음
  • "0" == falsetrue로 평가됩니다. "0"은 숫자 0으로 변환되고, 0 == falsetrue가 됩니다.
  • "false" == falsefalse로 평가됩니다. "false"는 숫자로 변환되지 못하고 NaN이 되며, NaN == falsefalse가 됩니다.

타입 강제 변환을 막는 방법

위와 같은 혼란을 피하기 위해, 엄격한 동등 연산자(===)를 사용하는 것이 좋습니다.

 

엄격한 동등 연산자는 타입 강제 변환 없이 값과 타입이 모두 같아야만 true로 평가됩니다.


실제 사례: 인증 함수 개선하기

타입 강제 변환이 문제를 일으킬 수 있는 실제 사례를 살펴보겠습니다.

문제 있는 코드

function isUserLoggedIn(user) {
  return user.loggedIn;  // 오류 발생 가능
}
  • userundefined라면, user.loggedIn을 접근하려는 시도에서 TypeError가 발생합니다.
  • loggedIn 값이 0, "", null 등 falsy 값이라면, 의도와 다르게 false로 평가될 가능성이 있습니다.

개선된 코드

function isUserLoggedIn(user) {
  if (!user) return false;
  return Boolean(user.loggedIn); // 명시적으로 변환
}
  • 먼저 user가 존재하는지 확인해 TypeError를 방지합니다.
  • Boolean()을 사용해 user.loggedIn의 값을 명시적으로 불리언 값으로 변환합니다.

자주 사용하는 Best Practice 예제

1️⃣ 유효한 역할(role) 반환

function getUserRole(user) {
  if (!user?.role) {
    return 'guest';
  }
  return user.role;
}
  • ?.(옵셔널 체이닝)을 사용해 useruser.rolenull 또는 undefined인 경우를 안전하게 처리합니다.
  • !를 사용해 falsy 값을 명확히 구분합니다.

2️⃣ 배열 길이 검사

function hasActiveItems(items) {
  return Boolean(items?.length);
}
  • itemsundefined거나 null인 경우를 처리합니다.
  • 배열이 비어있으면 false를 반환합니다.

3️⃣ 숫자 검증

function isValidScore(score) {
  return typeof score === 'number' && !Number.isNaN(score);
}
  • 숫자인지 확인한 뒤, NaN 여부를 추가로 체크해 정확성을 높입니다.

4️⃣ 안전한 설정 객체 반환

function getConfig(settings) {
  const config = {
    theme: settings?.theme ?? 'default',
    timeout: settings?.timeout ?? 5000,
    retries: settings?.retries ?? 3
  };
  return config;
}
  • ?.??를 조합해 null 또는 undefined일 경우에만 기본값을 설정합니다.
  • ||를 사용하는 경우와 달리, 0이나 빈 문자열 같은 falsy 값도 그대로 유지합니다.

Key Takeaways

JavaScript의 truthy와 falsy 개념은 간단해 보이지만, 타입 강제 변환이 개입되면 예상치 못한 결과를 낳을 수 있습니다. 다음 팁을 기억하세요:

// 항상 엄격한 동등 연산자 사용
value === false    // value == false 대신

// 불리언 값으로 명시적 변환
Boolean(someValue) // !!someValue 대신

// 기본값 설정 시 || 대신 ??
const volume = settings?.volume ?? 100;