[JavaScript] 타입과 타입 변환 (type coercion)
자바 스크립트 의 자료구조에 대해 정리하기 전에, 자바 스크립트 라는 언어의 특징중 한가지를 짚고 넘어가봅시다.
💡 정적 타입 검사 VS 동적 타입 검사
📌정적 타입 검사 (Static Type Checking)
정적 타입 검사 는 프로그램이 컴파일될 때, 변수와 표현식의 타입을 검사하는 방식입니다.
즉, 명시적인 타입 선언을 통해서 사전에 타입 오류를 발견해 방지하고, 이를 바탕으로 코드의 안정성을 확보하는 방식이라고 볼 수 있습니다.
정적 타입 검사 를 하는 언어인 자바 의 예를 봅시다.
1
2
3
4
int number = 10; // 타입을 정수형으로 명시적으로 선언
String text = "Hello"; // 타입을 문자열로 명시적으로 선언
number = "World"; // 컴파일 오류: 문자열을 정수에 할당할 수 없음
📌동적 타입 검사 (Dynamic Type Checking)
동적 타입 검사 는 프로그램이 실행되는 동안, 타입을 검사하는 방식입니다.
즉, 변수에 데이터가 대입되는 시점에 타입 검사를 통해 데이터 타입이 자동으로 결정되는 방식이라고 볼 수 있습니다.
자바 스크립트 가 바로, 이 동적 타입 검사 방식을 사용하는 동적 언어 입니다.
1
2
let number = 10; // 타입 선언 없음, 암시적으로 숫자 타입
number = "Hello"; // 다른 타입으로 변경 가능, 런타임에 문제 없음
자바 스크립트 는 동적 타입 검사 바탕으로, 타입 오류를 걱정할 필요가 없고, 간결하고 유연한 코딩이 가능하나 ,
본인이 의도치 않은 암시적 타입 변환으로 인해 프로그램의 잠재적인 오류를 발생시킬 수 있다는 단점도 인지해 두어야 합니다.
1
2
3
const num = 42; // num는 숫자겠죠?
const result = foo + "1"; // 이 코드에서 JavaScript는 num를 문자열로 강제 변환하므로, 다른 피연산자와 연결할 수 있습니다.
console.log(result); // 43이 아니라 421이 되네요. 전 이걸 의도한 적 없습니다!
정적 타입 검사와 동적 타입 검사는 각각의 장단점이 명확하기에 , 현재에도 프로그래머들 사이에서 선호도가 갈리는 주제이기도 합니다. 동적 타입 검사 방식이 편해서 좋다! 라는 생각은 접어두겠습니다…
💡 JavaScript의 타입
📌 원시 타입 (Primitive Type)
원시 값 은 언어의 가장 낮은 레벨에서 직접적으로 표현되는 불변 데이터를 의미합니다.
| 타입 | typeof 실행 시 반환값 | 객체 랩퍼 |
|---|---|---|
| Null 타입 | "object" | N/A |
| Undefined 타입 | "undefined" | N/A |
| Boolean 타입 | "boolean" | Boolean |
| Number 타입 | "number" | Number |
| BigInt 타입 | "bigint" | BigInt |
| String 타입 | "string" | String |
| Symbol 타입 | "symbol" | Symbol |
출처 - [mnm Web docs > JavaScript > JavaScript의 타입과 자료구조 ]([JavaScript의 타입과 자료구조 - JavaScript MDN (mozilla.org)](https://developer.mozilla.org/ko/docs/Web/JavaScript/Data_structures#원시_값primitive_values))
- Null 타입
Null타입은 “객체가 없음” 을 의미하는 유일한 값입니다.
즉, “값” 뿐만 아니라, “값을 담을 공간” 도 없음을 의미합니다.
typeof 를 통한 타입 검사 시 “object” 가 나오는 것을 확인 할 수 있습니다.
1
2
let n = null;
console.log(n, typeof(n)); // 출력 > null 'object'
설명의 용이를 위해 “값을 담을 공간도 없음” 이라고 표현하였지만, Null 자체도 “값” 의 종류 중 하나이긴 합니다.
즉, “값을 담을 유효한 객체를 참조하지 않는
값” 이라고 이해하는 것이 정확할 것 같습니다.
- Undefined 타입
Undefined타입은 “값이 없음” 을 의미하는 유일한 값입니다.
- 초기화가 없는 변수의 암시적 초기화
- 반환값이 없는
return문 - 존재하지 않는 객체 속성에 접근
등의 상황에서 Undefined 타입으로 정의되게 됩니다.
1
2
3
4
let u1;
let u2 = undefined;
console.log(u1, typeof(u1)); // 출력 > undefined 'undefined'
console.log(u2, typeof(u2)); // 출력 > undefined 'undefined'
- Boolean 타입
Boolean 타입은 논리값 입니다.
우리가 잘 알다시피 true 혹은 false 두 가지 값을 가질 수 있습니다.
- Number 타입
Number 타입은 숫자를 나타내는 타입입니다.
여기서 숫자는 말 그대로 “수학적인 값” 입니다!
1
2
3
4
let n1 = 1234;
let n2 = 56.78;
console.log(n1, typeof(n1)); // 1234 'number'
console.log(n2, typeof(n2)); // 56.78 'number'
NAN
NAN 은 산술 연산의 결과를 숫자로 표현할 수 없을 때, 일반적으로 발생하는 특별한 종류의 숫자 값입니다.
1
console.log(Number("숫자"), typeof Number("숫자")); // NaN 'number'
- BigInt 타입
BigInt 타입은 Number 타입의 확장판이라고 보면 될것같습니다.
Number 타입이 특성상 표현에 한계를 가지는 큰 정수를 나타낼 수 있는 타입입니다.
- String 타입
String 타입은 텍스트 데이터를 나타내는 타입입니다.
1
2
3
4
let s1 = "Hello, 'Vanguard'";
let s2 = 'Hello, "Vanguard"';
console.log(s1); // Hello, 'Vanguard'
console.log(s2); // Hello, "Vanguard"
이 타입은 조금 특이한 점이 있는데, 바로 어떤 자료구조라도 데이터 직렬화 과정을 통해서 String 타입으로 표현할 수 있다는 점입니다.
이 포맷이 바로 JSON 입니다 ! 하지만 지금은 관련이 없으니 넘어가도록 하겠습니다 ㅎ
- Symbol 타입
Symbol 타입은 고유하고 변경 불가능한 원시 값이며, 별도의 포스트에서 서술할 객체의 속성키 로 사용할 수 있습니다.
📌 객체 타입 (Object Type)
객체 는 식별자. 즉, 특정한 이름을 통해 참조할 수 있는 메모리 상의 값을 말합니다. 가장 익숙한 객체 중 하나를 뽑으라면 함수 를 뽑을 수 있겠네요.
함수 또한 callable 이라는 추가 기능이 있는 일급 객체 입니다.
가장 직관적이게 객체 타입 을 표현하자면, 원시 타입을 제외한 모든 타입 이라고 보면 됩니다.
배열, 함수, 날짜 등, 각 객체들이 하나하나 별도의 설명이 필요한 내용들이기에, 이 글에서는 개념에 대한 이해를 바탕으로 넘어가겠습니다.
💡 타입의 강제 변환
위에서 서술한것과 같이 자바스크립트 언어는 그 동적인 특징을 사용해 강제적으로 타입을 다른 타입으로 변환되게끔 할 수 있습니다.
이를 자바 스크립트 에서의 강제 형 변환 이라고 합니다.
📌 명시적 변환
타입의 명시적 변환은 String(), Number(), Boolean() 등을 사용해서 직접적으로 타입을 변환시키는 방법입니다.
이 경우에는 개발자가 직접 함수를 이용해 형 변환을 의도하므로, 코드에 대한 해석이 크게 필요하지 않습니다.
String()
1
2
console.log(String(52), typeof String(52)); // 결과값 => 52 string
console.log(String(true), typeof String(true)); // 결과값 => true string
Number()
1
2
3
4
5
6
console.log("52", typeof "52"); // 결과값 => 52 string
console.log(Number("52"), typeof Number("52")); // 결과값 => 52 'number'
console.log(Number(true), typeof Number(true)); // 결과값 => 1 'number'
console.log(Number(false), typeof Number(false)); // 결과값 => 0 'number'
console.log("숫자", typeof "숫자"); // 결과값 => 숫자 string
console.log(Number("숫자"), typeof Number("숫자")); // 결과값 => NaN 'number'
Boolean()
1
2
3
4
5
6
7
8
9
10
// 0, ""(빈 문자열), NaN, null, undefined는 false를 반환
console.log(0, typeof 0); // 0 'number'
console.log(Boolean(0), typeof Boolean(0)); // false 'boolean'
console.log(Boolean(""), typeof Boolean("")); // false 'boolean'
console.log(Boolean(NaN), typeof Boolean(NaN)); // false 'boolean'
console.log(Boolean(null), typeof Boolean(null)); // false 'boolean'
console.log(Boolean(undefined), typeof Boolean(undefined)); // false 'boolean'
// 그 외의 값들은 모두 true를 반환
console.log(Boolean(1), typeof Boolean(1)); // true 'boolean'
📌 암시적 변환
타입의 암시적 변환 은 특정 연산자, 또는 표현식이 사용될 때, 아직 정해지지 않은 타입 유형을 예상되는 유형으로 강제 변환하려는 성질을 의미합니다.
예를 들어보겠습니다.
1
consol.log(9 + 11 + 'Hello')
이 코드는 어떻게 출력될까요? 아니 애초에 정상적으로 출력은 되는걸까요?
1
>> '20Hello'
네, 위 처럼 출력됩니다. 이러한 결과가 나오는 이유가 암시적 변환 때문입니다. 이런 암시적 변환 특성은 개발시 유연성을 장점으로 가져갈 수 있으나, 동시에 원치않은 사용으로 에러의 주범이 될 수 있습니다. 이런 특징의 비판점때문에 Microsoft사 에서는 엄격한 문법성을 내세워 보완한 언어인 TypeScipt(타입스크립트) 를 발표하였습니다.
암시적 변환 의 경우의 종류를 따지려면 연산자 로 구분하여 살펴보아야 합니다.
산술연산자
➕ 연산자
문자의 우선순위가 가장 높습니다.
object, 함수 또한 문자보다 우선순위가 낮습니다.
모든 객체는 [object Object] 의 꼴을 띕니다.
1 2 3 4
console.log(10 + '10', typeof (10 + '10')); // 결과값 => "1010" "string" console.log(10 + 'abc', typeof (10 + 'abc')); // 결과값 => "10abc" "string" const obj = {a:'a'}; console.log(obj + 'abc'); //결과값 => "[object Object]abc"
그 외(➖,✖️, ➗) 연산자
숫자의 우선순위가 가장 높습니다.
숫자가 아닌 문자열이 들어갈 경우 연산이 불가능해
NAN을 반환합니다.Boolean타입의 경우 true는 1, false 는 0으로 변환됩니다.1 2 3 4 5 6
console.log(1 * 'a'); // 결과값 => "NAN" console.log(1 / 'a'); // 결과값 => "NAN" console.log(1 - 'a'); // 결과값 => "NAN" console.log(1 % 'a'); // 결과값 => "NAN" console.log(1 - true); // 결과값 => "0" console.log(1 * false); // 결과값 => "0"
동등(==) 연산자
0 과 “0” , false 를 동등연산자로 연산할 경우 각각 true 가 반환됩니다. = 0 이 각각
String타입과Boolean타입으로 형변환 후 비교” “ 와 0, false 를 동등연산자로 연산할 경우 true 가 반환됩니다. = “ “ 이 각각
Number타입과Boolean타입으로 형변환 후 비교” “ 와 “0” 을 동등연산자로 연산할 경우 false 가 반환됩니다. = 형변환 없이
String타입간의 비교1 2 3 4 5 6
console.log(0 == '0'); // 결과값 => "true" console.log(0 == false); // 결과값 => "true" console.log(1 == '1'); // 결과값 => "true" console.log('' == false) // 결과값 => "true" console.log('' == 0); // 결과값 => "true" console.log('' == '0'); // 결과값 => "false"
Java Script의 이러한 느슨한 문법 특성때문에, 비교 연산이 필요할 경우==동등 연산자가 아닌===일치 연산자를 사용합니다 !
얼음