3. const 소개


const: 변경 불가능한 변수 선언

1. 소개

변경 불가능한 변수라니, 앞뒤가 맞지 않는 말인 것 같습니다. 시작부터 혼란스럽네요. 역사적으로 프로그래밍 언어에서 쓰이는 단어를 실제 의미에 맞게 적용하는 대신 그때그때 필요에 따라 관습적으로 많이 사용되어 왔던 단어들을 적당히 차용해온 탓에, 후세에 학습을 해야 하는 우리가 고통스럽습니다. 이 고통의 원인은 사회에서 널리 통용되는 ‘변수’의 의미와 프로그래밍 언어상의 ‘변수’의 의미가 다르기 때문입니다. 변수란 값을 담을 수 있는 저장공간입니다. 이 개념을 곱씹어보면 이제는 전혀 이상하지 않게 느껴질 것입니다. const는 한 번 값을 담고 나면 다시는 다른 값으로 바꿔 담을 수 없는(변경 불가능한) 저장공간(변수)를 생성하는 방식입니다. 즉 ‘재할당’을 허용하지 않는 것입니다.

2. const는 상수가 아니다.

혹자는 const를 ‘상수’라고 부르기도 하지만, 필자의 생각에는 좀 길더라도 ‘재할당 불가능한 변수’라는 표현이 적절해 보입니다. 왜냐하면 ‘상수’란 처음부터 존재했고 끝까지 존재할, 처음부터 끝까지 늘 한결같은 값이라는 느낌인데, const로 선언한 변수는 그런 의미와는 다소 거리가 있기 때문입니다. const로 선언한 변수는 해당 선언 위치보다 이전에는 존재하지 않았고, 선언 후에도 유효범위(스코프)를 벗어난 뒤에는 존재하지 않게 됩니다. 값을 할당하여 초기화가 이뤄진 후부터 선언 당시의 스코프를 벗어나기 전까지가 const로 선언한 변수가 존재하는 영역입니다. 그러니까 ‘상수’라는 단어는 언제나 한결같이 유지될 것이 보장되는 Number.EPSILON, Math.PI, Number.MAX_SAFE_INTEGER 등에나 어울립니다.

그런가 하면 const로 선언한 변수가 실존하는 영역에서조차 ‘상수’ 개념과 다소 동떨어진 모습을 보이는 경우도 있습니다. const 변수에 객체를 할당한 경우에 그렇습니다. 코드로 확인해 봅시다.

1
2
3
4
5
6
7
8
const iu = {
name: '지은',
age: 26,
}
iu.age = 27
iu.job = '가수'
delete iu.name
console.log(iu) // { age: 27, job: '가수' }

const로 선언한 iu라는 변수에 객체를 할당했습니다. iu.age의 값을 27로 변경하고, iu.job에는 ‘가수’를 할당했습니다. iu.name을 삭제하고 iu를 출력하니 추가/변경/삭제한 내용들이 모두 제대로 반영되어 있습니다. const는 ‘변경 불가능한 변수’라고 했는데 어찌된 일인지 변경이 가능하네요. 이는 const를 ‘상수’로 여기면 이해하기 어려운 현상이지만 실은 아무런 문제가 없습니다. 자바스크립트에게 어떤 변수에 참조형 데이터를 할당하라고 명령하면, 자바스크립트는 그 변수의 저장공간에 참조형 데이터 자체를 저장하지 않습니다. 참조형 데이터는 별도의 메모리상에 저장하고, 저장한 데이터를 특정할 수 있는 ‘메모리 주솟값’을 할당합니다. 따라서 변수에는 그 객체가 저장된 ‘메모리 주솟값’이 담기고, 이후로는 다른 값으로 변경할 수 없게 되는 것입니다. 객체 내부의 프로퍼티들에 어떤 변경사항이 있더라도, 객체가 저장된 메모리 주솟값이 바뀌지 않는 한 const로 선언한 iu 변수에는 아무런 문제가 없습니다. 그리고 다행스럽게도(?) 객체가 저장된 메모리 주솟값은 const로 선언한 변수가 실행 컨텍스트의 종료에 따라 소멸하기 전까지는 영원히 변하지 않습니다.

참조형 데이터에 관한 얘기는 이정도로 마무리 짓겠습니다. 더 자세한 내용은 제 책 코어 자바스크립트를 참고하시기 바랍니다.

3. 심리적 안정감

const로 선언한 변수에 다른 값을 재할당할 수 없음은 둘째 치더라도, 명시적으로 ‘변경 불가능함’을 표기한다는 자체만으로도 생각보다 큰 심리적 안정감을 주게 됩니다. 동료가 작성한 코드를 살펴보던 중 어떤 변수가 let으로 선언되었다면 이후의 코드를 읽는 내내 해당 변수가 언제 변하게 될 지 모른다는 점을 계속 염두에 두며 읽어야만 합니다. 한참 아래에서 해당 변수를 발견했을 때, 그 변수가 그 위치에서 어떤 값을 가지고 있을지를 단번에 확신할 수 없습니다. 반면 const로 선언된 경우에는 이런 걱정을 할 필요가 없습니다. 예상치 못한 버그가 발생하더라도 혹여 const로 선언한 변수가 잘못 되었을지를 의심할 필요가 없습니다. 한참 아래에서 발견하더라도 처음의 그 값을 여전히 지니고 있을 거라고 자신할 수 있습니다.

눈썰미 좋은 독자라면 이미 눈치채셨을지도 모르지만, 사실 이전 let 포스트에서 기본 코드를 제외한 ‘더 나은 방안’들로 소개한 코드들은 순차적으로 최대한 let을 제거하는 방향성을 지니고 있습니다. 부득이하게 let을 제거하지 못한 경우는 debounce를 다룬 예제 뿐입니다. 바꿔 말하면 적절하게 let을 활용한 예제는 오직 debounce 예제 뿐이었다는 뜻이기도 합니다.

가급적 let을 사용하지 않는 방법을 고민하고, 가능한 모든 경우에서 const를 쓰고자 노력하시기 바랍니다. 이 노력이 이어지면 나중에는 let으로 선언한 변수에 대해서는 ‘무조건 언젠가는 값이 바뀔 것’이라고 인식하여 해당 변수를 중점적으로 살펴볼 수 있게 됩니다. 코드의 흐름을 파악하고 디버깅하는 데에 수고를 덜 들일 수 있게 되는 것입니다. 실제로 let을 쓰려는 생각이 들 때마다 const로도 가능할지를 고민하다 보면, 놀라우리만큼 많은 경우에 let을 쓰지 않아도 된다는 것을 깨닫게 될 것입니다. 앞서 소개한 참조형 데이터를 생각하면 더욱 그렇습니다. 실무에서는 어떤 변수를 선언하여 기본형 데이터를 직접 할당하는 경우에 비해, 참조형 데이터 하나를 만들어두고 그 내부의 프로퍼티만 조작하는 경우가 압도적으로 많습니다. 내부 프로퍼티 조작만으론 어려운 상황에 처하더라도 조금 더 생각해보면 내부 프로퍼티 조작만으로 가능한 다른 방법을 찾아낼 수 있는 경우가 있고, 설령 그럴 수 없다 해도 기존 변수는 그대로 둔 채 새로운 변수를 생성하여 새로운 객체를 할당하는 편이 나은 경우가 많습니다. 이에 대해서는 역시 코어 자바스크립트의 ‘불변성’ 파트를 참고하시기 바랍니다.

const는 변경 불가능성에서 비롯한 성질들을 제외하면 거의 모든 면에서 let과 똑같기 때문에, 자세한 성질들은 let과 const를 비교하면서 살펴보도록 하겠습니다. 그러기에 앞서, let과 const의 성질을 이해하기 위해 빼놓을 수 없는 중요한 개념인 ‘스코프’를 먼저 소개하겠습니다.