JS-데이터 타입

topics 200-프론트개발
types 이론 학습
tags #javascript #datatype #primitive
references 모던 자바스크립트 Deep Dive 6장

JS 데이터 타입은 왜 중요한가

자바스크립트의 데이터 타입을 제대로 이해하지 못하면 예상치 못한 버그가 발생한다. 특히 동적 타입 언어라서 더 신경 써야 한다.

데이터 타입 분류

Untitled

typeof 연산자로 확인할 수 있다.

원시 타입 (Primitive)

  • Number
  • String
  • Boolean
  • Undefined
  • Null
  • Symbol
  • BigInt (ES2020)

객체 타입 (Object)

  • Object, Array, Function, Date, RegExp 등

중요: 모든 원시 값은 **불변(immutable)**이다. 변수는 새로운 값을 다시 할당할 수 있지만, 이미 생성한 원시 값 자체는 변형할 수 없다. 이건 객체, 배열, 함수와는 다르다.


Number

배정밀도 64비트 부동소수점 형식

자바스크립트는 모든 숫자를 실수로 처리한다. 정수와 실수를 구분하지 않는다.

배정밀도: 64비트 사용
부동소수점: 1.xxx * 2^n 형식으로 표현

구조:

  • 첫 1비트: 부호 (양수/음수)
  • 다음 11비트: 지수 (n을 이진수로)
  • 나머지 52비트: 가수 (1.xxx의 xxx 부분)

부동소수점의 한계

0.1 + 0.2 === 0.3  // false!
0.1 + 0.2          // 0.30000000000000004

왜 이런 일이 생기나: 0.1과 0.2를 이진수로 정확히 표현할 수 없기 때문이다. 따라서 부동소수점 연산에서 오차가 발생한다.

해결 방법:

// 작은 값(epsilon)과 비교
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON  // true

// 또는 정수로 변환 후 계산
(0.1 * 10 + 0.2 * 10) / 10 === 0.3  // true

진법 표현

2진수, 8진수, 16진수를 구분하는 타입은 제공하지 않는다. 출력 시 모두 10진수로 변환된다.

0b1010  // 2진수 10
0o12    // 8진수 10
0xA     // 16진수 10

스크린샷 2023-08-09 오후 5.11.51.png

특수 숫자 값

Infinity    // 양의 무한대
-Infinity   // 음의 무한대
NaN         // Not a Number

// 주의!
NaN === NaN  // false
isNaN(NaN)   // true - 이걸 사용해야 함

왜 NaN === NaN이 false일까: IEEE 754 표준에 따라, NaN은 자기 자신과도 같지 않다고 정의되어 있다. 따라서 isNaN() 함수를 사용해야 한다.


String

세 가지 방법으로 표현할 수 있다:

"큰따옴표"
'작은따옴표'
`백틱 (템플릿 리터럴)`

줄바꿈 처리

스크린샷 2023-08-09 오후 5.23.49.png

스크린샷 2023-08-09 오후 5.24.48.png

주의: LF(\n)를 사용하는 게 좋다. CR(\r)은 운영체제에 따라 인식 못하는 경우가 있다.

템플릿 리터럴 (백틱)

const name = "민지";
const greeting = `안녕하세요, ${name}님!
오늘도 좋은 하루 되세요.`;

// 줄바꿈 문자도 그대로 인식
// ${} 통해 변수를 넣을 수 있음

Undefined

자바스크립트 엔진이 변수를 초기화할 때 사용하는 값이다.

let foo;
console.log(foo);  // undefined

// 값을 초기화하지 않으면 기본적으로 undefined가 할당된다

중요: 개발자가 의도적으로 undefined를 할당하지 않는 게 좋다. "값이 없음"을 명시하려면 null을 사용하자.


Null

값이 없다는 것을 의도적으로 명시하는 자료형이다.

let foo = "hi";
foo = null;  // 더 이상 "hi"를 참조하지 않음

// 가비지 컬렉션이 수행되어 메모리에서 제거됨

Symbol

ES6에서 추가된 타입. 다른 값과 중복되지 않는 유일무이한 값이다. 외부에 노출되지 않는다.

const sym1 = Symbol('key');
const sym2 = Symbol('key');

console.log(sym1 === sym2);  // false - 같은 설명이라도 다름!

// 주로 객체의 고유한 프로퍼티 키를 만들 때 사용
const obj = {
  [sym1]: 'value1'
};

래퍼 객체 (Wrapper Object)

원시 타입에도 메서드가 있다고? 원시 타입은 객체가 아닌데 어떻게 가능할까?

const str = "문자열";
str.length;  // 7 - 어떻게 프로퍼티에 접근할 수 있을까?

이유: 자바스크립트는 원시 타입에 대해 자동으로 래퍼 객체를 생성한다.

var str = "문자열";           // 문자열 리터럴 생성
var strObj = new String(str); // 문자열 객체 생성

str.length;      // 내부적으로 래퍼 객체를 생성한 후 length 프로퍼티 참조
str == strObj;   // true - 동등 연산자는 리터럴 값과 래퍼 객체를 동일하게 봄
str === strObj;  // false - 일치 연산자는 구별함

typeof str;      // "string"
typeof strObj;   // "object"

데이터 타입이 필요한 이유

왜 데이터 타입이 필요할까?

  1. 메모리 공간 확보: 값을 저장할 때 필요한 메모리 크기를 결정
  2. 메모리 읽기: 값을 참조할 때 읽어야 할 메모리 크기를 결정
  3. 해석: 메모리에서 읽은 2진수를 어떻게 해석할지 결정

예를 들어, 숫자 1과 문자열 "1"은 메모리에서 다르게 저장되고 해석된다.


정적 타입 vs 동적 타입

정적 타입 언어 (Java, C, C++)

선언 시 타입을 명시하고, 컴파일 시점에 타입 체크를 한다.

int num = 10;
num = "hello";  // 컴파일 에러!

동적 타입 언어 (JavaScript, Python)

할당에 의해 타입이 결정된다. 할당되는 값에 따라 타입이 동적으로 변한다.

let foo = 10;    // Number
foo = "hello";   // String - 타입이 바뀜!
foo = true;      // Boolean - 또 바뀜!

장점: 유연성이 높다
단점: 신뢰성이 낮다 (런타임 에러 발생 가능)


동적 타입 언어 작성 시 주의점

  1. 변수를 필요한 경우에만 생성하고, 상수를 최대한 사용

    const PI = 3.14;  // 변하지 않는 값은 const 사용
    
  2. 스코프를 최대한 좁게 만듦

    // 나쁜 예
    var globalVar = "전역 변수";
    
    // 좋은 예
    function foo() {
      const localVar = "지역 변수";
    }
    
  3. 네이밍을 잘하자

    // 나쁜 예
    let a = 10;
    let b = "hello";
    
    // 좋은 예
    let userAge = 10;
    let userName = "hello";
    
  4. TypeScript 도입 검토
    큰 프로젝트라면 TypeScript를 사용하는 것이 좋다. 정적 타입 체크를 통해 버그를 사전에 방지할 수 있다.


관련 문서