코딩과 글쓰기 사이에는, 오랜 시간 의자에 앉은 채로 타자기를 두들기는 작업이라는 점 외에도 많은 공통점이 있겠지만, 여기선 단어와 문장을 사용한다는 것을 언급하고 싶다. 단어와 문장은 어떤 개념의 표상(Representation)이다. 가령 어떤 특정한 개념을 여러 나라의 언어로 표현할 수 있지만 그것이 가리키는 대상이 하나인 것 처럼, 단어와 문장은 어떤 개념을 표현하기 위한 껍데기와 같다. 그런 면에서 변수에 값을 할당하는 과정과 어떤 단어에 어떤 개념을 매칭시키는 사회적 합의 과정은 유사한 점이 있다. 다만 전자는 유한한 메모리 크기 안에서 관계도를 그리는 반면, 후자는 무한한 마음 속 공간에서 매칭이 이루어진다는 차이가 있다. 따라서 코딩은 어떤 정보를 어느 정도 크기의 빈 메모리 공간에 할당해야 할 지 정하는 것이 중요하다.
데이터 타입(Data type)은 데이터를 할당하기 위해, 그 변수가 가지는 메모리 공간의 크기 정보를 담고있는 표현 형식이다. 자바 스크립트의 경우 변수를 선언하면, 컴퓨터는 그 값의 데이터 타입을 판별한 뒤 그에 맞는 메모리 공간을 확보한다 예를 들어 int 데이터 타입의 어떤 변수는, 4byte의 메모리 공간을 할당받으므로, -2,147,483,648 부터 2,147,483,647까지의 정수 정보를 가질 수 있다. 반면 char 데이터 타입의 경우 1byte, 즉 256개의 알파벳 등을 구분할 수 있다. int와 char와 같은 데이터 타입을 기본 타입(Primitive Type)이라고 부른다.
자바스크립트의 원시 타입은, 참조 타입과 달리, 하나의 변수는 자신에게 할당된 데이터 공간에 하나의 데이터를 가지고 있다. 이 변수는 새로운 데이터를 가질 수 있으며, 기존의 데이터를 가리키는 주소와 변수 간의 링크가 사라졌을 뿐 기존의 데이터 자체는 여전히 메모리 공간에 남아있다(immutablity). 또 변수를 재할당해도 원시 타입 변수의 데이터를 받아온 다른 원시 타입 데이터의 값은 변하지 않는다.
let a = 2;
let b = a;
a = 3;
console.log(a) // a = 3
console.log(b) // b = 2
자바스크립트의 원시 타입에는 다음과 같은 카테고리가 존재한다.
데이터 타입 | 형태 | 메모리의 크기 |
---|---|---|
Boolean 타입 | True, False | 1bit |
Null 타입 | null | 1byte |
Undefined 타입 | undefined | - |
Number 타입 | - | 8byte(정수/소수 구분이 없음) |
String 타입 | - | 한 문자 당 2byte |
그 외에도 객체의 식별자를 대신할 수 있는 Symbol Type과, Number Type이 다룰 수 없는 매우 크거나 작은 수를 표현할 수 있는 Bigint Type이 존재한다.
조사 과정에서 원시 타입과 관련된 몇 가지 궁금한 점을 여기 정리해둔다.
첫 번째는 Null 타입과 Undefined 타입의 메모리 할당이다. Null type은 존재하지 않는(nothing) 값, 알 수 없는 값(unknown)이 할당되어 있음을 가리킨다. Undefined type은 변수에 값이 할당되어 있지 않은 상태를 가리킨다. 두 타입은 의미가 상당히 유사하지만, 자바스크립트는 이 두 타입을 구분하여 사용하고 있는데, 두 타입의 구분 기준 중 하나는 초기화 여부이다.
const a; // 변수 선언(Declaration)
const b = 2; // 변수 정의(Definition)
변수의 선언 단계에서 자바스크립트는 그 변수에 undefined를 할당한다. 이 단계에서 변수는 메모리 상의 어떤 주소를 가리키고 있지만, 그 메모리 공간은 초기화되지 않은 상태에 있다. 반면 null을 할당받은 변수는, 초기화된 후의 빈 메모리 공간을 갖는다. 이때 null 타입의 변수가 갖는 빈 메모리 공간의 크기는 1byte이다. 또 한가지 사소한 차이점은 null은 개발자가 의도한 데이터의 부재 상태를 가리키는 반면, undefined는 호이스팅 과정과 같이 개발자의 의도와 관계 없이 자바스크립트가 선언(또는 정의)한 것일 가능성이 크다는 점이다.
두 번째는 String 타입의 주소 할당 문제이다. 자바스크립트는 문자당 2byte의 데이터 공간을 할당하지만, 문자가 모인 문장이 정의된 문자열 변수는 하나이다. 이 문자열 변수는 모든 문자의 주소 공간을 가리키고 있을까? 그렇지 않다. 문자열 변수와 매칭된 주소는 문자열의 시작 문자의 주소이며, 문자열의 끝에 null값을 추가함으로써 문자열의 끝을 알린다. 따라서 한 문자는 2byte의 공간을 갖지만, 한 문자열은 그 총합에 더해 1byte를 추가로 갖는다.
참조 타입이란 원시 타입을 제외한 모든 타입을 가리킨다. 원시 타입 변수가 값을 가리키는 주소 정보를 가진다면, 참조 타입 변수는 다른 변수를 가리키는 주소 정보를 가진다. 때문에 참조 타입 변수가 가리키는 다른 타입의 변수를 재할당할 경우, 값이 담긴 주소를 참조한 모든 참조 타입 변수가 가리키는 값 또한 달라진다.
let a = {
value : 'a'
}
let b = a // 얕은 복사(Shallow copy)
console.log(b.value) // b.value = a
b.value = 'b'
console.log(a.value) // a.value = b
이런 참조 타입의 특징 때문에, 참조 타입 변수를 복사하는 방법은 얕은 복사(shallow copy)와 깊은 복사(deep copy) 두 가지로 나뉜다. 얕은 복사는 원본 참조 변수와의 관계가 유지되는 경우, 깊은 복사는 연결 관계가 완전히 끊어진 경우를 가리킨다. 흔히 그 편의성으로 인하여 클라이언트-서버 사이의 데이터 전송시 JSON.stringify 함수와 JSON.parse 함수를 사용하는데, 이는 대표적인 깊은 복사의 사례이다.
자바스크립트의 참조 타입에는 다음과 같은 카테고리가 존재한다.
데이터 타입 | 주 사용 목적 |
---|---|
Object 타입 | 동적 데이터 저장 및 전송 |
Array 타입 | 동적 데이터 저장 공간 |
Date 타입 | 정해진 날짜, 시간 반환 |
Function 타입 | 메서드를 담고있는 인스턴스 생성 |
Primitive wrapper 타입 | 원시 값 조작을 위한 참조 타입 |
흥미로운 점은 자바스크립트가 Function을 참조 타입 변수에 포함시키고 있다는 점이다. 여기서 function 타입의 변수는 실질적인 메서드를 가리키는 주소 정보를 가지고 있다.
자바스크립트의 원시 타입 변수인 number, string, 그리고 boolean은 서로 형변환이 가능하다. 때로 형변환은 개발자의 의도에 따라 명시적으로 일어날 수도 있으나, 산술 연산자의 사용과 같이 암묵적으로 일어날 수도 있다.
let a = 1
let b = 2
console.log(a + b) // number 3
let c = "c"
console.log(a + c) // string "1c"
//string을 만나는 순간 형변환이 일어납니다.
console.log(a + b + c) // string "3c"
console.log(c + b + a) // string "c21"
명시적 형변환의 용례는 다음과 같다.
Number("123") // string "123" to number 123
ParseInt(“27”) // 27
ParseInt(0033); // 27 (8진수)
ParseInt(0x1b); // 27 (16진수)
ParseFloat("123.123") // number 123.123
String("123") // 123 to "123"
a = "100"
a.toString() // string "100"
a.toString(2) // string "1100100"
// 인자로 param진수로 변환하여 반환한다.
b = 100.123
b.toFixed(2) // string "100.12"
// 소수점 param자리까지 올림
Boolean(100) // true
Boolean("1") // true
Boolean([]) // true
Boolean(null) // false
Boolean(undefined) // false
Boolean() // false
자바스크립트는 변수 선언 시 형(type)을 개발자가 지정해주어야 하는 자바, 또는 C와 같은 강 타입(또는 정적 타입)언어와 구분되는 약 타입(또는 동적 타입, 느슨한 타입) 언어이다. 약 타입 언어인 자바스크립트는 변수 선언 단계에서 의도적인 형 지정을 요구하지 않아 강 타입 언어보다 편의성에서 강점이 있다. 또한 자바스크립트는 다양한 변수 타입을 Number, String, Boolean 등으로 간추려 형변환이 요구되는 부분에서도 상대적으로 쉽게 의도하는 것을 구현할 수 있도록 하고있다.
시간이 다르게 하드웨어 성능이 개선되는 현 시대에서 형변환의 필요성은 다소 감소한 부분이 있다는 점에서 자바스크립트의 동적 타입 할당은 그 강점이 계속 증가하고 있다고 볼 여지가 있다. 그러나, 모든 웹 사용자가 양질의 하드웨어를 사용할 수 없다는 점, 클라우드 서버의 메모리 관리는 곧 기업의 순수익과 직결된다는 점, 빠른 데이터 전달을 통한 사용자 경험의 향상, 다른 언어 기반 프로그램과의 연동성, SQL DB 사용시 용이한 점 등 여전히 개발자가 변수 타입을 지정하는 것은 필수적이다. 때문에 자바스크립트도 탄생 이후 개발자에게 변수 타입을 조정할 수 있는 선택권을 부여할 필요성에 대하여 꾸준히 목소리가 있어왔다.
Typescript와 Flow는 자바스크립트 개발자에게 이러한 선택권을 부여해준다. 그러나 Typescript와 Flow는 사용법이나 구동 방식 등에서 차이가 있다. Typescript는 개발자가 미리 변수 타입을 지정해야 하고, 이것은 변수 타입 추론을 기본적으로 지원하는 Flow에 비해 비교적 엄격하다. 또 (비록 정확하게 일치하는 표현은 아니지만)Typescipt로 짜인 코드를 자바스크립트로 컴파일하는 과정을 거치는 것과 비교해 Flow는 자바스크립트로 짜인 코드 위에서 어노테이션처럼 작동한다. 때문에 기존에 자바스크립트 기반으로 서비스하고 있다가 타입 지정이 가능한 다른 언어로 넘어갈 때 Flow가 Typescript보다 장점이 있다고 볼 여지가 있다. 다만 Typescript는 facebook 내부의 특정 문제를 해결하기 위해 개발된 Flow보다 광범위하고 장기적인 목적의식과 개발 플랜을 따를 여지가 크다고 보는 견해가 존재한다참조. VSCode, Angular, Reddit 등이 Typescript를 사용하고 있는 대표적인 사례이다.