JS-데이터 타입
| topics | 200-프론트개발 |
| types | 이론 학습 |
| tags | #javascript #datatype #primitive |
| references | 모던 자바스크립트 Deep Dive 6장 |
JS 데이터 타입은 왜 중요한가
자바스크립트의 데이터 타입을 제대로 이해하지 못하면 예상치 못한 버그가 발생한다. 특히 동적 타입 언어라서 더 신경 써야 한다.
데이터 타입 분류

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

특수 숫자 값
Infinity // 양의 무한대
-Infinity // 음의 무한대
NaN // Not a Number
// 주의!
NaN === NaN // false
isNaN(NaN) // true - 이걸 사용해야 함
왜 NaN === NaN이 false일까: IEEE 754 표준에 따라, NaN은 자기 자신과도 같지 않다고 정의되어 있다. 따라서
isNaN()함수를 사용해야 한다.
String
세 가지 방법으로 표현할 수 있다:
"큰따옴표"
'작은따옴표'
`백틱 (템플릿 리터럴)`
줄바꿈 처리


주의: 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"
데이터 타입이 필요한 이유
왜 데이터 타입이 필요할까?
- 메모리 공간 확보: 값을 저장할 때 필요한 메모리 크기를 결정
- 메모리 읽기: 값을 참조할 때 읽어야 할 메모리 크기를 결정
- 해석: 메모리에서 읽은 2진수를 어떻게 해석할지 결정
예를 들어, 숫자 1과 문자열 "1"은 메모리에서 다르게 저장되고 해석된다.
정적 타입 vs 동적 타입
정적 타입 언어 (Java, C, C++)
선언 시 타입을 명시하고, 컴파일 시점에 타입 체크를 한다.
int num = 10;
num = "hello"; // 컴파일 에러!
동적 타입 언어 (JavaScript, Python)
할당에 의해 타입이 결정된다. 할당되는 값에 따라 타입이 동적으로 변한다.
let foo = 10; // Number
foo = "hello"; // String - 타입이 바뀜!
foo = true; // Boolean - 또 바뀜!
장점: 유연성이 높다
단점: 신뢰성이 낮다 (런타임 에러 발생 가능)
동적 타입 언어 작성 시 주의점
변수를 필요한 경우에만 생성하고, 상수를 최대한 사용
const PI = 3.14; // 변하지 않는 값은 const 사용스코프를 최대한 좁게 만듦
// 나쁜 예 var globalVar = "전역 변수"; // 좋은 예 function foo() { const localVar = "지역 변수"; }네이밍을 잘하자
// 나쁜 예 let a = 10; let b = "hello"; // 좋은 예 let userAge = 10; let userName = "hello";TypeScript 도입 검토
큰 프로젝트라면 TypeScript를 사용하는 것이 좋다. 정적 타입 체크를 통해 버그를 사전에 방지할 수 있다.
관련 문서
- JS-객체 프로퍼티 설정 - 객체 프로퍼티의 속성 제어
- JS-this - 실행 컨텍스트와 this 바인딩