Post

[JavaScript] 타입과 타입 변환 (type coercion)

[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의 타입과 자료구조 - JavaScriptMDN (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 의 이러한 느슨한 문법 특성때문에, 비교 연산이 필요할 경우 == 동등 연산자가 아닌 === 일치 연산자를 사용합니다 !

얼음

This post is licensed under CC BY 4.0 by the author.