배워서 남 주자

변수, 선언, 할당, 메모리 그게 다 뭔데? (부제: 원시자료형과 참조자료형의 차이)

미래에서 온 개발자 2022. 11. 7. 11:56

오늘의 주제는 변수에 원시자료형(number, string, boolean, undefined, null)을 담을 때와 참조자료형(객체, 배열, 함수)을 담을 때 데이터를 저장하는 공간(메모리)에서는 어떤 일이 일어나는지를 두 자료형의 차이점을 중심으로 살펴보고자 한다. 

 

우리가 프로그램을 짜는 이유는 데이터를 내가 원하는 방식으로 처리, 가공하여 재생산하고자 함이 크다. 그렇기 때문에 이런 데이터 핸들링을 원활하게 하기 위해서는 데이터를 저장해 두고 필요한 때에 꺼내 쓸 수 있어야 한다. 컴퓨터에서 데이터를 저장하는 공간이 바로 우리가 아는 '메모리'이다. 우리는 메모리에 데이터를 저장해두고 원할 때에 해당 데이터를 꺼내와서 그 데이터를 읽기도 하고 수정하기도 하고 삭제하기도 한다. 이러한 과정을 쉽게 도와주는게 바로 '변수'이다. 우리가 데이터를 알아보기 쉽도록 데이터에 '이름표(label)'를 붙여주는 것이라고 생각하면 쉽다. 

 

const student1 = {
    name: '김첨지',
    id: '20220418',
    score: 85,
}

const student2 = {
    name: '윤석사',
    id: '20114729',
    score: 99,
}

위의 예시는 학생의 이름(name)과 학번(id), 점수(score)라는 데이터를 취급한다고 할 때 해당 데이터를 객체에 담는 코드이다. 각각의 데이터를 student1, student2라는 변수명에 담아주었다. 변수는 일종의 label 역할을 한다. 

 

데이터(자료)는 크게 두 가지 타입이 있는데 위에 언급한 원시자료형(primitive data type )과 참조자료형(reference data type)으로 나뉜다. 위의 예시에서 객체는 참조자료형에 해당한다. 두 가지 데이터 타입을 나눠서 구분하는 이유는 당연히도 둘 사이에 차이점이 있기 때문이다. 

 

 

위의 학생 데이터는 잠시 잊고 새로운 예시를 들어보자. 

 

let title;
title = '어제보다 나은 내일'

 

title이라는 변수에 '어제보다 나은 내일'이라는 문자열(string)을 담아주었다. 이제 앞으로 '어제보다 나은 내일'이라는 데이터(자료)를 쓰고 싶을 때는 title이라는 변수를 사용해 데이터를 꺼내오고, 수정할 수 있다. 그럼 우리가 이렇게 변수를 선언하고 해당 변수에 데이터('어제보다 나은 내일'이라는 문자열 값)를 할당할 때 메모리에서는 어떤 일이 일어나는 걸까? 사설이 길었는데 이제부터가 오늘의 본론이다. 

 

메모리에는 변수와 데이터가 들어갈 수 있는 자리가 마련되어 있다. 각각의 공간은 stack이라는 선형 자료구조로 마련되어 있는데, 쉽게 생각해서 사물함이 쭉 마련되어 있고 사물함 한 칸 한 칸에 각각의 데이터를 담을 수 있다고 생각하면 된다. 이 때 변수를 선언한다는 것은 사물함 한 칸에 변수명에 해당하는 이름표를 붙여주는 행위인 셈이다. 

 

let title; // 사물함 한 칸에 'title'이라는 이름표를 붙인다.
title = '어제보다 나은 내일' // title이라는 이름표가 붙은 사물함 칸을 열고 그 안에 '어제보다 나은 내일'이라는 데이터를 담는다.

let userID = 'kimcoding';
let createdAt = '2022-10-20';

 

title 뿐만 아니라 userId와 createdAt이라는 이름표를 또 만들어서 각각 사물함 한 칸씩 자리를 마련해 주고, 해당하는 사물함 칸 안에 userID 정보와 해당 user가 블로그를 개설한 일자 정보를 담아주었다. 

 

이때 담은 정보들은 모두 문자열로 원시자료형에 속한다. 원시자료형은 지금 든 예시처럼 사물함 칸 안에 데이터를 담을 때 해당 값을 바로 담을 수 있다. 정리하자면 변수를 선언한다는 것은 메모리에 자리를 잡는 것이고, 해당 변수에 값을 할당한다는 것은 메모리에 값을 채우는 것이다. 

 

 

하지만 매번 이렇게 블로그 유저의 정보를 개별 변수에 담아서 처리하는 것은 유저가 늘어날 수록 불편할 수 밖에 없다. 그래서 이때 더 많은 데이터를 한꺼번에 취급할 수 있는 배열이나 객체, 즉 참조자료형을 사용해서 데이터를 담는다. 

 

const userBlog = {
    title = '어제보다 나은 내일',
    userID = 'kimcoding',
    createdAt = '2022-10-20',
}

 

userBlog라는 변수명으로 사물함에 이름표를 달아주는 것까지는 원시자료형이든 참조자료형이든 똑같다. 그러나 userBlog라는 이름표가 붙은 사물함을 열고 데이터를 담으려고 할 때 차이가 발생한다. 원시자료형은 데이터가 숫자이든 문자이든 참(true), 거짓(false)을 알려주는 boolean 값이든 단일 데이터인 반면, 배열이나 객체는 위의 userBlog 정보나 학생의 학번, 점수 정보처럼 하나의 데이터 값만 있는 게 아니기 때문이다.

 

이를 위해서 메모리는 참조자료형이 담기는 공간을 별도로 마련해 두는데, 이번에는 heap이라는 비선형 자료구조로 마련해서 데이터 사이즈가 얼마든지 늘었다 줄었다 할 수 있게 한다. 다른 말로 동적(dynamic)으로 변하는 데이터 보관함을 마련해 두는 것이다. 원시자료형처럼 사물함 칸에 값을 직접 저장하는 대신 실제 데이터가 담긴 저장소(heap 자료구조로 된 메모리)의 주소를 저장한다. 예를 들어 원시자료형에서는 title 이름표가 붙은 사물함을 열었을 때 '어제보다 나은 내일'이라는 문자열 값이 직접 들어있었다면, userBlog라는 객체 데이터가 담긴 사물함 칸을 열면 title, userID, 개설일자 정보 대신 해당 정보들이 담겨 있는 메모리 주소(예를 들어 0x1234라는 주소)를 넣는 것이다. 그리고 우리는 0x1234라는 주소를 참조(refer)하여 실제 데이터에 접근할 수 있다! 주소를 알면 그 주소지에 있는 건물을 찾아갈 수 있는 것과 마찬가지이다.

 

정리하자면 참조자료형(reference data type)은 데이터는 별도로 관리하고 우리가 직접 다루는 변수에는 그 데이터가 담긴 메모리의 주소만 저장해 둔다. 

 

이렇게 참조자료형을 담은 변수가 있는 사물함에는 실제 데이터 값이 아닌 데이터가 있는 주소만 있기 때문에 배열이나 객체를 다룰 때 가끔 황당한 일들을 목격하곤 한다. 이미 장광설이 차고 넘친고로 어떤 황당한 일들이 벌어지는지에 대해서는 다음 포스팅에서 다루도록 하겠다.