본문으로 건너뛰기
Advertisement

2.2 데이터 타입

JavaScript의 타입 시스템

JavaScript는 동적 타입(dynamic typing) 언어입니다. 변수 자체에 타입이 없고, 변수가 담고 있는 값에 타입이 있습니다.

let variable = 42;          // number
variable = "텍스트"; // string (타입이 바뀜!)
variable = true; // boolean
variable = null; // null
variable = { name: "JS" }; // object

Primitive 타입 7가지

JavaScript의 원시 타입(primitive type)은 **불변(immutable)**이며 값 자체로 비교됩니다.

1. string — 문자열

// 선언 방법 3가지
const single = '작은따옴표';
const double = "큰따옴표";
const template = `템플릿 리터럴`; // ES6+

// 이스케이프 시퀀스
const newline = "줄바꿈\n이후";
const tab = "탭\t이후";
const quote = '따옴표\'포함';
const backslash = "역슬래시\\포함";

// 템플릿 리터럴 (백틱)
const name = "철수";
const age = 25;
const greeting = `안녕하세요, ${name}! 나이: ${age}`;
console.log(greeting); // "안녕하세요, 철수! 나이: 25살"

// 멀티라인
const multiline = `
첫 번째 줄
두 번째 줄
세 번째 줄
`;

// 주요 string 메서드
const str = "Hello, World!";
console.log(str.length); // 13
console.log(str.toUpperCase()); // "HELLO, WORLD!"
console.log(str.toLowerCase()); // "hello, world!"
console.log(str.includes("World")); // true
console.log(str.startsWith("Hello")); // true
console.log(str.endsWith("!")); // true
console.log(str.indexOf("World")); // 7
console.log(str.slice(7, 12)); // "World"
console.log(str.replace("World", "JS")); // "Hello, JS!"
console.log(str.split(", ")); // ["Hello", "World!"]
console.log(" 공백 ".trim()); // "공백"
console.log("ha".repeat(3)); // "hahaha"

2. number — 숫자

JavaScript에는 정수와 소수 구분 없이 하나의 number 타입만 있습니다. IEEE 754 배정밀도 64비트 부동소수점 형식을 사용합니다.

// 정수와 소수
const integer = 42;
const float = 3.14;
const negative = -10;

// 특수값
console.log(Infinity); // 양의 무한대
console.log(-Infinity); // 음의 무한대
console.log(NaN); // Not a Number (숫자가 아님)

// NaN 확인 (== 비교 불가!)
console.log(NaN === NaN); // false (NaN은 자기 자신과도 다름!)
console.log(isNaN(NaN)); // true
console.log(Number.isNaN(NaN)); // true (더 정확한 방법)

// 부동소수점 정밀도 문제
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false (!)

// 해결 방법: Number.EPSILON 사용
const a = 0.1 + 0.2;
const b = 0.3;
console.log(Math.abs(a - b) < Number.EPSILON); // true

// 숫자 범위
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 (2^53 - 1)
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

// 진법 표현
const hex = 0xFF; // 16진수 = 255
const oct = 0o77; // 8진수 = 63
const bin = 0b1010; // 2진수 = 10

// 숫자 구분자 (ES2021+)
const million = 1_000_000; // 읽기 편하게
const pi = 3.141_592_653;

// Math 객체
console.log(Math.floor(3.7)); // 3 (내림)
console.log(Math.ceil(3.2)); // 4 (올림)
console.log(Math.round(3.5)); // 4 (반올림)
console.log(Math.abs(-5)); // 5 (절대값)
console.log(Math.max(1, 2, 3)); // 3
console.log(Math.min(1, 2, 3)); // 1
console.log(Math.pow(2, 10)); // 1024
console.log(Math.sqrt(16)); // 4
console.log(Math.random()); // 0~1 난수

3. bigint — 큰 정수

number는 2^53-1보다 큰 정수를 정확하게 표현하지 못합니다. bigint는 임의 크기의 정수를 정확하게 처리합니다.

// n 접미사로 bigint 생성
const big = 9007199254740993n; // number로는 정확히 표현 불가
const huge = BigInt("123456789012345678901234567890");

console.log(typeof big); // "bigint"

// 연산
console.log(1n + 2n); // 3n
console.log(10n / 3n); // 3n (소수 없이 정수 나눗셈)
console.log(2n ** 100n); // 매우 큰 수

// bigint와 number는 혼합 연산 불가
console.log(1n + 1); // TypeError: Cannot mix BigInt and other types
console.log(1n + 1n); // 2n (OK)

// 비교는 가능
console.log(1n === 1); // false (타입 다름)
console.log(1n == 1); // true (타입 강제 변환)

4. boolean — 불리언

const isTrue = true;
const isFalse = false;

console.log(typeof isTrue); // "boolean"

// 비교 연산의 결과
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 === 5); // true

// Truthy / Falsy
// Falsy 값 (불리언 변환 시 false):
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean(0n)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false

// 나머지는 모두 Truthy:
console.log(Boolean("0")); // true (문자열 "0"은 truthy!)
console.log(Boolean([])); // true (빈 배열도 truthy!)
console.log(Boolean({})); // true (빈 객체도 truthy!)

5. null — 의도적인 없음

null은 값이 의도적으로 없음을 나타냅니다.

let user = null; // 사용자가 아직 로그인하지 않음

console.log(typeof null); // "object" (역사적 버그! null은 object가 아님)
console.log(null === null); // true
console.log(null == undefined); // true (느슨한 비교)
console.log(null === undefined); // false (엄격한 비교)

6. undefined — 초기화되지 않음

undefined는 변수가 선언됐지만 값이 할당되지 않은 상태입니다.

let uninitialized;
console.log(uninitialized); // undefined
console.log(typeof uninitialized); // "undefined"

// 함수가 반환값이 없을 때
function noReturn() {}
console.log(noReturn()); // undefined

// 존재하지 않는 객체 프로퍼티
const obj = {};
console.log(obj.missing); // undefined

// null vs undefined
console.log(typeof null); // "object" (버그)
console.log(typeof undefined); // "undefined"

7. symbol — 유일한 식별자

Symbol은 ES6에서 추가된 타입으로, 항상 유일한(unique) 값을 생성합니다.

const sym1 = Symbol("설명");
const sym2 = Symbol("설명");
console.log(sym1 === sym2); // false (같은 설명이어도 다름!)

// 주로 객체의 고유 키로 사용
const ID = Symbol('id');
const user = {
name: "김철수",
[ID]: 12345, // Symbol을 키로 사용
};

console.log(user[ID]); // 12345
console.log(user.name); // "김철수"

// Symbol 키는 for...in, Object.keys에서 보이지 않음 (숨겨진 프로퍼티)
console.log(Object.keys(user)); // ["name"] (ID 없음)

// Well-known Symbol: 내장 동작 커스터마이징
class MyCollection {
constructor() {
this.items = [1, 2, 3];
}
[Symbol.iterator]() {
return this.items[Symbol.iterator]();
}
}

for (const item of new MyCollection()) {
console.log(item); // 1, 2, 3
}

typeof 연산자

console.log(typeof "hello");     // "string"
console.log(typeof 42); // "number"
console.log(typeof 42n); // "bigint"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof Symbol()); // "symbol"
console.log(typeof null); // "object" ← 버그!
console.log(typeof {}); // "object"
console.log(typeof []); // "object" ← 배열도 object
console.log(typeof function(){}); // "function"

참조 타입 (Reference Types)

Primitive 외의 모든 값은 참조 타입입니다.

// 원시값: 복사 시 값이 복사됨
let a = 10;
let b = a;
b = 20;
console.log(a); // 10 (영향 없음)

// 참조값: 복사 시 참조(주소)가 복사됨
const arr1 = [1, 2, 3];
const arr2 = arr1; // 같은 배열을 가리킴
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4] (arr1도 변경됨!)

// 객체도 동일
const obj1 = { x: 1 };
const obj2 = obj1;
obj2.x = 100;
console.log(obj1.x); // 100

// 복사본 만들기 (얕은 복사)
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3] (영향 없음)

// 객체 얕은 복사
const orig = { a: 1, b: { c: 2 } };
const shallow = { ...orig };
shallow.a = 100;
console.log(orig.a); // 1 (영향 없음)
shallow.b.c = 999;
console.log(orig.b.c); // 999 (내부 객체는 여전히 같은 참조!)

고수 팁

타입 확인 완벽 가이드

// 원시 타입 확인
const isString = (v) => typeof v === 'string';
const isNumber = (v) => typeof v === 'number' && !isNaN(v);
const isBoolean = (v) => typeof v === 'boolean';
const isUndefined = (v) => typeof v === 'undefined';
const isNull = (v) => v === null; // typeof로는 안됨!
const isSymbol = (v) => typeof v === 'symbol';
const isBigInt = (v) => typeof v === 'bigint';

// 참조 타입 확인
const isArray = (v) => Array.isArray(v); // typeof []는 "object"
const isObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
const isFunction = (v) => typeof v === 'function';

// 더 정확한 타입 확인
const getType = (v) => Object.prototype.toString.call(v).slice(8, -1).toLowerCase();

console.log(getType([])); // "array"
console.log(getType({})); // "object"
console.log(getType(null)); // "null"
console.log(getType(new Date())); // "date"
console.log(getType(/regex/)); // "regexp"
Advertisement