본문 바로가기
JAVASCRIPT

[JS][강제변환] 동등비교 : 문자열 -> 숫자, * -> 불리언

by KBS 2022. 2. 12.
728x90

동등비고 : 문자열 -> 숫자

== 강제변환 예제를 살펴보자.

var a = 42;
var b = "42";

a === b; // false
a == b; // true

 

예상대로 a === b는 false다. 강제변환이 허용되지 않는 데다 42와 "42"는 그냥 봐도 다른 값이기 때문이다.

하지만 느슨한 동등 비교 a == b에서는 피연산자의 타입이 다르면, 비교 알고리즘에 의해 한쪽 또는 양쪽 피연산자 값이 알아서 암시적으로 강제변환 된다.

그런데 정확히 어떻게 강제변환이 일어나는 걸까? 42가 문자열로 바뀌어 a가 되는 걸까 아니면 "42"가 숫자로 바뀌어 b가 되는 걸까?

ES5 11.9.3.4-5 원문을 보면

  1. Type(x)가 Number고 Type(y)가 String이면, x == ToNumber(y) 비교 결과를 반환한다.
  2. Type(x)가 String고 Type(y)가 Number이면, ToNumber(x) == y 비교 결과를 반환한다.

명세를 보니 비교 전 먼저 "42" 값이 숫자로 강제변환 된다는 것을 분명히 알 수 있다. 강제변환은 이미 앞서 설명했던 ToNumber 추상 연산이 담당하고 결괏값은 42이므로 두 42는 명백히 동등하다.

동등비교 * -> 불리언

어던 값을 true/false와 직접 비교하려고 하면 느슨한 동등비교의 숨겨진, 가장 끔찍한 강제변환 함정에 빠지게 될 것이다.

var a = "42";
var b = true;

a == b; // false

 

잠깐 이게뭐지? "42"는 truthy 값이니 == 비교하면 true 아닌가? 겨로가가 반대인 이유는 단순하면서도 꽤 까다롭다. 참으로 오해하기 쉬운 문제인데 많은 개발자가 관심을 갖고 제대로 이해하려고 하지 않는다.

ES5 11.9.3.6-7를 살펴보면

  1. Type(x)이 불리언이면 ToNumbe(x) == y의 비교 결과를 반환한다.
  2. Type(y)이 불리언이면 x == ToNumber(y)의 비교 결과를 반환한다.

자, 다음의 코드를 보자.

var x = true;
var y = "42";

x == y; // false

 

Type(x)은 불리언이므로 ToNumber(x) -> 1로 강제변환 된다. 다라서 1 == "42"이 되는데 타입이 상이하므로 알고리즘을 수행한다. 결국 "42"는 42로 바뀌어 1 == 42 --> false가 된다.

x, y 순서를 바꾸어도 결과는 같다.

var x = "42";
var y = true;

x == y; // false

 

이번에는 Type(y)가 불리언이므로 ToNumber(y)는 0이고 "42" == 0 ---> (재귀적으로)42 == 0 ---> false다.

결론적으로 "42"는 == true도, == false도 아니다. 언뜻보면 말도 안되는 소리다. 어떻게 truthy도 falsy도 아닌 값이 있단 말인가?

하지만 바로 이게 문제다. 우리는 지금 완전히 틀린 질문을 하고 있다. "42"는 분명 truthy 값이지만 "42" == true는 우리의 두뇌가 어떻게 반응하든 상관없이 불리언 평가나 강제변환을 전혀 하지 않는다. "42"가 불리언으로 강제변환 되는 것이 아니라 도리어 true가 1로 강제변환 되고 그 후 "42"가 42로 강제변환 된다.

이런 방식이 맘에 들지 않더라도 어쨌든 ToBoolean은 전혀 개입하지 않으며, "42" 값 자체의 truthy/falsy 여부는 == 연산과는 전혀 무관하다.

==의 비교 알고리즘이 각 타입 조합별로 어떻게 작동하는가가 중요하다. 아까도 보았지만 ==의 피연산자 한쪽이 불리언 값이면 예외 없이 그 값이 먼저 숫자로 강제변환 된다.

나만 이상한건가 싶다면, 절대 그렇지 않다. 어떠한 일이 있더라고, 절대로, 두 번 다시 == true, == false 같은 코드는 쓰지 말라고 개인적으로 강권하고 싶다.

하지만 쓰지 말라고 한 연산자는 ==이지, ===이 아니다. === true. === false는 강제변환을 허용하지 않기에 ToNumber 강제변환 따위는 신경 쓰지 않아도 된다.

var a = "42";

// 실패한다
if (a == true) {
  // ...
}

// 이것도 실패
if (a === true) {
  // ...
}

// 암시적으로 작동
if (a) {
  // ...
}

// 명시적으로 작동
if (!!a) {
  // ...
}

// 명시적으로 작동
if (Boolean(a)) {
  // ...
}

 

그냥 앞으로 == true, == false 같은 코드를 안쓰면 윌는 골치 아픈 truthy/falsy 문제에서 해방될 것이다.


참고

  • You Don't Know JS - 타입과 문법, 스코프와 클로저( 한빛 미디어 )
728x90

댓글