본문 바로가기
이론/Frontend

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

by 유세지 2021. 3. 5.

이전 시간에는 강제변환을 이해하기에 앞서 값변환에 대해 정리하였습니다. 이번에는 명시적 강제변환에 대해 알아보겠습니다.

 

명시적 강제변환

명시적 강제변환은 상대적으로 덜 복잡합니다. 적혀있는 코드가 어떤 타입으로 변할지 눈에 바로 보이고, 그 결과를 혼동할 일이 거의 없습니다.

 

더군다나 자바스크립트의 명시적 강제변환은 다른 정적 타입 언어에서의 관례와 패턴을 충실하게 따르고 있기 때문에 별다른 논쟁거리도 없고 쉽게 받아들일 수 있습니다. 문자 그대로 이해하시면 되겠습니다.

 

 

  • 문자열 <-> 숫자

먼저 문자열과 숫자의 변환입니다.

서로 간의 타입 변환을 위해 String()Number()을 사용합니다. 여기에 new 키워드가 붙으면 새로운 객체 래퍼를 생성하는 구문이겠지만, 키워드 없이 사용되면 타입 변환에 이용된다는 점을 기억해주세요.

 

 

String()/Number() 타입 캐스팅으로 감싸주면 이전 시간에 살펴봤던 ToString/ToNumber 추상 연산 로직에 따라 변환할 수 있는 값들을 원시 값으로 변환시키게됩니다.

 

또 다른 명시적인 변환 방법으로는 문자열의 경우 .toString() 을 이용하는 방법, 숫자의 경우 + 연산자를 이용한 방법이 있습니다.

 

 

toString()의 경우 내부적으로는 어느정도 암시적인 동작들을 가지고 있습니다. 원시값 321은 toString()이라는 메소드를 가지고 있지 않으므로 객체 래퍼로 박싱하는 작업을 암시적으로 동반하게 됩니다.

 

여기서 +연산자가 문자열을 숫자로 변환하는건 암시적인 기능이 아닌가 하는 의문이 들 수도 있지만, +연산자는 덧셈을 하는 기능이 아닌, 연산자 뒤에 붙는 문자열을 숫자로 변환하는 독립적인 단항 연산자입니다. 따라서 엄연히 명시적이라고 보아야하는게 맞습니다. 저도 이번에 처음 알게되었네요.

 

다만 더 직관적인 Number()대신 +를 굳이 사용해야할까? 하는 질문에는 그렇다고 이야기하긴 어려울 것 같습니다. 비슷하게 생긴 다른 연산자들과 함께 사용되면 아무리 위 사실을 알고 있던 프로그래머라고 하더라도 혼동이 올 수 밖에 없기 때문입니다.

 

최악의 경우 이런 코드가 나올 수도 있습니다.

 

어디까지나 극단적인 예시입니다

 

이렇게 써도 작동은 하지만, 리팩토링 스터디에서도 여러번 언급했듯이 최대한 문제 요소를 줄이는 방향으로 코드를 짜는것이 바람직합니다.

 

 

  • 날짜 -> 숫자

다음은 날짜를 숫자 형태로 변환하는 과정입니다.

자바스크립트에는 날짜를 나타내는 Date 객체가 존재합니다. new Date()를 통해 1970년 1월 1일을 기점으로 지금까지 시간을 타임스탬프 표현형(밀리초 단위) 으로 갖게 됩니다. 

 

timestamp 값을 그대로 출력하면 Thu Mar 04 2021 18:05:08 GMT+0900 (대한민국 표준시) 와 같은 값을 얻을 수 있는데, 이를 +연산자나 Number()을 이용해 강제변환하면 1614848708699 처럼 밀리초단위로 카운트 된 원시값이 나오게 됩니다.

 

다만 Date 객체의 경우 타임스탬프 표현형을 숫자 값으로 변환해주는 getTime() 메소드를 가지고 있기 때문에 굳이 강제변환을 할 필요없이 Date.getTime() 를 통해 얻을 수 있습니다.

 

 

 

 

  • 비트 연산자 ~

틸드(Tilde, ~) 는 피연산자의 NOT을 구하는 비트 단위 연산자입니다. 이진법에 관한 내용을 공부했다면, 보수(complement) 의 개념을 알고 계실겁니다. 자바스크립트에서 보수를 구할때 바로 이 틸드를 사용합니다.

 

틸드의 적용 과정을 보면 먼저 피연산자를 32비트 숫자로 강제변환합니다. 그 뒤 이진수를 뒤집는 NOT 연산을 진행합니다. 따라서 어떤 숫자를 넣으면 그 숫자에 1을 더한 값에 -을 붙인 값을 반환하게됩니다.

 

명시적 강제변환을 이야기하는데 뜬금없이 왜 틸드가 나오는지 의아했었지만, 보수를 반환하는 특성 때문에 문자열에서 특정 위치를 검색하는 indexOf() 메소드와 함께 사용하면 강제변환을 통해 불리언 값으로 만들 수 있습니다.

 

 

 

 

  • 파싱

문자열에 포함된 숫자를 파싱하는 parseInt()는 언뜻 보기엔 타입변환과 비슷해보입니다. 그러나 이 둘은 분명 용도도, 기능도 다른 각각의 로직입니다. 파싱과 타입변환의 가장 큰 차이점은 비 숫자형 문자의 허용여부입니다.

 

 

예시 코드의 변수명을 보고 이미 직감하신 분도 계시겠지만 파싱의 경우 숫자가 아닌 문자를 만나면 그 자리에서 변환을 중단하고 결과값을 반환하는 반면, 타입변환은 단 하나의 숫자가 아닌 문자도 허용하지 않고 NaN을 반환하는 모습입니다.

 

parseInt(), parseFloat() 등 파싱하는 메소드들은 어디까지나 문자열에 사용하는 함수입니다. 타입변환을 위해서는 그에 맞는 방법을 사용해주시면 되겠습니다.

 

 

  • -> 불리언

어떤 값으로부터의 불리언 값으로의 변환입니다. 불리언도 마찬가지로 Boolean()를 통해 명시적으로 타입을 변환해 줄 수 있습니다. 다만 Boolean() 자체는 그리 자주 사용되는 구문은 아닙니다.

 

부정 단항 연산자인 !+ 단항 연산자 처럼 값을 불리언 타입으로 변환해줍니다. 다만 변환 과정에서 Truthy와 Falsy 한 값들이 뒤바뀐다는 문제가 있습니다. 따라서 연산자를 이중으로 사용해서 변환 해주는것이 일반적입니다.

 

 

 

이 외에도 삼항 연산자 ? : 또한 명시적인 변환처럼 보입니다. ? 앞의 표현식의 결과에 따라 각기 다른 항을 반환한다는 점에서 명시적인 변환이라고 볼 수 있겠지만, 표현식을 평가하기 위해 ToBoolean으로 강제변환이 되어야 항을 반환할 수 있기 때문에 암시적인 변환의 성격 또한 가지고 있습니다. 이런 경우를 명시적으로 암시적(Explicitly Implicit) 이라고 하는데 되도록이면 사용을 지양하도록 권장하고 있습니다.

 

다만 개인적으로는 자바와 같은 언어를 먼저 접하고 사용했었기 때문에 삼항 연산자의 불리언 변환에 큰 문제를 느껴보지 못했습니다. 좀 더 고민해봐야할 문제 같습니다.

 

 

 

여기까지 명시적인 강제변환에 대해 알아보았습니다. 다음 포스팅에서는 마지막으로 암시적 강제변환에 대해 알아보겠습니다.

반응형

댓글