학습 자료

형변환 — 자바스크립트가 몰래 값을 바꾼다


Article

자바스크립트에는 친절한 구석이 있다. 숫자와 문자열을 섞어 써도 에러를 내지 않고, 알아서 하나로 맞춰준다.

그 친절함이 바로 초보자를 가장 많이 울리는 지점이다.

console.log("1" + 1);
console.log("1" - 1);
콘솔
  • '11'
  • 0

같은 값인데 연산자만 바꿨더니 결과가 완전히 달라졌다. 이번 강의는 이 현상을 파헤친다.

왜 이런 일이 벌어지나

"1"은 문자열이고 1은 숫자다. 둘은 서로 다른 타입이라 원래 연산할 수 없다. 그런데 자바스크립트는 에러를 내는 대신 한쪽을 다른 쪽 타입으로 바꿔버린다. 이걸 암묵적 형변환이라 부른다.

문제는 어느 쪽으로 바꿀지가 연산자마다 다르다는 것이다.

연산자가 방향을 정한다

+

문자열이 이긴다

  • 한쪽이 문자열이면 이어붙이기
  • "1" + 1"11"
  • 1 + "1""11"
  • 덧셈과 연결을 겸하는 유일한 연산자

- * / %

숫자가 이긴다

  • 무조건 숫자로 바꿔 계산
  • "1" - 10
  • "6" * "2"12
  • 바꿀 수 없으면 NaN

+만 유별나다. 덧셈이면서 동시에 문자열 연결이기도 해서, 문자열을 만나면 연결 쪽으로 기운다.

console.log(1 + 2 + "3");
console.log("1" + 2 + 3);
콘솔
  • '33'
  • '123'

왼쪽부터 차례로 계산되기 때문에 결과가 갈린다.

1 + 2 + "3" 의 계산 과정
  1. 01

    1 + 2 를 먼저 한다

    둘 다 숫자라 그냥 더한다. 결과는 3 (숫자).

    1 + 23
  2. 02

    3 + "3" 을 한다

    한쪽이 문자열이니 숫자 3 을 문자열 "3" 으로 바꿔 이어붙인다.

    3 + "3""33"

"1" + 2 + 3은 첫 단계부터 문자열이라 계속 이어붙어서 "123"이 된다.

명시적으로 바꾼다

자바스크립트가 알아서 바꾸게 두지 말고, 직접 바꾸는 게 안전하다.

console.log(Number("42"));
console.log(Number("42px"));
console.log(parseInt("42px"));
console.log(String(42));
console.log(Boolean(1));
콘솔
  • 42
  • NaN
  • 42
  • '42'
  • true

Number()는 통째로 숫자여야 하고, parseInt()앞에서부터 읽다가 숫자가 아니면 멈춘다. "42px" 같은 CSS 값을 다룰 때 유용하다.

몇 가지는 결과가 직관과 어긋난다. 외우기보다 필요할 때 찾아보는 편이 낫다.

원래 값Number()Boolean()
"" (빈 문자열)0false
" " (공백)0true
"0"0true
null0false
undefinedNaNfalse
[]0true
{}NaNtrue

공백 한 칸 " "은 숫자로는 0인데 boolean으로는 true다. "0"도 문자열로서는 비어 있지 않으니 true다.

truthy 와 falsy

6강에서 예고한 부분이다. 조건문이나 &&, ||에 넣으면 자바스크립트는 값을 boolean으로 바꿔 판단한다.

거짓으로 취급되는 값은 딱 여섯 개뿐이다.

falsy 값무엇인가
falseboolean 거짓
0숫자 영 (-0 포함)
""빈 문자열
null일부러 비운 값
undefined아직 없는 값
NaN숫자가 아님

이 여섯 개만 외우면 된다. 나머지는 전부 truthy다.

if ([]) console.log("빈 배열은 참이다");
if ({}) console.log("빈 객체도 참이다");
if ("0") console.log("문자열 0 도 참이다");
if (-1) console.log("음수도 참이다");
콘솔
  • 빈 배열은 참이다
  • 빈 객체도 참이다
  • 문자열 0 도 참이다
  • 음수도 참이다

== 를 쓰면 안 되는 이유

드디어 본론이다. 자바스크립트에는 같음을 확인하는 연산자가 둘이다.

두 개의 같음

==

동등 — 타입을 맞춘 뒤 비교

  • 타입이 다르면 몰래 변환한다
  • 변환 규칙이 복잡하고 예외가 많다
  • 숙련자도 결과를 못 맞춘다
  • 쓰지 않는다

===

일치 — 타입까지 같아야 참

  • 타입이 다르면 바로 거짓
  • 규칙이 하나뿐이라 예측된다
  • 읽는 사람이 헷갈리지 않는다
  • 항상 이걸 쓴다

==가 얼마나 이상한지 직접 본다.

console.log(1 == "1");
console.log(0 == "");
console.log(0 == false);
console.log("0" == false);
console.log(null == undefined);
console.log([] == false);
콘솔
  • true
  • true
  • true
  • true
  • true
  • true

전부 true다. 빈 배열이 false와 같다는 결론에 이르면 이미 정상적인 프로그래밍이 아니다.

===로 바꾸면 전부 뒤집힌다.

console.log(1 === "1");
console.log(0 === "");
console.log(null === undefined);
console.log([] === false);
콘솔
  • false
  • false
  • false
  • false

타입이 다르면 볼 것도 없이 거짓이다. 이해하기 쉽고, 놀랄 일이 없다.

NaN 은 자기 자신과도 다르다

마지막 함정이다.

console.log(NaN === NaN);

const result = Number("사과");
console.log(result === NaN);
콘솔
  • false
  • false

NaN은 "숫자가 아닌 무언가"인데, 그런 것들이 서로 같다고 할 근거가 없다. 그래서 자기 자신과 비교해도 거짓이다.

값이 NaN인지 확인하려면 전용 함수를 쓴다.

const result = Number("사과");

console.log(Number.isNaN(result));
console.log(Number.isNaN("사과"));
콘솔
  • true
  • false

Number.isNaN()은 진짜 NaN일 때만 참이다. 문자열 "사과"는 숫자로 바꿀 수 없을 뿐 그 자체가 NaN은 아니므로 거짓이다.

정리하면

  • 타입이 다른 값을 연산하면 자바스크립트가 알아서 한쪽을 바꾼다.
  • +는 문자열 쪽으로, - * /는 숫자 쪽으로 기운다.
  • 입력창의 값은 항상 문자열이다. 계산 전에 Number()로 바꾼다.
  • falsy는 false 0 "" null undefined NaN 여섯 개뿐이고 나머지는 truthy다. 빈 배열도 참이다.
  • ==는 쓰지 않는다. 항상 ===를 쓴다.
  • NaN은 자기 자신과도 다르다. Number.isNaN()으로 확인한다.

여기까지가 값을 다루는 이야기다. 다음 강의부터는 그 값으로 길을 나누는 법, 조건문을 배운다.

형변환 — 자바스크립트가 몰래 값을 바꾼다 — 디코드랩(DCODELAB)