Menu

자바스크립트 원시 타입 7가지 완벽 정리

자바스크립트의 7가지 원시 타입(string, number, bigint, boolean, null, undefined, symbol)과 객체와의 차이점을 예제로 정리했습니다.

원시 타입 7가지, 그리고 나머지 전부

자바스크립트의 값은 크게 두 진영으로 나뉩니다. 한쪽은 일곱 가지 원시 타입(primitive) — 단순하고 불변인 값이고, 다른 한쪽은 객체(object) — 복합적이거나 가변이거나 호출 가능한 모든 것입니다. 자바스크립트 데이터 타입 체계는 값 수준에서 보면 딱 이게 전부예요.

자바스크립트 원시 타입 7가지는 다음과 같습니다:

index.js
Output
Click Run to see the output here.

이 목록에 없는 것들 — 배열, 함수, 날짜, 정규식, 그리고 일반 {} — 은 전부 객체입니다. typeof는 런타임에 타입을 알려주는데, 마지막 줄에서 그 유명한 버그를 볼 수 있죠. typeof null은 1995년부터 'object'를 반환해 왔고 앞으로도 고쳐지지 않을 겁니다. 이미 너무 많은 코드가 이 동작에 의존하고 있거든요.

원시 타입은 값 그 자체이지, 그릇이 아니다

이해에 가장 도움이 되는 사고방식은 이겁니다: 원시 타입은 값 그 자체 입니다. 숫자 3은 3을 담고 있는 상자가 아니라, 그냥 3 그 자체예요. 3을 가진 두 변수는 같은 값을 갖고 있는 것이지, 어딘가 공유된 대상을 가리키는 복사본 두 개가 아닙니다:

index.js
Output
Click Run to see the output here.

원시 타입은 값 자체로 비교하고, 객체는 참조로 비교합니다. 이 차이 하나 때문에 나중에 배열이나 객체를 ===로 비교할 때 "어? 왜 false지?" 하는 순간이 자주 생깁니다.

원시 타입은 불변(immutable)이다

원시 타입은 값을 바꿀 수 없습니다. 언뜻 값을 수정하는 것처럼 보이는 연산도 사실은 전부 새로운 값을 만들어 냅니다:

index.js
Output
Click Run to see the output here.

첫 번째 호출은 새 문자열을 만들어 놓고는 그냥 버려버립니다. 반환값을 아무도 받지 않았으니까요. 두 번째 호출은 name에 값을 다시 할당합니다. 원래의 "ada"는 절대 바뀌지 않았고, 바뀔 수도 없습니다. 숫자도 마찬가지예요. x + 1은 새로운 숫자를 만들어낼 뿐, x 자체를 변경하지는 않습니다.

그래서 문자열이나 숫자에 const를 붙이는 게 실질적으로 안전한 겁니다. 값 자체가 바뀔 수 없고, const는 변수 재할당까지 막아주니까요.

number와 BigInt, 왜 두 종류가 필요할까

자바스크립트의 number는 64비트 부동소수점입니다. 덕분에 산술 연산은 빠르지만 한계도 명확합니다. 정수는 Number.MAX_SAFE_INTEGER(2^53 - 1)까지만 정확하게 표현할 수 있어요.

index.js
Output
Click Run to see the output here.

이 한계를 넘어가면 정수 값끼리 서로 충돌하기 시작합니다. 크기에 상관없이 정확한 값을 유지해야 하는 정수를 위해 바로 bigint 타입이 존재합니다. 숫자 뒤에 n을 붙이면 됩니다:

index.js
Output
Click Run to see the output here.

bigintnumber는 산술 연산에서 섞어 쓸 수 없습니다. 섞어버리면 애초에 bigint를 쓰는 이유인 정밀도가 무의미해지니까요. 데이터베이스 ID, 나노초 단위 타임스탬프, 암호학 관련 연산처럼 큰 수를 다뤄야 할 때 bigint를 꺼내 쓰고, 일반적인 산술은 그냥 number로 처리하면 됩니다.

문자열도 원시 타입입니다

자바스크립트에서 문자열(string)은 객체가 아니라 원시 타입(primitive type)입니다. .length, .slice, .toUpperCase 같은 메서드를 쓸 수 있는데도 말이죠:

index.js
Output
Click Run to see the output here.

내부적으로 문자열의 메서드를 호출하면, 자바스크립트는 잠깐 String 객체로 감싸서 메서드 호출이 동작하게 한 뒤 그 래퍼를 곧바로 버립니다. 이 래퍼까지 신경 쓸 필요는 없고, 문자열이 풍부한 메서드를 제공하긴 하지만 본질은 값처럼 동작한다(불변이며 값으로 비교됨)는 점만 기억하면 됩니다.

작은따옴표, 큰따옴표, 백틱은 모두 동일한 타입을 만듭니다. 다만 백틱은 보간(interpolation)과 여러 줄 문자열을 추가로 지원하는데, 이 부분은 다음 문서에서 다룹니다.

null과 undefined의 차이

"값이 없음"을 뜻하는 원시 타입이 두 개 있는데, 서로 바꿔 쓸 수 있는 건 아닙니다.

undefined는 애초에 값이 할당된 적이 없을 때 나옵니다. 선언만 하고 초기화하지 않은 변수, 전달되지 않은 함수 인자, 존재하지 않는 속성 등이 여기에 해당합니다:

index.js
Output
Click Run to see the output here.

null은 "의도적으로 비어 있다"는 걸 표현하고 싶을 때 여러분이 직접 적는 값입니다:

index.js
Output
Click Run to see the output here.

대략적인 관례는 이렇다. undefined는 언어가 "여긴 아무것도 없어"라고 말하는 방식이고, null은 개발자가 직접 "비어 있음"을 표현하는 방식이다. 둘 다 falsy 값이고, 일반 값과의 동등 비교에서 실패한다는 공통점이 있다. 각각에 대해서는 뒤에서 따로 자세히 다룬다.

symbol: 생성할 때마다 유일한 값

symbol은 원시 타입 중에서 가장 쓰임새가 적다. 심볼은 만들 때마다 고유하며, 설명(description) 문자열이 똑같아도 서로 다른 값으로 취급된다.

index.js
Output
Click Run to see the output here.

심벌은 기존 키와 충돌하지 않는 객체 키로 쓰기에 유용합니다. 예를 들어 어떤 라이브러리가 여러분의 객체에 메타데이터를 붙여야 할 때 심벌을 키로 사용하면, 다른 코드가 그 값을 덮어쓸 일이 없다고 확신할 수 있죠. 이터레이터나 Symbol.iterator 같은 잘 알려진 심벌(well-known symbols)을 다룰 때 다시 만나게 됩니다.

런타임에 타입 확인하기 (자바스크립트 typeof)

대부분의 경우 typeof로 충분합니다. 다만 몇 가지 함정은 기억해 두세요:

index.js
Output
Click Run to see the output here.

null인지 확인할 때는 value === null처럼 직접 비교하면 됩니다. 배열 여부는 Array.isArray(value)로 판별하고요. "이 값이 원시 타입인가?"를 한 번에 확인하는 내장 함수는 따로 없지만, 아래 패턴이 관용적으로 쓰입니다:

index.js
Output
Click Run to see the output here.

원시 타입 vs 객체: 할당할 때 조심해야 할 함정

넘어가기 전에 꼭 한 번 짚고 갈 부분이 있다. 원시 타입은 값 그 자체고, 객체는 참조이기 때문에 할당할 때 동작 방식이 완전히 다르다.

index.js
Output
Click Run to see the output here.

원시 타입끼리는 b = a로 값 자체가 복사됩니다. 하지만 객체는 다릅니다. y = x는 참조를 복사하기 때문에 두 변수가 같은 객체를 가리키게 되고, 한쪽에서 값을 바꾸면 다른 쪽에도 그대로 반영됩니다. 자바스크립트에서 "어? 왜 이게 바뀌었지?" 하는 버그의 1순위 원인이 바로 이겁니다.

핵심 정리

  • 원시 타입은 총 7가지입니다: string, number, bigint, boolean, null, undefined, symbol. 이 외의 모든 것은 객체라고 보면 됩니다.
  • 원시 타입은 불변(immutable)이며 값으로 비교되고, 객체는 변경 가능하며 참조로 비교됩니다.
  • typeof는 런타임에서 타입을 확인할 때 쓰는데, 꼭 외워둘 만한 예외가 두 가지 있습니다. typeof null === "object", 그리고 typeof function === "function"입니다.
  • number는 64비트 부동소수점이라 정수로 안전하게 다룰 수 있는 상한이 있고, 그 이상의 정수를 정확하게 표현해야 할 때 bigint를 씁니다.

다음 편: 문자열과 템플릿 리터럴

자바스크립트에서 가장 자주 다루게 될 원시 타입은 단연 문자열입니다. 백틱(`)으로 감싸는 템플릿 리터럴을 쓰면 문자열을 만드는 일이 훨씬 편해집니다. 값 삽입(interpolation), 여러 줄 문자열, 태그드 템플릿까지 한 번에 해결되거든요. 자세한 내용은 다음 페이지에서 이어집니다.

자주 묻는 질문

자바스크립트의 원시 타입은 총 몇 개인가요?

총 7개입니다. string, number, bigint, boolean, null, undefined, symbol이죠. 이 7가지를 제외한 배열, 함수, 날짜(Date), 일반 객체 등은 전부 객체입니다. 런타임에 값의 타입이 궁금하면 typeof 연산자로 확인할 수 있는데, 딱 하나 유명한 함정이 있습니다. typeof null'object'를 반환한다는 점이에요.

원시 타입과 객체(참조 타입)는 뭐가 다른가요?

원시 타입은 불변(immutable)이고 값 그 자체로 비교됩니다. 33은 그냥 같은 3이에요. 반면 객체는 가변(mutable)이고 참조로 비교되기 때문에, 겉보기에 똑같은 {} 두 개도 서로 다른 값으로 취급됩니다. 변수에 대입할 때도 원시 타입은 값 자체가 복사되지만, 객체는 같은 데이터를 가리키는 참조만 복사됩니다.

자바스크립트 원시 타입이 정말 불변인가요?

네, 그렇습니다. 원시 값은 그 자리에서 변경할 수 없어요. 예를 들어 'hello'.toUpperCase()는 원본 문자열을 바꾸는 게 아니라 새 문자열을 반환합니다. x = x + 1처럼 변수에 재할당하는 것도 기존 값을 바꾸는 게 아니라, 변수가 가리키는 값을 다른 원시 값으로 교체하는 것뿐입니다. 그래서 const name = 'Ada'로 선언해 둬도 name을 기반으로 새 문자열을 얼마든지 만들 수 있습니다.

왜 typeof null은 'object'를 반환하나요?

1995년 초기 자바스크립트 구현 때부터 있던 버그인데, 이미 너무 많은 코드가 이 동작에 의존하고 있어서 끝내 고치지 못한 케이스입니다. 그래서 null을 체크할 땐 그냥 ===로 비교하세요. value === null 이렇게요. undefinedvalue === undefined 또는 typeof value === 'undefined'로 확인하면 됩니다.

Coddy로 코딩 배우기

시작하기