TIL 23-10-17
데이터 타입(심화)
오늘 공부한 내용
- 데이터 타입(심화)
- 데이터 타입의 종류
- 메모리와 데이터에 관한 배경지식
- 변수 선언과 데이터 할당
- 값을 바로 변수에 대입하지 않는 이유
- 기본형 데이터와 참조형 데이터
- 메모리를 기준으로 두 가지 주요 개념에 대해 살펴보자
- 가변값과 가변성
- 중첩 객체의 할당
- 변수 복사의 비교
- 불변 객체
데이터 타입(심화)
데이터 타입의 종류
자바스크립트에서 값의 타입은 크게 기본형(Primitive Type)과 참조형(Reference Type)으로 구분된다. 값의 저장 방식과 불변성 여부로 구분한다.
1. 복제 방식
a. 기본형: 값이 담긴 주소값을 바로 복제
b. 참조형: 값이 담긴 주소값들로 이루어진 묶음을 가리키는 주소값을 복제
2. 불변성 여부
a. 기본형: 불변성을 띔
b. 참조형: 불변성을 띄지 않음
불변성을 이해하기 위해 메모리와 데이터에 대한 내용을 먼저 이해해야 한다. 아래에서 살펴보자.
메모리와 데이터에 관한 배경지식
비트는 컴퓨터가 이해할 수 있는 가장 작은 단위로 0과 1만으로 정보를 표현한다. 비트의 수가 많을수록 표현할 수 있는 정보가 다양해진다. 비트들이 모여서 ‘메모리’가 만들어진다.
그런데 수많은 비트를 일일이 찾는다는 것은 부담이 된다. 비트를 가지고 정보를 표현하기 적합한 개수로 묶은 단위가 바이트이다. 1바이트(byte)는 8비트로 구성된다.
모든 데이터는 바이트 단위의 식별자인 메모리 주소값을 통해서 구분이 된다. 바이트 단위로 데이터를 구분할 수 있다.
64비트 정수를 메모리에 저장한다고 하자. 메모리 주소는 바이트 단위라고 했다. 64비트는 8바이트이므로 64비트 정수는 8개의 연속된 바이트에 저장된다.
변수 선언과 데이터 할당
이제 변수를 어떻게 메모리에 할당하는지 살펴보자.
var str = 'test!';
변수 str과 값이 ‘test!’를 메모리에 저장해야 한다. 변수와 원시 값은 메모리의 스택 영역에 저장된다. 메모리 영역을 변수를 저장하는 변수 영역과 값을 저장하는 데이터 영역으로 나누어져 있다고 하자.
스택 메모리: 프로그램 실행 중에 사용되는 변수와 데이터를 관리하는 데 사용
주소(변수 영역) | … | 1002 | 1003 | 1004 | 1005 | … |
---|---|---|---|---|---|---|
데이터 | str / @5002 |
주소 (데이터 영역) | … | 5002 | 5003 | 5004 | 5005 | … |
---|---|---|---|---|---|---|
데이터 | ‘test!’ | - |
변수 영역의 데이터에는 변수와 값을 참조하는 주소가 저장된다. 현재 사용할 수 있는 메모리 주소에서 가장 가까운 주소에 할당한다고 하자. 변수명 str은 1002번지에 저장이 된다. 하지만 아직 값이 저장된 주소는 참조할 수 없다.
값인 ‘test!’는 데이터 영역의 5002번지 메모리에 저장되었다고 하자. 이제 변수가 저장된 1002번지에서 값이 저장된 5002번지를 참조한다.
“변수 영역과 데이터 영역을 나눌 것 없이 변수 영역의 데이터에 값을 바로 저장하면 되지 않나?” 라고 생각할 수 있다.
값을 바로 변수에 대입하지 않는 이유
그 이유는 먼저 자유로운 데이터 변환을 위해서이다.
“이미 입력한 문자열이 길어진다면?” ➡ 문자열 타입의 값을 변경하고자 한다. `teeeeest!’로. 문자열은 글자수에 따라 크기가 달라진다. 어떤 값이 들어올지 모르기 때문에 미리 메모리 공간을 확보둘 수 없다.
알파벳 한글자를 표현하기 위해 1바이트를 사용한다. 이미 할당된 문자열보다 큰 문자열을 저장해야 하는데 값을 바로 저장하게 된다면 1002번지 이후부터 저장되어 있는 모든 데이터를 밀어놓고 값을 저장해야 할 것이다. 이는 비효율적인 방법이다.
두 번째 이유는 메모리의 효율적 관리하기 위해. 1만개의 변수를 생성해 각각 1을 할당해야한다고 하자.
숫자형은 8바이트의 공간이 필요하다. 값을 바로 변수에 저장한다면 변수 영역에 1만개의 변수를 생성한다. 각각 2바이트로 할당한다고 하자. 1만개 * 8byte = 8만 byte가 필요하다.
주소 | … | 1002 | 1003 | 1004 | 1005 | … |
---|---|---|---|---|---|---|
데이터 | a / 1 | b / 1 | c / 1 | d /1 | … |
변수 영역과 데이터 영역에 별도로 저장한다면 1이라는 값만 따로 저장하면 된다.
-
변수 영역: 1만개 * 2byte = 2만 byte
-
데이터 영역: 8byte
2만 8byte 만을 사용해 훨씬 효율적으로 똑같은 데이터를 저장할 수 있다.
주소(변수 영역) | … | 1002 | 1003 | 1004 | 1005 | … |
---|---|---|---|---|---|---|
데이터 | a / @5002 | b / @5002 | c / @5002 | d / @5002 | … |
주소 (데이터 영역) | … | 5002 | 5003 | 5004 | 5005 | … |
---|---|---|---|---|---|---|
데이터 | 1 | - | - |
이러한 이유로 값을 바로 변수에 대입하지 않고 값을 참조하는 방식을 사용한다.
기본형 데이터와 참조형 데이터
메모리를 기준으로 두 가지 주요 개념에 대해 살펴보자.
-
변수 vs 상수
-
변수: 변수 영역 메모리 변경 가능
-
상수: 변수 영역 메모리 변경 불가능
-
-
불변하다 vs 불변하지 않다
-
불변하다: 데이터 영역 메모리 변경 불가능
-
불변하지 않다: 데이터 영역 메모리 변경 가능
-
변수는 변수 영역의 메모리를 변경할 수 있다. 변수에 새로운 값을 재할당하면 메모리 공간에 저장되어 있던 값을 변경하는 것이 아니라 새로운 메모리 공간을 확보하고 재할당한 값을 저장한 후, 변수는 새롭게 재할당한 값을 가리킨다. (여기서 값은 원시 값에 한함)
값의 이러한 특성을 불변성이라 한다. 재할당 이외에는 변수 값을 변경할 수 없어 예기치 않게 변수 값이 변경되는 것을 방지한다. 이전 값이 더이상 사용되지 않는다면 가비지 콜렉터의 수거 대상이 된다.
가비지콜렉터는 애플리케이션이 할당한 메모리 공간을 주기적으로 검사하여 더 이상 사용되지 않는 메모리를 해제하는 기능
상수는 변수 영역의 메모리를 변경할 수 없으므로 데이터 영역의 주소가 고정된다. 재할당하려고 하면 에러가 발생한다.
데이터 타입은 기본형과 참조형으로 구분된다고 하였다. 이제 참조형 데이터를 살펴보며 가변값과 가변성에 대해 알아보자.
가변값과 가변성
var obj1 = {
a: 1,
b: 'bbb,
};
참조형 데이터를 할당하려고 한다. 기본형 데이터의 변수 할당과는 다르게 객체의 변수(프로퍼티) 영역이 별도로 존재한다. obj1는 객체이므로 별도의 메모리 영역이 있다. 객체는 힙 영역에 저장된다.
힙 메모리는 동적으로 할당된 데이터, 즉 실행 중에 생성된 객체와 배열, 함수, 문자열 등을 저장하는 데 사용
주소(변수) | 1001 | 1002 | 1003 | 1004 |
---|---|---|---|---|
데이터 | obj1 / @7003 |
주소(데이터) | 5001 | 5002 | 5003 | 5004 |
---|---|---|---|---|
데이터 | 1 | ‘bbb’ |
주소(obj1) | 7103 | 7104 | 7105 | 7106 |
---|---|---|---|---|
데이터 | a / @5001 | b / @5002 |
이제 “참조형 데이터는 불변하지 않다(=가변하다)”라고 하는 이유를 알아보자.
var obj1 = { a: 1, b: 'bbb' }
obj1.a = 2
obj1의 프로퍼티 키 a의 프로퍼티 값을 2로 변경한다.
-
변경할 값인 2를 데이터 영역에서 찾는다.
-
존재하지 않으므로 5003번지에 값을 저장하고 obj1를 위한 별도 영역에서 새로운 주소를 가리킨다.
주소(변수) | 1001 | 1002 | 1003 | 1004 |
---|---|---|---|---|
데이터 | obj1 / @7003 |
주소(데이터) | 5001 | 5002 | 5003 | 5004 |
---|---|---|---|---|
데이터 | 1 | ‘bbb’ | 2 |
주소(obj1) | 7103 | 7104 | 7105 | 7106 |
---|---|---|---|---|
데이터 | a / |
b / @5002 |
데이터 영역에 저장된 값은 여전히 불변값이지만, obj1을 위한 별도 영역은 변경 가능하다. 이 때문에 참조형 데이터를 ‘불변하지 않다’라고 한다.
중첩 객체의 할당
중첩객체란, 객체 안에 또 다른 객체가 들어가는 것을 말한다. 참조형 데이터를 할당했던 것과 다르지 않다. 마찬가지로 메모리 관점에서 어떻게 할당되는지 간략히 살펴보자.
var obj = { x: 3, arr: [3, 4, 5] }
-
obj 객체를 1001번지에 할당한다.
-
obj 객체 값은 7103번지에 할당되어 변수는 이를 가리킨다.
-
프로퍼티 x와 arr가 obj를 위한 별도 메모리 영역에 저장된다.
-
x는 5001번지에 값을 저장하고 해당 주소를 참조한다.
-
arr는 객체이므로 arr를 위한 별도 메모리 영역이 존재한다.
- arr 인덱스와 값이 저장된 주소를 각각 가리킨다.
-
주소 (변수) | 1001 | 1002 | 1003 | 1004 |
---|---|---|---|---|
데이터 | obj / @7103 |
주소 (데이터) | 5001 | 5002 | 5003 | 5004 |
---|---|---|---|---|
데이터 | 3 | 4 | 5 |
주소 (obj) | 7103 | 7104 | … |
---|---|---|---|
데이터 | x / @5001 | arr / @8104 |
주소 (arr) | 8104 | 8105 | 8106 | … |
---|---|---|---|---|
0 / @5001 | 1 / @5002 | 2 / @5003 |
참조 카운트가 0인 객체는 더 이상 사용되지 않으므로, 가비지 컬렉터에 의해 메모리에서 제거된다.
참조카운트란? 객체를 참조하는 변수나 다른 객체의 수를 나타내는 값.
변수 복사의 비교
지금껏 기본형 데이터와 참조형 데이터를 메모리에 어떻게 선언하고 할당하는지 알아보았다. 기본형 데이터 변수와 참조형 변수를 복사했을 때 어떤 차이점이 있는지 살펴보자.
var a = 10;
var obj1 = { c: 10, d: 'ddd' };
var b = a;
var obj2 = obj1;
주소(변수) | 1001 | 1002 | 1003 | 1004 | 1005 | … |
---|---|---|---|---|---|---|
데이터 | a / @5001 | obj1 / @7103 | b / @5001 | obj2 / @7103 |
주소 (데이터) | 5001 | 5002 | 5003 | 5004 | 5005 | |
---|---|---|---|---|---|---|
데이터 | 10 | ‘ddd’ |
주소 (obj1) | 7103 | 7104 | … |
---|---|---|---|
데이터 | c / @5001 | d / @5002 |
어려운 점은 없다. 이제 복사 이후 값을 변경해보자.
var a = 10;
var obj1 = { c: 10, d: 'ddd' };
var b = a;
var obj2 = obj1;
b = 15;
obj2.c = 20;
console.log(obj1.c, obj2.c) // 20, 20
주소 (변수) | 1001 | 1002 | 1003 | 1004 | 1005 | … |
---|---|---|---|---|---|---|
데이터 | a / @5001 | obj1 / @7103 | b / |
obj2 / @7103 |
주소 (데이터) | 5001 | 5002 | 5003 | 5004 | 5005 | |
---|---|---|---|---|---|---|
데이터 | 10 | ‘ddd’ | 15 | 20 |
주소 (obj) | 7103 | 7104 | … |
---|---|---|---|
데이터 | c / |
d / @5002 |
기본형과 참조형의 변수 복사 시 주요한 차이점은
-
기본형
- 값(15)을 데이터 영역에서 찾아보고 없으면 생성한다
- 값이 존재하던 주소 or 생성한 주소를 변수 영역(b)에서 참조한다.
-
참조형
-
값(20)을 데이터 영역에서 찾아보고 없으면 생성한다
-
값이 존재하던 주소 or 생성한 주소를 obj2의 별도 영역(7104)에서 참조한다.
-
여기서 문제가 발생한다. obj1과 obj2는 같은 주소를 참조하고 있다. 따라서 obj2에서 프로퍼티 c의 값을 변경했으나 obj1까지 변경된 것을 알 수 있다. 똑같은 주소를 바라보고 있기 때문에.
분명 이것은 기대했던 결과는 아닐 것이다. ‘복사’를 원했지 ‘참조’를 원한 것은 아니다.
객체의 프로퍼티에 접근해서 값을 변경하는 것이 아니라 ‘객체 자체’를 변경하는 방식으로 값을 바꿔야 한다.
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
b = 15;
obj2 = { c: 20, d: 'ddd'};
console.log(obj1.c, obj2.c); // 10, 20
주소 (변수) | 1001 | 1002 | 1003 | 1004 | 1005 |
---|---|---|---|---|---|
데이터 | a / @5001 | b / |
obj1 / @7103 | obj2 / |
주소 (데이터) | 5001 | 5002 | 5003 | 5004 | 5005 |
---|---|---|---|---|---|
데이터 | 10 | ‘ddd’ | 15 | 20 |
주소 (obj1) | 7103 | 7104 | … |
---|---|---|---|
데이터 | c / @5001 | d / @5002 |
주소 (obj2) | 8204 | 8205 | … |
---|---|---|---|
데이터 | c / @5004 | d / @5002 |
obj2를 위한 별도 영역이 생성되었다. 이유는 객체 리터럴로 값을 새롭게 생성하였기 때문이다. obj1과 obj2는 별도의 주소를 가리키고 있기 때문에 obj1과 obj2는 다른 객체이다.
부족한 내용 / 궁금한 내용
-
불변 객체
- 얕은 복사, 깊은 복사
-
스택과 힙
느낀점
변수 선언과 원시 값과 객체 값의 할당, 재할당을 메모리 관점에서 어떻게 동작하는지 강의를 들으며 이해할 수 있었다. 생각하던 것과 비슷하긴 했지만 스택과 힙 영역으로 나누어지고 참조하는 방식을 설명할 수 있게 되었다.
3주차 강의로 들어서면서 이렇게까지 깊은 개념을 다루는지 몰랐다. 그럼에도 알고 싶었던 부분이라서 재밌게 들었고 시간 가는 줄 몰랐다. 현재 내배캠 2주차에서 곧 3주차 넘어가는 시점에서 가장 몰두해서 학습한 날이 오늘이다.
앞으로 컨텍스트, this, 콜백함수, 동기/비동기, DOM, 클래스 등.. 학습할 내용이 너무 많이 있다. 심지어 내일부터는 과제기간이기도 하다. 이번 주 내로 강의는 완강하고 과제 병행하면서 부족한 개념을 보충하는 방향으로 갈 것.
댓글남기기