Menu

자바스크립트 타입 변환: 암시적 vs 명시적 총정리

자바스크립트가 값의 타입을 알아서 바꿔주는 방식 — 많이들 헷갈리는 암시적 변환 규칙과, 가급적 써야 하는 명시적 변환, 그리고 각각이 언제 발동되는지 정리했습니다.

형 변환의 두 가지 종류

자바스크립트는 타입에 관대한 언어입니다. 연산자에 "엉뚱한" 타입의 값이 들어와도 에러를 던지는 대신 알아서 변환해 버리죠. 이걸 형 변환(coercion) 이라고 부르는데, 크게 두 가지로 나뉩니다.

  • 명시적 형 변환(Explicit) — 직접 변환을 요청하는 경우입니다: Number("42"), String(99), Boolean(value).
  • 암시적 형 변환(Implicit) — 변환 코드를 쓰지 않았는데 연산자가 알아서 변환해 버리는 경우입니다: "5" - 2, "" == 0, if (value).

둘 다 내부적으로는 같은 변환 규칙을 따릅니다. 차이는 "누가" 변환을 결정했느냐는 것뿐이죠. 여러분을 헷갈리게 만드는 거의 모든 자바스크립트 결과 — "5" + 1 === "51", [] == false, null == undefined 같은 — 는 전부 암시적 형 변환이 뒤통수를 치면서 생기는 일입니다.

index.js
Output
Click Run to see the output here.

이걸 이렇게 기억하면 편합니다. Number(...)String(...)처럼 직접 적으면 내가 뭘 원하는지 명확하죠. 그런데 그렇게 안 쓰면, 각 연산자마다 알아서 판단해 버립니다 — 버그는 바로 그 "알아서"에서 튀어나와요.

변환 대상이 되는 세 가지 타입

자바스크립트 타입 변환은 항상 세 가지 원시 타입 중 하나를 향합니다: string, number, boolean. (엄밀히는 bigint도 있지만, 다른 타입에서 자동으로 bigint로 변환되는 일은 없습니다.) 나머지 규칙들은 전부 "이 연산자가 어떤 타입을 목표로 하느냐"에서 자연스럽게 따라옵니다.

index.js
Output
Click Run to see the output here.

문자열로의 형변환은 비교적 너그러운 편입니다. 모든 값은 나름의 문자열 표현을 가지고 있거든요. 다만 객체를 문자열로 바꾸면 별로 쓸모없는 "[object Object]"가 나온다는 점은 기억해 두세요. 객체를 문자열과 그냥 이어 붙여서 ("user: " + user) 출력했을 때 원하는 결과가 거의 나오지 않는 이유가 바로 이것입니다. 이럴 땐 JSON.stringify를 쓰거나, 템플릿 리터럴로 필요한 필드만 꺼내 쓰는 편이 훨씬 낫습니다.

숫자로의 형변환

숫자로의 형변환은 훨씬 까다롭습니다. 문자열이 실제로 숫자처럼 생겨야 하고, 그렇지 않으면 바로 NaN이 떨어집니다:

index.js
Output
Click Run to see the output here.

괄호 안에 있는 의외의 동작들은 꼭 외워두는 게 좋습니다.

  • 빈 문자열이나 공백만 있는 문자열은 NaN이 아니라 0이 됩니다.
  • null0이 되지만, undefinedNaN이 됩니다.
  • 빈 배열은 0이 되고, 원소가 하나인 배열은 그 원소 자체 를 변환하며, 원소가 여러 개인 배열은 NaN이 됩니다.

문자열에 불필요한 문자가 섞여 있을 때 그 안에서 숫자만 뽑아내고 싶다면, Number 대신 parseIntparseFloat을 쓰세요.

index.js
Output
Click Run to see the output here.

parseInt을 쓸 때는 항상 진법(10)을 함께 넘겨주세요. 이걸 빠뜨리면 "0x"로 시작하는 문자열이 16진수로 파싱되는데, 십중팔구 의도한 동작이 아닙니다.

불리언으로의 형변환

불리언 형변환은 세 가지 중에서 가장 단순합니다. 딱 정해진 몇 개의 값만 false로 바뀌고, 나머지는 전부 true가 되죠.

falsy한 값은 다음과 같습니다:

  • false
  • 0, -0, 0n
  • "" (빈 문자열)
  • null
  • undefined
  • NaN

이게 전부입니다. 이 목록에 없는 값은 전부 truthy예요. "false", "0", [], {} 같은 값들도 모두 truthy로 취급됩니다.

index.js
Output
Click Run to see the output here.

파이썬에서 넘어온 분들이 가장 많이 당하는 부분이 바로 빈 배열과 빈 객체입니다. 파이썬에서는 빈 컬렉션이 falsy지만, 자바스크립트에서는 truthy예요. 그래서 "이 배열이 비어 있나?"를 확인하려면 arr.length === 0처럼 명시적으로 검사해야 합니다.

불리언 변환(자바스크립트 불리언 변환)은 불리언 값이 와야 하는 자리에 다른 값이 오면 자동으로 일어납니다. if (...), while (...), 삼항 연산자 ? :, 그리고 논리 연산자 &&, ||, !가 대표적인 경우죠.

+ 연산자는 예외입니다

대부분의 산술 연산자는 피연산자를 숫자로 강제 변환합니다. 그런데 +는 좀 다릅니다. 어느 한쪽이라도 문자열이면 +는 문자열 연결로 동작하고, 그렇지 않을 때만 숫자 덧셈을 수행해요.

index.js
Output
Click Run to see the output here.

평가는 왼쪽에서 오른쪽으로 진행됩니다. 1 + 2 + "3"은 먼저 1 + 2 = 3을 계산한 뒤, 3 + "3" = "33"이 됩니다. 반면 "1" + 2 + 3은 처음부터 문자열로 시작하기 때문에 끝까지 문자열로 남습니다. "12"가 되고, 다시 "123"이 되는 식이죠.

이래서 +로 문자열을 조립하는 방식은 취약합니다. 템플릿 리터럴을 쓰면 이런 문제가 아예 없습니다:

index.js
Output
Click Run to see the output here.

템플릿 리터럴은 count + 1을 별도의 숫자 연산으로 먼저 계산한 뒤 그 결과를 문자열에 끼워 넣습니다. 엉뚱한 형변환이 일어날 일이 없죠.

명시적 형변환을 쓰자

타입을 바꿔야 한다면 명확하게 드러내는 게 좋습니다. 글자 몇 개 더 치는 대신 다음 사람이 코드를 읽을 때 모호함이 싹 사라집니다:

index.js
Output
Click Run to see the output here.

불리언도 마찬가지예요. !!value도 잘 동작하고 흔히 쓰이는 방식이지만, Boolean(value)는 의도가 훨씬 분명하게 드러납니다:

index.js
Output
Click Run to see the output here.

합리적인 원칙 하나: 애플리케이션 로직에서는 명시적 형변환을 쓰고, 짧은 형태(+x, !!x)는 간결함이 중요하면서 의도가 맥락상 명확한 곳에만 아껴 쓰세요.

== 연산자는 타입 강제 변환에 깊이 의존한다

자바스크립트에서 타입 변환 관련 예상치 못한 동작이 가장 많이 터지는 곳이 바로 동등 연산자입니다. ==는 비교하기 전에 강제로 형을 맞추고, ===는 그렇지 않거든요.

index.js
Output
Click Run to see the output here.

위의 true 값들은 하나같이 대부분의 개발자가 외워서 설명하기 힘든 여러 단계의 형변환 체인을 거친 결과입니다. 바로 이게 문제죠 — 우연히 동작하는 코드는 언젠가 반드시 터집니다. 전체 동등 비교 규칙은 다음 페이지에서 자세히 다루고, 지금은 이 한 가지만 기억하세요: 기본은 ===로 쓰고, ==nullundefined를 한 번에 잡아내는 x == null 관용구에서만 예외적으로 사용합니다.

실전에서 합쳐 보기

형변환이 도움이 되는 경우와 오히려 발목을 잡는 경우를 한 번에 보여주는 예제입니다:

index.js
Output
Click Run to see the output here.

두 번째 호출을 잘 보세요. Number("")NaN이 아니라 0을 반환합니다. 그래서 parsePrice("")0을 돌려주는데, 이 함수를 쓰는 입장에서는 보통 원하는 동작이 아니죠. 빈 문자열을 아예 거부하고 싶다면 다음처럼 명시적으로 체크를 추가해 주세요:

index.js
Output
Click Run to see the output here.

어떤 값이 0으로 변환되고, 어떤 값이 NaN으로 변환되는지 알아두면, 나중에 추적하기 힘든 버그를 미리 막을 수 있습니다.

핵심 정리

  • 자바스크립트 타입 변환은 연산자에 따라 문자열, 숫자, 불리언 중 하나로 값을 바꿉니다.
  • + 연산자는 문자열이 하나라도 끼면 문자열 이어붙이기가 되고, 그 외 산술 연산자는 전부 숫자로 형변환합니다.
  • falsy 값은 정해진 몇 개뿐이니 아예 외워두는 게 낫습니다. 그 외의 값은 전부 truthy이고, []{}도 여기에 포함됩니다.
  • Number("")0, Number([])0, Number(null)0입니다. 하지만 Number(undefined)NaN입니다. 실제 버그에서 자주 마주치는 포인트죠.
  • 영리해 보이는 암시적 형변환 트릭보다는 Number(x), String(x), Boolean(x) 같은 명시적 형변환을 쓰는 게 좋습니다. 나중의 내가 고마워할 겁니다.

다음: 동등 비교 연산자

비교 과정에서 일어나는 타입 변환의 거의 모든 것이 == 안에 들어 있습니다. 다음 페이지에서는 == vs === 차이, 그리고 Object.is까지 살펴보고, ==가 여전히 쓸모 있는 유일한 패턴 하나와 린터가 기본적으로 느슨한 비교(==)에 경고를 띄우는 이유를 다뤄봅니다.

자주 묻는 질문

자바스크립트의 타입 변환(type coercion)이란 뭔가요?

타입 변환은 자바스크립트가 값의 타입을 자동으로 바꾸는 동작입니다. 숫자가 문자열이 되기도 하고, 문자열이 숫자가 되기도 하고, 어떤 값이든 불리언으로 바뀌기도 하죠. +, ==, if 같은 연산자가 기대하지 않은 타입을 만나면 암시적으로 변환이 일어나고, Number(x), String(x), Boolean(x)를 직접 호출하면 명시적으로 변환됩니다.

암시적 변환과 명시적 변환의 차이는 뭔가요?

명시적 변환은 말 그대로 변환 함수를 의도적으로 호출하는 것입니다. Number("42"), String(99), Boolean(value) 처럼요. 반면 암시적 변환은 연산자가 뒤에서 몰래 타입을 바꾸는 경우인데, "5" - 23이 나오지만 "5" + 2"52"가 나옵니다. 명시적 변환은 읽기 쉽고 예측 가능한 반면, "왜 갑자기 NaN이 나오지?" 같은 버그는 대부분 암시적 변환에서 비롯됩니다.

자바스크립트에서 문자열을 숫자로 변환하려면 어떻게 해야 하나요?

엄격한 변환이 필요하면 Number("42")를 쓰세요 (유효한 숫자 형식이 아니면 NaN을 반환합니다). 문자열 앞부분에서만 숫자를 뽑고 싶다면 parseInt("42px", 10)이나 parseFloat("3.14em")이 적절합니다. 단항 + 연산자(+"42")도 Number()와 동일하게 동작하지만, 코드를 빠르게 훑을 때 놓치기 쉬우니 주의하세요.

[] + []는 빈 문자열을 반환하나요?

+ 연산자는 배열 두 개에 대해 의미 있는 숫자 연산이 없기 때문에, 자바스크립트는 양쪽을 문자열로 변환합니다. 배열은 요소를 쉼표로 이어 붙여 문자열이 되는데, 빈 배열은 ""가 됩니다. 그래서 [] + []"" + ""가 되고, 결과는 ""가 되는 거죠. 재밌는 예시지만, 동시에 +를 원시값이 아닌 값에 쓰면 안 되는 이유이기도 합니다.

Coddy로 코딩 배우기

시작하기