6장 데이터 타입
JS의 모든 값은 반드시 데이터 타입을 가진다.
데이터 타입 = 값의 종류 구분 + 해석 및 처리 방법 결정
데이터 타입 분류:
| 구분 | 데이터 타입 | 설명 |
|---|---|---|
| 숫자 타입 (number) | 정수와 실수 구분 없는 숫자 타입 | |
| 문자열 타입 (string) | 텍스트 데이터 | |
| 원시 타입 | 불리언 타입 (boolean) | 논리적 참(true), 거짓(false) |
| (Primitive Type) | undefined 타입 | var 키워드 선언 변수에 암묵적 할당 값 |
| null 타입 | 값 없음을 의도적으로 명시할 때 사용 | |
| 심벌 타입 (symbol) | ES6 추가 - 변경 불가능한 유일무이한 값 | |
| 객체 타입 (Object/Reference Type) | 객체, 함수, 배열 등 |
개발자는 데이터 타입으로 값의 의미를 명확히 표현해야 함
ex) 숫자 1 ≠ 문자열 '1'
6-1. 숫자 타입
JS는 C, Java와 달리 하나의 숫자 타입만 존재
특징:
- 정수, 실수, 부동소수점 구분 없이 하나의 타입
- 64비트 부동소수점 형식 (배정밀도, double-precision) 사용
- 모든 수를 실수로 처리
// 모두 number 타입
var integer = 10;
var double = 10.12;
var negative = -20;
// 모든 숫자는 실수로 처리
console.log(1 === 1.0); // true
console.log(4 / 2); // 2
console.log(3 / 2); // 1.5 (정수 나눗셈 아님)다양한 진법 표현:
JS는 2진수, 8진수, 16진수 리터럴 제공 → 모두 10진수로 해석
var binary = 0b01000001; // 2진수 (0b 접두사)
var octal = 0o101; // 8진수 (0o 접두사)
var hex = 0x41; // 16진수 (0x 접두사)
console.log(binary); // 65
console.log(octal); // 65
console.log(hex); // 65
console.log(binary === octal); // true
console.log(octal === hex); // true특별한 숫자 값 3가지:
- Infinity: 양의 무한대
- -Infinity: 음의 무한대
- NaN (Not-a-Number): 산술 연산 불가
console.log(10 / 0); // Infinity
console.log(10 / -0); // -Infinity
console.log(1 * 'String'); // NaN주의사항:
- JS는 대소문자 구분 →
NaN정확히 표기 필수 NaN은 자기 자신과도 같지 않은 유일한 값 (NaN === NaN은false)NaN확인 →Number.isNaN()메서드 사용
console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(10)); // false6-2. 문자열 타입
문자열(String): 텍스트 데이터 표현
특징:
- 0개 이상의 16비트 유니코드 문자(UTF-16) 집합
- 전 세계 대부분 문자 표현 가능
- 작은따옴표(
''), 큰따옴표(""), 백틱(``) 사용 - 원시 타입 + 변경 불가능한 값(immutable value)
var string;
string = '문자열'; // 작은따옴표
string = "문자열"; // 큰따옴표
string = `문자열`; // 백틱 (ES6)
// 따옴표 내부의 다른 종류 따옴표 = 문자열로 인식
string = '작은따옴표로 감싼 문자열 내의 "큰따옴표"는 문자열로 인식된다.';
string = "큰따옴표로 감싼 문자열 내의 '작은따옴표'는 문자열로 인식된다.";따옴표의 필요성:
키워드나 식별자와 문자열 구분 위함
var string = hello; // ReferenceError: hello is not defined
var string = 'hello'; // 'hello'라는 문자열문자열의 불변성:
문자열 생성 후 변경 불가 (11장에서 자세히 다룸)
var str = 'Hello';
str[0] = 'W'; // 변경 불가 (에러 없지만 변경 안 됨)
console.log(str); // 'Hello'6-3. 템플릿 리터럴
템플릿 리터럴(Template Literal): ES6 도입 - 새로운 문자열 표기법
제공 기능:
- 멀티라인 문자열 (Multi-line String)
- 표현식 삽입 (Expression Interpolation)
- 태그드 템플릿 (Tagged Template)
백틱(``) 사용 → 런타임에 일반 문자열로 변환
var template = `Template literal`;
console.log(template); // Template literal6-3-1. 멀티라인 문자열
일반 문자열의 줄바꿈:
줄바꿈 허용 X → 백슬래시(\)로 시작하는 이스케이프 시퀀스 사용 필요
주요 이스케이프 시퀀스:
| 이스케이프 시퀀스 | 의미 |
|---|---|
\n | 개행 (Line Feed) |
\t | 탭 (Tab) |
\\ | 백슬래시 |
\' | 작은따옴표 |
\" | 큰따옴표 |
// 잘못된 예: 개행 직접 입력
var str = 'Hello
world.'; // SyntaxError: Invalid or unexpected token
// 올바른 예: 이스케이프 시퀀스 사용
var template = '<ul>\n\t<li><a href="#">Home</a></li>\n</ul>';
console.log(template);
// 출력:
// <ul>
// <li><a href="#">Home</a></li>
// </ul>템플릿 리터럴의 멀티라인:
이스케이프 시퀀스 없이 줄바꿈과 공백 그대로 적용
var template = `<ul>
<li><a href="#">Home</a></li>
</ul>`;
console.log(template);
// 출력:
// <ul>
// <li><a href="#">Home</a></li>
// </ul>6-3-2. 표현식 삽입
일반 문자열의 연결:
문자열 연결 연산자 + 사용
var first = 'Ung-mo';
var last = 'Lee';
// ES5: 문자열 연결
console.log('My name is ' + first + ' ' + last + '.');
// My name is Ung-mo Lee.템플릿 리터럴의 표현식 삽입:
${} 사용 → 가독성과 편의성 향상
var first = 'Ung-mo';
var last = 'Lee';
// ES6: 표현식 삽입
console.log(`My name is ${first} ${last}.`);
// My name is Ung-mo Lee.활용:
console.log(`1 + 2 = ${1 + 2}`); // 1 + 2 = 3주의:
표현식 삽입은 반드시 템플릿 리터럴 내에서 사용
→ 일반 문자열에서는 문자열로 취급
console.log(`1 + 2 = ${1 + 2}`); // 1 + 2 = 3 (표현식 평가)
console.log('1 + 2 = ${1 + 2}'); // 1 + 2 = ${1 + 2} (문자열 그대로)6-4. 불리언 타입
불리언(Boolean): 논리적 참, 거짓 나타내는 true, false 두 값만 존재
var foo = true;
console.log(foo); // true
foo = false;
console.log(foo); // false주로 조건문에서 프로그램 흐름 제어에 사용
var isAdult = true;
if (isAdult) {
console.log('성인입니다.');
} else {
console.log('미성년자입니다.');
}6-5. undefined 타입
undefined 타입의 값 = undefined가 유일
의미와 용도:
undefined= “값이 할당된 적 없음”var키워드 선언 변수 → 암묵적으로undefined로 초기화- 개발자가 의도적 할당은 비권장
var foo;
console.log(foo); // undefined변수 초기화 과정:
- 변수 선언 시 메모리 공간 확보
- JS 엔진이 암묵적으로
undefined로 초기화 - 첫 할당까지
undefined상태 유지
undefined vs null:
undefined: JS 엔진이 변수 초기화에 사용null: 개발자가 의도적으로 “값 없음” 명시
⇒ 의도적으로 “값 없음” 표현 시 undefined 아닌 null 사용
var foo = null; // 의도적으로 값 없음 명시6-6. null 타입
null 타입의 값 = null이 유일
의미와 용도:
null= “값 없음”을 의도적으로 명시- 변수가 이전 참조값을 더 이상 참조하지 않겠다는 의미
- 이전 할당 값에 대한 참조 명시적 제거
var foo = 'Lee';
// foo가 더 이상 'Lee'를 참조하지 않도록 설정
// 이전 값 참조 제거 → 가비지 컬렉션 대상
foo = null;활용:
함수가 유효한 값 반환 불가 시 명시적으로 null 반환
var element = document.querySelector('.myClass');
// HTML 문서에 .myClass 없으면 null 반환
console.log(element); // null주의:
- JS는 대소문자 구분 →
null정확히 표기 Null,NULL등 = 다른 식별자로 인식
6-7. 심벌 타입
심벌(Symbol): ES6 추가 - 7번째 타입, 변경 불가능한 원시 타입
특징:
- 다른 값과 중복 없는 유일무이한 값
- 주로 객체의 유일한 프로퍼티 키 생성에 사용
- 외부 노출 X → 충돌 위험 없음
심벌 값 생성:
Symbol 함수 호출
// 심벌 값 생성
var key = Symbol('key');
console.log(typeof key); // symbol
// 객체 생성
var obj = {};
// 심벌을 프로퍼티 키로 사용
obj[key] = 'value';
console.log(obj[key]); // value활용:
// 서로 다른 심벌 = 유일무이
var symbol1 = Symbol('mySymbol');
var symbol2 = Symbol('mySymbol');
console.log(symbol1 === symbol2); // false
// 객체 프로퍼티 키 충돌 방지
var obj = {
[symbol1]: 'value1',
[symbol2]: 'value2'
};
console.log(obj[symbol1]); // value1
console.log(obj[symbol2]); // value2심벌 자세한 내용은 33장에서 다룸
6-8. 객체 타입
지금까지 살펴본 6가지 원시 타입 이외 = 모두 객체 타입
핵심:
JS = 객체 기반 언어
→ JS를 이루는 거의 모든 것 = 객체
- 배열, 함수, 정규 표현식 등 모두 객체
- 원시 타입 제외 = 모두 객체 타입
객체 타입은 11장에서 자세히 다룸
var obj = { name: 'Lee', age: 20 }; // 객체
var arr = [1, 2, 3]; // 배열 (객체)
var func = function() {}; // 함수 (객체)6-9. 데이터 타입의 필요성
데이터 타입이 필요한 이유 = 3가지 측면
6-9-1. 메모리 공간의 확보
값 저장 시 확보할 메모리 공간 크기 결정 위함
→ 낭비와 손실 없이 저장
예제:
var score = 100;값의 저장 과정:
- 리터럴
100을 숫자 타입 값으로 해석 - 숫자 타입 = 8바이트 공간 필요
- 8바이트 메모리 공간 확보
- 100을 2진수로 변환 → 메모리에 저장
데이터 타입 없으면 → 확보할 메모리 크기 알 수 없음
6-9-2. 메모리 공간의 참조
값 참조 시 읽어들일 메모리 공간 크기 결정 위함
var score = 100;
console.log(score); // 변수 score 참조값의 참조 과정:
- 변수
score의 메모리 주소 확인 - 저장된 값의 타입(숫자) 확인
- 숫자 타입 = 8바이트 → 8바이트만큼 읽음
잘못된 크기로 읽으면 → 값 손실 or 다른 값까지 읽는 문제 발생
6-9-3. 값의 해석
메모리에서 읽은 2진수를 어떻게 해석할지 결정 위함
메모리 저장 값 = 모두 2진수 비트 나열
ex) 0100 0001 해석:
- 숫자로 해석: 65
- 문자로 해석: ‘A’
데이터 타입에 따라 동일 2진수도 다르게 해석
심벌 테이블:
컴파일러/인터프리터는 **심벌 테이블(Symbol Table)**로 관리
→ 식별자를 키로 다음 정보 저장:
- 바인딩된 값의 메모리 주소
- 데이터 타입
- 스코프
6-10. 동적 타이핑
프로그래밍 언어의 변수 타입 결정 방식에 따른 분류
6-10-1. 정적 타입 언어와 동적 타입 언어
정적 타입 언어 (Static Type Language)
예: C, C++, Java, Kotlin, Go, Haskell, Rust, Scala
특징:
- 명시적 타입 선언: 변수 선언 시 데이터 타입 사전 선언 필요
- 타입 변경 불가: 선언한 타입 이외 값 할당 불가
- 컴파일 타임 타입 체크: 컴파일 시점에 타입 체크 수행
- 타입 안정성: 타입 일관성 강제 → 안정적 코드 구현
// C 언어 예시
char c; // 1바이트 정수 타입 (-128 ~ 127)
int num; // 4바이트 정수 타입
c = 100; // OK
c = "Hello"; // 컴파일 에러: 타입 불일치동적 타입 언어 (Dynamic Type Language)
예: JavaScript, Python, PHP, Ruby, Lisp, Perl
특징:
- 타입 선언 불필요: 변수 선언 시 타입 선언 안 함
- 자유로운 할당: 어떤 타입의 값이든 자유롭게 할당 가능
- 동적 타입 결정: 할당된 값에 의해 타입 동적 결정
- 런타임 타입 결정: 실행 시점에 타입 결정
var foo;
console.log(typeof foo); // undefined
foo = 3;
console.log(typeof foo); // number
foo = 'Hello';
console.log(typeof foo); // string
foo = true;
console.log(typeof foo); // boolean
foo = null;
console.log(typeof foo); // object (JS의 버그)
foo = Symbol();
console.log(typeof foo); // symbol
foo = {};
console.log(typeof foo); // object
foo = [];
console.log(typeof foo); // object
foo = function() {};
console.log(typeof foo); // functiontypeof 연산자:
피연산자의 데이터 타입을 문자열로 반환
핵심:
- JS에서 변수는 타입을 갖지 않음
- 값은 타입을 가짐
- 할당된 값의 타입에 따라 변수 타입이 동적 결정
⇒ 동적 타이핑(Dynamic Typing)
→ JS = 동적 타입 언어
6-10-2. 동적 타입 언어의 장단점
장점 (유연성):
- 변수 선언 간편
- 타입에 대한 고민 적음
- 빠른 프로토타이핑 가능
단점 (신뢰성 저하):
- 변수 타입이 언제든 변경 가능 → 값 추적 어려움
- 타입 확인 전까지 변수 타입 확신 불가
- 암묵적 타입 변환으로 예측 불가 동작
- 복잡한 프로그램에서 런타임 에러 발생 가능성 증가
동적 타입 언어 사용 시 주의사항:
- 변수 사용 최소화: 꼭 필요한 경우에만 제한적 사용
- 스코프 최소화: 변수 유효 범위를 최대한 좁게
- 전역 변수 지양: 전역 변수 최대한 사용 안 함
- 상수 활용: 변경 불필요 값은
const키워드 사용 - 의미 있는 네이밍: 변수 이름으로 목적과 의미 명확 전달
핵심 원칙:
“동작하는 코드도 중요하지만, 가독성 좋은 코드가 좋은 코드”
코드는 작성 시간 < 읽히는 시간
→ 가독성과 유지보수성을 최우선 고려
학습 점검 문제
문제 1: 데이터 타입 구분
다음 값들의 데이터 타입을 typeof 연산자로 확인했을 때의 결과를 쓰시오.
A. 42
B. '42'
C. true
D. undefined
E. null
F. Symbol('key')
G. { name: 'Lee' }
H. [1, 2, 3]
I. function() {}해답 보기
A. typeof 42 // 'number'
B. typeof '42' // 'string'
C. typeof true // 'boolean'
D. typeof undefined // 'undefined'
E. typeof null // 'object' (JS의 버그)
F. typeof Symbol('key') // 'symbol'
G. typeof { name: 'Lee' } // 'object'
H. typeof [1, 2, 3] // 'object'
I. typeof function() {} // 'function'주의:
null의 typeof 결과가 ‘object’ = JS 초기 버전의 버그- 배열도 객체 → ‘object’ 반환
- 함수는 ‘function’ 반환 (특별 취급)
문제 2: 숫자 타입의 특징
다음 코드의 실행 결과를 예측하시오.
console.log(1 === 1.0);
console.log(0.1 + 0.2 === 0.3);
console.log(10 / 0);
console.log(-10 / 0);
console.log(0 / 0);해답 보기
console.log(1 === 1.0); // true (모든 수를 실수로 처리)
console.log(0.1 + 0.2 === 0.3); // false (부동소수점 연산 한계)
console.log(10 / 0); // Infinity
console.log(-10 / 0); // -Infinity
console.log(0 / 0); // NaN설명:
0.1 + 0.2= 정확히0.3이 아니라0.30000000000000004- 2진법 변환 시 무한소수가 되는 일부 10진수의 특성
- 부동소수점 연산 비교 시 주의 필요
문제 3: 템플릿 리터럴 활용
다음 변수들을 사용하여 “My name is John Doe and I am 25 years old.” 문자열을 템플릿 리터럴로 생성하시오.
var firstName = 'John';
var lastName = 'Doe';
var age = 25;해답 보기
var message = `My name is ${firstName} ${lastName} and I am ${age} years old.`;
console.log(message);
// My name is John Doe and I am 25 years old.
// 표현식 삽입 활용
var message2 = `My name is ${firstName} ${lastName} and I am ${age + 1} years old next year.`;
console.log(message2);
// My name is John Doe and I am 26 years old next year.문제 4: undefined vs null
다음 중 올바른 사용법을 고르고, 그 이유를 설명하시오.
// A
var foo;
console.log(foo); // undefined
// B
var bar = undefined;
// C
var baz = null;해답 보기
올바른 사용:
- A: 올바름 - 변수 선언 시 JS 엔진이 자동으로 undefined 할당
- C: 올바름 - 의도적으로 값 없음 명시 시 null 사용
비권장:
- B: 비권장 - 개발자가 의도적으로 undefined 할당 = 본래 취지에 맞지 않음
이유:
undefined= JS 엔진이 초기화에 사용하는 값- 개발자가 명시적으로 “값 없음” 나타낼 때 =
null사용 - 변수 상태 명확 구분 가능
undefined: 초기화 안 됨 (엔진 관리)null: 의도적으로 값 없음 (개발자 의도)
문제 5: 동적 타이핑의 문제
다음 코드의 문제점을 찾고 개선 방안을 제시하시오.
var data = '100';
console.log(data + 50); // 기대: 150
console.log(data - 50); // 기대: 50해답 보기
문제점:
var data = '100';
console.log(data + 50); // '10050' (문자열 연결)
console.log(data - 50); // 50 (암묵적 타입 변환)+연산자 = 문자열 연결 연산자로 동작-연산자 = 산술 연산자로 동작 → 문자열을 숫자로 변환
개선 방안:
// 방법 1: 명시적 타입 변환
var data = '100';
console.log(Number(data) + 50); // 150
console.log(Number(data) - 50); // 50
// 방법 2: 처음부터 올바른 타입 사용
var data = 100;
console.log(data + 50); // 150
console.log(data - 50); // 50
// 방법 3: parseInt 사용 (정수 변환)
var data = '100';
console.log(parseInt(data) + 50); // 150교훈:
- 동적 타이핑의 편리함에 의존 X
- 명시적 타입 변환으로 의도 명확화
- 변수 타입을 일관되게 유지
문제 6: 심벌의 활용
다음 객체에 외부에 노출되지 않는 비공개 프로퍼티를 추가하시오.
var user = {
name: 'Lee',
age: 20
};해답 보기
// 심벌을 사용한 비공개 프로퍼티
var privateKey = Symbol('privateData');
var user = {
name: 'Lee',
age: 20,
[privateKey]: 'secret information'
};
console.log(user.name); // 'Lee'
console.log(user[privateKey]); // 'secret information'
console.log(Object.keys(user)); // ['name', 'age'] (심벌 키 제외)
// 일반 프로퍼티 순회에서 심벌 제외
for (var key in user) {
console.log(key); // 'name', 'age'만 출력
}
// 심벌로 생성한 프로퍼티 키 = 외부 접근 어려움
// 다른 심벌 값으로는 접근 불가
var anotherKey = Symbol('privateData');
console.log(user[anotherKey]); // undefined활용 사례:
- 프레임워크/라이브러리 내부 구현
- 이름 충돌 방지 필요한 상황
- Well-known symbols (내장 심벌):
Symbol.iterator,Symbol.toStringTag등
심화 학습: 타입 변환과 단축 평가
JS = 동적 타입 언어 → 다양한 타입 변환 발생
명시적 타입 변환 (Explicit Coercion):
개발자가 의도적으로 타입 변환
var num = 10;
var str = String(num); // '10'
var bool = Boolean(num); // true
var str2 = '100';
var num2 = Number(str2); // 100
var num3 = parseInt(str2); // 100암묵적 타입 변환 (Implicit Coercion):
JS 엔진이 자동으로 타입 변환
var x = 10;
// 문자열 연결 연산자
var str = x + ''; // '10' (숫자 → 문자열)
// 산술 연산자
var num = '10' - 0; // 10 (문자열 → 숫자)
var num2 = '10' * 1; // 10 (문자열 → 숫자)
// 불리언 컨텍스트
if ('hello') { // 'hello' → true
console.log('참');
}
if (0) { // 0 → false
console.log('거짓');
}Truthy와 Falsy 값:
JS는 불리언 타입 아닌 값을 불리언 컨텍스트에서 사용 시 Truthy or Falsy로 평가
Falsy 값 (거짓으로 평가):
false0,-0''(빈 문자열)nullundefinedNaN
Truthy 값:
- Falsy 값 제외한 모든 값
// Falsy 값
if (false) { } // 실행 안 됨
if (0) { } // 실행 안 됨
if ('') { } // 실행 안 됨
if (null) { } // 실행 안 됨
if (undefined) { } // 실행 안 됨
if (NaN) { } // 실행 안 됨
// Truthy 값
if (true) { } // 실행됨
if (1) { } // 실행됨
if ('hello') { } // 실행됨
if ({}) { } // 실행됨 (빈 객체도 Truthy)
if ([]) { } // 실행됨 (빈 배열도 Truthy)타입 변환과 단축 평가 자세한 내용 = 9장에서 다룸
심화 학습: 데이터 타입과 메모리
원시 타입과 객체 타입의 메모리 관리:
- 원시 타입: 값 자체가 변수에 저장 (값에 의한 전달)
- 객체 타입: 참조값(메모리 주소)이 변수에 저장 (참조에 의한 전달)
// 원시 타입
var num1 = 100;
var num2 = num1; // 값 복사
num2 = 200;
console.log(num1); // 100 (변경 안 됨)
console.log(num2); // 200
// 객체 타입
var obj1 = { value: 100 };
var obj2 = obj1; // 참조값 복사
obj2.value = 200;
console.log(obj1.value); // 200 (함께 변경됨)
console.log(obj2.value); // 200이러한 차이는 11장에서 더 자세히 다룸