본문 바로가기
이론/Frontend

JavaScript의 강제변환 - 1 (Coercive Type Conversion of JavaScript)

by 유세지 2021. 3. 2.

자바스크립트에서는 프로그래머가 의도했건, 그렇지 않건 강제적 타입변환이 일어납니다. 누군가에겐 유용한 기능일수도 있고 누군가에겐 언어 자체가 잘못 설계된 부작용처럼 느껴질수도 있지만 어떤 상황에서 강제변환이 일어나는지 정확히 알아보고 이해해서 이용해 보는 것이 목표로 잡고 공부해봅니다.

 

 

값 변환

어떤 값을 다른 타입의 값으로 바꾸는 과정에서 명시적으로 일어나면 타입 캐스팅이라고 부르고, 암시적으로 일어나면 강제변환이라고 부릅니다.

 

그러나 많은 사람들이 이러한 용어를 뭉뚱그려 강제변환이라고 사용하는 경향이 있기 때문에, 여기선 암시적 강제변환과 명시적 강제변환이라고 부르겠습니다.

 

암시적과 명시적을 구분하는 방법은 간단합니다. 타입변환만을 의도한 코드를 작성했다면 명시적 강제변환이고, 의도와 상관없이 타입변환이 부수 효과로 일어나면 암시적 강제변환입니다.

 

예를 들면 이렇습니다.

 

 

 

 

추상 연산

이렇듯 암시적인 변환을 이해하기 위해서는 값의 타입이 어떻게 결정되는지 알아야 할 필요가 있습니다. 더하기(+) 연산자를 통해 문자열을 피연산자로 받았을 경우 문자열 타입이 된다는 사실을 모른다면 위의 예시에서 변수 explicit 가 어째서 문자열이 되는지 알 수 없었을 것입니다.

 

ES5에 명시된 몇 가지 변환 규칙을 알아보겠습니다.

 

1. ToString

문자열로의 변환을 담당하는 ToString 입니다. null이나 undefined와 같은 내장 원시 값은 특수한 문자열로 정해져있습니다 (각각 "null", "undefined"). 숫자의 경우 그대로 문자열로 바뀌는 대신, 너무 크거나 작은 값은 지수 형태로 변환됩니다.

 

일반적인 객체는 기본적으로 있는 toString() 메서드가 내부의 [[Class]] 를 반환합니다 ("[object Object]"). 따로 toString() 메소드를 가진 객체라면 대신 이 메소드를 호출합니다.

 

배열 또한 기본적으로 toString()을 가지고 있습니다. 내부의 원소들을 , 로 구분하여 문자열로 반환합니다 ("1,2,3").

 

추가적으로, JSON 문자열화도 ToString과 유사한 변환을 보입니다. JSON 안전 값으로 나타낼 수 있는 값은 모두 JSON.stringify() 를 통해 문자열로 변환할 수 있습니다. 안전 값이 아닌 예로는 undefined, 함수, 심벌 등이 있습니다.

 

안전 값이 아닌 값들이 인자로 들어오면 null로 바꾸어 반환합니다. 다만 null은 "null" 으로 문자열화 할 수 있다는 것도 재미있는 부분입니다.

 

객체에 toJSON() 메소드가 정의되어있다면 먼저 호출하여 그 값을 반환합니다. 이를 이용해 안전 값이 아닌 값들을 문자열화하기 위해서는 toJSON() 메소드를 따로 정의해주어야합니다. toJSON() 메소드를 통해 문자열하기 적절한 값을 반환시켜 stringify() 가 최종적으로 문자열로 변환하여 반환해줄 수 있도록 만들어줍니다.

 

2. ToNumber

수식 연산이 가능한 숫자로 바꾸는 로직을 담당하는 ToNumber 입니다. 숫자로 이루어진 문자열은 그대로 숫자로 변환되고, Boolean 값은 0과 1, undefined는 NaN, null은 0으로 변환됩니다.

 

또한 0이 맨앞에 붙은 8진수의 형태여도, 10진수로 처리합니다. 객체나 배열은 원시 값으로 변환한 뒤 위의 규칙들에 의해 변환됩니다. ToPrimitive 과정에서 해당하는 객체가 valueOf() 메소드를 구현했다면 해당 메소드를 사용합니다. 따로 구현되지 않았다면 toString()이 사용됩니다. 원시 값으로 바뀌지 않으면 typeError 오류를 던집니다.

 

어찌해도 변환할 수 없는 객체를 직접 확인해보고 싶다면 Object.create(null) 을 이용해서 확인해 볼 수 있습니다.

 

3. ToBoolean

true와 false 로의 변환 로직을 담당하는 ToBoolean입니다. ToBoolean을 다루기 전에 true와 false에 대해 먼저 짚고 넘어가겠습니다. 먼저 true/false 는 1/0 과 같지 않습니다. 비록 다른 언어에서는 같게 취급할지라도, 최소한 자바스크립트에서는 분명 다릅니다. 강제변환에 의해서 서로 변환될 수는 있지만 그렇다고 두 값이 똑같다는 것을 의미하지는 않습니다.

 

두 값은 변환될 수 있지만 같지 않습니다

 

 

그렇다면 어떤 값을 변환해야 1이 나오고, 어떤 값을 변환해야 0이 나올까요?

 

 

자바스크립트가 가질 수 있는 수 많은 값들중, 극히 일부만이 불리언으로 강제변환시 false가 됩니다. 이 값들을 제외한 다른 모든 값들은 강제변환시 true가 됩니다.

 

명세에 정의된 강제변환시 false가 되는 값들을 찾아보면 다음과 같습니다.

 

  • undefined
  • null
  • false
  • +0, -0, NaN
  • ""

 

이 값들을 falsy 값이라고 부르며, 이 외의 값들은 모두 truthy 값이고 강제변환시 true가 됩니다.

 

정말 특이한 케이스로 Falsy 객체라는게 있습니다. 값 이외에도 강제변환을 하면 False가 나오는 객체가 있는데, 대표적인 사례가 document.all입니다.

 

웹페이지의 요소를 자바스크립트에서 접근할 수 있게 하는 document.all은 이미 오래전에 deprecate된 형식이고, 이를 응용하여 구버젼의 IE를 식별하기 위한 일종의 검사기로서의 역할을 해왔습니다.

 

시간이 지나고 IE도 표준을 준수하기 시작하면서 더 이상 이러한 코드를 사용할 필요가 없게 되었지만 아직도 많은 레거시 코드가 남아있었고, 더 이상 이러한 객체가 쓰이는걸 막기 위해 document.all가 falsy한 객체처럼 작동하도록 자바스크립트 타입체계를 수정해버립니다.

 

정말 특이한 케이스이니 이런게 있다는 사실 정도만 알고 넘어가시면 되겠습니다.

 

 

truthy 값은 위에서 설명했듯 falsy 값이 아닌 모든 값들이고 그것이 배열이든, 객체든, 심지어 함수이더라도 true를 반환합니다. 아무리 falsy 한 값을 반환할 것처럼 보이는 변수라도 falsy 목록에 없다면 무조건 true를 반환하게 되니 흔들리지 않고 판별할 수 있도록 합시다.

 

 

 

 

 

명시적/암시적 강제변환에 앞서 값 변환에 대해 알아보았습니다.

다음 포스팅에 이어집니다.

반응형

댓글