JavaScript 개발자라면 반드시 한 번은 봤을 유명한 밈이다. 이 밈은 JavaScript에서 느슨한 비교 연산자 ==
를 사용하였을 때 발생하는 암시적 형변환(Type Coercion)
으로 인한 문제점에 대해 이야기하고 있다. 만약, 저 밈이 말하는 바가 정확히 무엇인지, 더 나아가서는 JS의 암시적 형변환이 일어나는 세 가지 경우를 읊어볼 수 있다면, 이 포스팅을 읽을 필요가 없다.
하지만 만약 누군가로부터 JS 형변환
이 뭐죠? 라고 질문이 들어왔을 때 답변할 자신이 없다면 5분만 시간을 내 이 포스팅을 살펴보면 좋을 듯 하다.
이번 포스트에서는 JS의 두 가지 종류의 형변환에 대해 다룬다. 암시적 형변환
의 세 가지 케이스와 명시적 형변환
의 두 가지 방법에 대해 소개하고자 한다. 부디 이 포스트가 도움이 되었으면 한다.
형변환
이란 변수의 기존 자료형이 다른 자료형으로 바뀌는 것을 의미한다. JS
외에도 모든 언어의 형변환은 크게 암시적 형변환(Implicit Type Coercion)
과 명시적 형변환(Explicit Type Casting)
으로 나뉜다. 암시적, 명시적이라는 단어에서도 유추할 수 있듯 두 방식을 나누는 기준은 형변환의 주체
이다. 암시적 형변환
은 개발자의 의도와 무관하게 언어 내부에서 자동으로 변수의 자료형을 바꾸는 것을 의미한다. 반면, 명시적 형변환
은 개발자가 함수나 생성자 등을 활용한 코드를 짜서 의도적으로 변수의 자료형을 바꾸는 행위를 지칭한다.
암시적 형변환(Type Coercion)
: JS 엔진이 자동으로 변수의 자료형을 변환명시적 형변환(Type Casting)
: 개발자가 의도적으로 변수의 자료형을 변환
요약하자면, JS
에는 크게 두 종류의 형변환이 있으며, 이는 형변환의 주체
에 따라 구분한다. JS 엔진
이 자동으로 변수의 자료형을 변환한 것은 암시적 형변환
이며 개발자가 직접 코드를 작성해 변수의 자료형을 변환한 것을 명시적 형변환
이라 지칭한다. 정의에 대해서는 이 정도만 이해해도 충분하다.
단순히 웃긴 밈으로 넘겨선 안된다. 만약, 암시적 형변환
이 발생하는 대표적인 세 케이스를 모른다면 자신이 작성한 코드에서도 JS 엔진
이 자동으로 수행한 형변환에 의해 고통 받을지도 모른다. (저 뇌가 내 뇌가 되어서는 안된다...)
내 코드에서 내가 모르는 암시적 형변환
이 발생해서는 안된다. 이를 막기 위해 우리는 대표적인 3가지 암시적 형변환
케이스에 대해 알고 있어야 한다. JS 엔진
은 산술 연산자
, 느슨한 비교 연산자
, 이중 부정
을 사용할 때 자동으로 암시적 형변환
을 수행한다. 이 세 케이스에 대해 알아보자.
개발자가 서로 다른 자료형을 가지는 변수를 대상으로 산술 연산자
를 사용했을 때, 산술연산을 위해 JS 엔진
은 두 자료형을 공통된 자료형으로 통일해버린다. (혹자는 이를 JS의 유연성이라며 칭찬할 수 있지만, 쓰다보면 사실 암시적 형변환 때문에 고생스러운 경우가 더 많다)
산술 연산자에 의한 암시적 형변환은 + 연산자
에 의한 경우와 그 외 다른 연산자
에 의한 경우 두 가지로 나눌 수 있다.
서로 다른 자료형을 가지는 변수를 + 연산자
로 연결했을 때, JS 엔진
은 Boolean < Number < String
의 우선 순위에 기반해 암시적 형변환을 수행한다. 만약 연속적으로 + 연산자를 사용한 경우, 다른 자료형을 만나기 전까지는 일반적인 + 연산자
로 동작한다.
+ 연산자
에 의한 암시적 형변환 우선 순위 :Boolean < Number < String
// 우선순위에 의한 형변환
Number + Boolean // Number
Number + String // String
Boolean + String // String
// 실제 String을 만나기 전까지는 일반적인 + 연산자로 동작한다.
7 + 7 + 7; // = 21
7 + 7 + "7"; // = 147
"7" + 7 + 7; // = 777
+
외의 산술 연산자는 + 연산자
의 우선 순위와는 달리 다른 자료형의 두 변수에 사용 시 두 변수 모두를 Number 자료형으로 변환하고 계산을 수행한다.
-,*,/,% 연산자
에 암시적 형변환 우선 순위: 모든 변수를 무조건Number 자료형
으로 형변환
//// * 외에 모든 다른 연산자(-,/,%)도 동일하게 작동한다.
String * Number // Number
String * String // Number
Number * Number // Number
String * Boolean // Number
Number * Boolean // Number
“1031” * false; // 0
1031 * true; // 1031
서로 다른 자료형을 가지는 변수를 == 연산자로 비교
할 때, JS 엔진
은 양 측의 두 변수를 공통된 자료형으로 변환한 후 엄격한 비교 연산자인 ===
를 수행한 결과를 리턴한다.
만약 ==
와 ===
의 차이점에 대해 잘 모른다면, == vs ===를 참고하자.
느슨한 비교연산자
로 서로 다른 자료형을 가지는 변수를 비교하는 경우 발생하는 암시적 형변환
규칙은 피연산자의 자료형
에 따라 갈리며, 다른 우선순위 규칙에 비해 복잡한 편이다.
느슨한 비교 연산자(==)
에 의한 암시적 형변환 : 피연산자의 자료형에 따라 형변환 수행
피연산자 중String/Booelan
있는 경우 :String/Booelan
->Number
형변환
피연산자 중Null/Undefined
있는 경우 : 양측이 모두Null
이나Undefined
인 경우에만 참
피연산자가기본형
와객체(참조형)
를 비교하는 경우 :객체
를기본형
으로 강제 형변환.
1 == 1 // true
"1" == 1 // true
1 == '1' // true
0 == false // true
0 == null // false
0 == undefined // false
null == undefined // true
위에 적어 둔 규칙 외에도 많은 사항이 있다. 예를 들어 두 피연산자 기본형
과 객체(참조형)
를 비교할 때 객체가 빈 배열이라면 숫자로 강제 변환한다든가, 피연산자 중 하나의 앞에 ! 연산자
가 있는 경우 Boolean 형태
로 강제 변환한다는 등의 내용이다. 이러한 내용을 모두 외울 필요는 당연히 없다! 개발자가 알지 못하게 암시적으로 형변환을 수행하는 느슨한 비교 연산자인 ==
대신 엄격한 비교 연산자 ===
을 사용하면 암시적 형변환
문제에서 벗어날 수 있기 때문이다.
암시적 형변환의 마지막 대표 케이스는 변수 앞에 !! 이중 부정 연산자
를 사용한 경우이다. JS 엔진
은 변수 앞에 !! 이중 부정연산자
를 마주하면 해당 변수를 무조건 Boolean 자료형
으로 강제 변환한다.
이중 부정 연산자(!!)에 의한 암시적 형변환 : Boolean으로 강제 변환
이중 부정 연산자
에 의한 암시적 형변환
은 주로 JS
에서 false
로 여겨지는 Falsy
값들을 활용하여 if문을 분기하는데 자주 사용된다.
JS
Falsy
값:0
인 숫자,null
,undefined
,NaN
,빈 문자열
,빈 객체
Falsy
값 외는 모두 true로 판단됨
function check(variable) {
if (!!variable) {
console.log(variable);
} else {
console.log('잘못된 값');
}
}
check(null); // 잘못된 값
check(3.14); // 3.14
check(undefined); // 잘못된 값
check(0); // 잘못된 값
check('Good'); // Good
check(''); // 잘못된 값
check(NaN); // 잘못된 값
check(5); // 5
앞서 말했듯, 명시적 형변환
은 개발자가 해당 변수의 자료형을 변환할 의도를 가지고 코드를 작성하는 경우를 지칭한다. 명시적 형변환
의 두 방법은 해당 변수를 특정 자료형의 생성자
로 감싸는 방법과 자료형을 변환할 수 있는 특별한 함수를 사용하는 방식이 있다.
변수를 Object()
, Stiring()
, Booelan()
, Number()
등의 특정 자료형의 생성자로 감싸는 경우, 개발자는 변수를 특정 자료형으로 쉽게 변환할 수 있다.
let trans = 100; //Number
Object(trans); //100
console.log(typeof trans); //Object
String(trans); //”100"
console.log(typeof trans); //String
Boolean(trans); //true
console.log(typeof trans); //Boolean
변수.toString()
, 변수.toNumber()
, 변수.toBoolean()
등의 특별한 자료형 변환 함수를 사용하여 변수를 특정 자료형으로 변환할 수도 있다.
// 모든 자료형 -> String
변수.toString()
숫자.toString(base) // base 진법의 문자열로 해당 숫자 변환 디폴트 = 10
// 모든 자료형 -> Number
undefined.toNumber() // NaN
null.toNumber() // +0
true.toNumber() // 1
false.toNumber() // +0
// 모든 자료형 -> Boolean
falsy값.toBoolean() // false
그 외 값.toBoolean() // true
그 외에도 String
자료형에 제공되는 parseInt()
, parseFloat()
등의 특별한 함수를 사용해 자료형을 변환하는 방법도 있다.
// 문자열 -> 숫자
parseInt("문자열") // 문자열이 정수 외 포함시 NaN 반환
parseFloat("문자열") // 문자열이 정수 외 포함시 NaN 반환
앞서 알아본 암시적 형변환
의 세 가지 케이스를 알고 JS를 사용하면, 개발자의 의도와 부합하지 않는 형변환이 일어났을 경우, 해당 지점을 쉽게 파악하고 해결할 수 있을 것이라 기대된다. 협업을 하는 환경에서는 암시적 형변환
을 코드에 의도적으로 이용하는 것을 지양하고, 만약 의도적으로 자료형을 변경해야하는 경우 명시적 형변환
을 수행해야 한다.