정말 중요한 개념을 시작하네요.
이 개념을 정확히 알아야, 객체나 배열을 다룰 때 발생하는 참조 오류를 정확히 수정할 수 있게 돼요! 🙇🏻♂️
매우 간단한 주제이지만, 한편으로는 그렇기에 놓쳐서는 안될 키워드입니다.
같이 살펴볼까요! 🚀
원시 타입과 객체 타입은 정말 명확한 차이가 존재해요.
"메모리 공간에 실제 값을 저장하는가, 참조 값을 저장하는가"
여기서 원시 타입이 실제 값이고, 객체 타입이 참조 값을 의미합니다.
여기서 의문이 들 수 있어요.
🖐🏻 응? 도대체 실제 값은 뭐고, 참조 값이 뭐죠? 우리 다 값이 나오잖아요!
말이 좀 어렵죠?
예시를 한 번 들어볼게요.
let a = 2;
a = 4;
우리가 정말 흔하게 사용하는, 변수의 값을 핸들링하는 코드에요.
분명 a
에는 결국 4
라는 값이 들어있습니다.
그런데, 이렇게 한 번 질문을 던져볼게요.
🚦
a
의 메모리 주소 값은 바뀌었을까요?
답은, 네! 바뀌었습니다. 이유는 원시 값은 곧 메모리에 읽기만 가능한 값이기 때문이에요.
변수를 통해 할당한 값은 단순히 읽기로만 접근이 가능해서, 우리가 직접 이를 조작할 수가 없어요.
대신에 변수는 값을 바꿔야 할 수 있어야 하므로, 대안은 새로운 메모리 공간을 할당해서 그곳에 새로운 값을 넣습니다. 이것이 변수에 값을 새롭게 할당하는 로직이에요.
🔥 "으, 너무 복잡해요! 왜 이렇게 만든 거죠?"
이는 상태 관리를 더욱 편하게 하기 위함이에요.
우리, 에러가 나고 있다고 가정해봅시다. 자기가 모르는 사이에 메모리가 참조하고 있는 값이 변경된다면, 과연 어떻게 에러를 추적할 수 있을까요?
나아가, 앞으로 그 엔진을 믿고 사용할 수 있을까요? 데이터 저장에 대한 신뢰성이 매우 낮아진다는 문제가 발생합니다.
따라서 원시 값은 적어도 자신이 존재하는 공간에서의 값을 변경할 수 없도록 해요. 그것이 바로 불변성입니다.
음, 이 파트도 좀 아쉽기는 합니다.
어떤 게 아쉽냐면요! 바이트에 관한 내용입니다.
"ECMAScript 사양에 문자열 타입(2바이트)와 숫자 타입(8바이트) 이외의 원시 타입은 크기를 명확히 규정하고 있지는 않아서...(중략)"
좀 더 깊은 이해를 하기 위해서는 "아스키 텍스트"와 "유니코드"를 이해해야 해요.
사실 저 크기도 위의 기준에 따라 다릅니다.
혹시 메모장에서 옵션을 끄적이다 보면 ASCII Text / ANSI Text
와 같은 타입을 보셨나요?
이것이 바로 아스키 텍스트입니다. 이 아스키 텍스트로 계산하면 문자는 2바이트가 맞아요.
하지만 요새는 대개 UTF-8
을 많이 쓰죠? (이건 파일을 만지다 보면 흔히들 마주한 타입이라고 생각해요!) 이런 것들이 바로 유니코드입니다.
이 UTF-8
에서는 한글이 3바이트이고, 숫자나 영어가 1바이트에요. 4바이트 이상의 문자도 있을 수 있구요.
나아가 UTF-16
에서는 2바이트로 통일되나 싶지만, 희귀한 한자의 경우에는 2바이트 이상이라고 합니다! UTF-32
도 있지만, 대개 프로그래밍 환경에서 많이 쓰인다고 하네요.
이제 호기심이 많은 여러분은 이런 생각이 드실 수 있어요.
🙇🏻♂️ "응? 왜 이렇게 유니코드란 게 생겨나게 됐죠?!"
좀 더 이야기를 해볼까요? 각 나라의 문자를 각자가 사용하면 정~말 깔끔하겠죠.
그런데, 우리는 가끔 영어로 이야기해야 할 수도, 갑자기 로마자로 이야기해야 할 순간이 생겨요.
ASCII
텍스트가 호령하던 그 시절에는, 이런 외국 문자를 보낼 시, 글자가 호환이 되지 않는 경우가 비일비재했답니다. 인터넷이 지향하는 문서로써의 기능을 못하는 것이죠!
따라서 각 나라의 문자 양식을 통일한 것이 UTF-8
에요.
그런데 문제는... UTF-8
에서 할당할 수 있는 65,535개의 문자를 이미 넘어버렸어요.
따라서 현재 시대는 고정 2바이트 인코딩이 불가능한 겁니다! 다만 UTF-8
이 주는 단일 인코딩 방식은 UTF-16
과 혼동할 여지가 없어 사랑을 받는다고 하네요 🥰
허허... 갑자기 이야기하다가 인코딩 이야기로 넘어가버렸네요.
다시 문자열과 불변성에 대해 이야기를 해보죠!
이전에 변수 파트에서 비슷한 얘기를 했어요.
어떻게 보면 문자열의 경우도 이런 원시 값의 특혜를 받은 것이라구요!
우리는 자바스크립트에서 한 변수에 1개의 문자만 입력했는데, 어느 순간 1억 개의 문자열을 입력할 수도 있어요.
그러면 컴퓨터는 어떤 처리를 해야 할까요?
쭉~ 바이트에 맞는 메모리 공간을 할당하다가, 다른 변수가 차지하고 있는 메모리 공간에 부딪힌다면?
그러면 원시 값의 경우에는 값을 핸들링하려면 다른 변수들의 메모리 공간을 바꿔주어야 하고, 값을 또 변경해야 해요. 그런데, 이런 다른 변수들이 5만 개 정도 있다고 치면, 과연 이 변경이 깔끔하게 이루어질까요?
하지만 원시 값처럼 재할당 시 변수를 다른 메모리 공간에 저장해두면, 1억 바이트 만큼이 비어 있는 메모리 공간을 찾아 넣어주면 되겠죠. 그리고 변수는 그 메모리 공간이 위치한 주소를 참조하면 되는 거구요.
따라서 더욱 안정성 있는 프로그램 운영이 가능하다는 거에요!
사실 결국 원시 값은 새로운 메모리 공간에 값을 넣고, 변수를 그 메모리 공간을 참조하게 한다는 것과 같은 맥락입니다. 이 파트는 왜 있는 걸까요... 😭
여기서 포인트는, 값이 전달되는 것이 아니라, 메모리 주소가 전달된다는 것이네요.
네, 이 내용만 보면 다 복사 + 붙여넣기입니다. 넘어가죠!
객체는 정말 동적인 타입이에요.
예시를 들자면, 객체를 하나의 자동차라고 정해봅시다.
자동차에 들어갈 수 있을 옵션이 몇 개가 있을까요? 당장 떠오르는 것만 해도 셀 수가 없을 정도죠.
이 이야기를 좀 다르게 풀어봅시다.
그렇다면, 객체라는 애에 들어갈 프로퍼티의 개수는 몇 개일까요?
이 역시 데이터가 어떻게 정의되었는지에 따라 정말 무한할 수 있다는 것입니다.
그렇다면, 객체를 생성할 때의 비용도 정말 커지겠군요?
따라서 자바스크립트 엔진마다 다르지만, 성능 추구를 위해 기존 객체 지향 프로그래밍에서 말하는 해시 테이블보다 나은 객체를 만들게 되었어요.
대표적으로, V8에서 자주 언급되는 히든 클래스가 있겠군요!
이 역시 나중에 꼭! 머지 않은 시기에 파보려 합니다. 😉
결국 객체는 무한히 커질 수 있어요. 따라서 이를 기존 변수처럼
은 상당한 비용이 들 수밖에 없겠죠?
이에 대한 대안이 바로, 변수에 메모리 주소 값을 할당하는 참조 값이에요.
이를 통해 객체는 따로 값을 새롭게 만들지 않고도 기존 객체의 값을 갖게 되었군요!
하지만 문제가 생겼어요.
처음에 막상 그냥 들으면 이해가 가지 않을 수 있어요.
예시를 한 번 살펴보죠!
const person = { name: 'Jaeyoung' }
const b = person;
b.age = 29;
console.log(person) // { name: 'Jaeyoung', age: 29 }
아니! 이럴 수가...
결국 이러한 참조에 의한 전달은 객체의 불변성을 깨뜨리는, 상당한 문제를 일으키는 군요!
따라서 자바스크립트에서 객체를 핸들링한다는 것은 불변성이라는 것을 제대로 이해해야 하겠군요. 이는 Virtual DOM으로 컴포넌트의 상태를 비교하는 SPA 프레임워크에서 매우 중요합니다.
이를 해결하는 대표적인 방법으로는, spread
연산자와 객체 리터럴을 활용해주는 거에요.
우리가 함께 배운대로, 값 그 자체로 인식되는 객체 리터럴을 통해 새롭게 객체를 만들어주면 되겠죠? 🥰
const person = { name: 'Jaeyoung' }
const b = { ...person };
b.age = 29;
console.log(person) // { name: 'Jaeyoung' }
나머지 내용을 보니까 다 고만고만한 내용이네요.
결국 핵심만 다시 정리해볼까요?
- 두 타입 모두 메모리 공간의 주소를 통해 접근하는 것은 같다.
- 차이라면 메모리 공간에 어떤 값이 저장되어 있느냐이다. 원시 값은 진짜 값이, 참조 값은 메모리 주소가 들어가 있다.
- 원시 값은 여러 변수를 통해 접근할 때 불변성을 지켜주지만, 객체 타입은 성능을 위해 어쩔 수 없이 같은 메모리 주소 공간을 공유하게 되어 불변성의 문제가 발생한다.
음... 딱 이 정도만 알아도 정말 좋겠네요!
정말 빠르게 훑고 지나간 것 같아요.
정말 간단하지만, 한편으로는 이로 인해 꽤나 많은 실수가 실제로도 빈번히 일어납니다.
제 경우에는 데이터의 불변성을 지켜주며 안정적으로 코드를 관리하는 걸, 잘하는 개발자를 판단하는 기준이라고도 생각하는 편이에요.
이 파트를 통해 모두들 안정적인 코드를 짜보도록 열심히 노력해봐요! 이상 🖐🏻