const에 대한 오해

const is not immutable?

얼마 전에 어떤 페이스북 그룹에서 공유된 글 중에 이런 글이 있었다. 본문에는 이런 문장이 있다.

ES6 const does not indicate that a value is ‘constant’ or immutable. A const value can definitely change.

여기서는 const에 할당된 값이 바뀔 수 있다고 한다. 믿겨지는가? 값이 바뀐다면 어떻게 상수(Constant)1라고 할 수 있을까?

오해하지말자. const 변수에 할당된 값은 절대로 바뀌지 않는다. 여기서는 재할당만 막는 걸 보장한다고 되어있지만, 재할당을 막는 게 곧 값을 못 바꾼다는 얘기다. 이건 그냥 말장난, 혹은 동어 반복이다.

그러면서 드는 예가 Object다. 다들 아시겠지만 Object가 변수에 할당될 때는 Object 자체가 할당되는 게 아니라 Object의 주소가 할당된다. 이는 JavaScript 개발자라면 당연히 기본적으로 알아야할 사항이다. (그러나 안타깝게도 나는 올해 2월까지는 JavaScript 개발자가 아니었던 것 같다.) {} === {}가 거짓을 반환하는 이유에 대해 다들 알고 있지 않은가? const 변수에 Object를 할당하면 당연히 Object를 가리키는 주소 이 할당된다. 그리고 이 주소 값은 불변이고, 상수다. 그러나 그 Object에 Key-Value 쌍을 추가하거나 변경하는 행위는 가능하다. 예외인게 아니고 당연하다. 이 값은 const에 바인딩 되는 값이 아니니까.

MDN에서도 비슷한 방식으로 설명하고 있다. 이런 식으로 밖에 설명할 수 밖에 없는 이유가 대체 뭔지 정말 안타깝다. 그냥 주소 값을 때려박는다고 왜 말을 못해! Object 내의 값을 뭘로 바꾸든 간에 그건 변수에 할당된 값을 바꾸는 것이 아니다. 그래.. C 배울 때도 포인터가 제일 어렵긴 했었지..

요즘 const 사용법

그 외 글의 논조 자체에는 동의한다. 원문에서는 변수 사용시에 다음과 같이 하라고 되어있다.

  1. 기본적으로는 const를 사용한다.
  2. 재할당이 필요한 경우에만 let을 사용한다.
  3. var는 ES2015에서는 쓰지 않는다.

이상하거나 어색하게 느끼실 분도 계실 것이다. const는 상수에만 쓰는 거 아닌가 하고 말이다. 맞다. const는 상수에만 쓰는 것이다. 그러므로 이 말은 이렇게 표현할 수 있다. “기본적으로 상수를 사용하라”

왜 이렇게 해야할까? 원문에서는 const가 항상 같은 객체를 가리키므로 코드가 읽기 편해진다고 한다. 물론 맞는 말이다. 하지만 나는 보다 근본적으로 접근해야 한다고 본다. 요즘 프로그래밍 패러다임에서는 변수가 나쁘다고 본다. 이 질문의 첫번째 답변에서는 예상치 못한 변수의 변경으로 인해 많은 버그가 발생한다고 한다. 굳이 함수형 프로그래밍이 아니더라도, 코드의 흐름을 따라가기 위해 변하는 부분을 최소화 하는 게 요즘 프로그래밍 패러다임이다. const를 우선시해서 쓴 코드를 읽게 된다면 let은 자연히 재할당하게 되는 변수겠거니, 하고 알기 쉬운 건 덤이다. 그 유명한 Airbnb의 스타일 가이드에서도 const를 우선하는 걸 권장하고 있으며, 이는 ESLint의 룰로도 존재한다.

실제로 const를 우선시하는 코딩을 시작해보면 놀랄만큼 let을 쓸일이 없다는 것을 경험하게 된다. 아니, 사실 let을 쓰던 때에도 const를 써서 해결할 방법이 많았다는 것을 알 수 있게 된다. 예를 들면, 조건에 따라 동적으로 변수에 값을 할당할 때 let을 쓴다면 아래와 같이 할 수 있다.

1
2
3
4
5
6
7
8
let foo, bar;
if (a === b) {
foo = a;
bar = b;
} else {
foo = b;
bar = a;
}

이 경우는 언뜻 보기에 const를 써서 할당할 방법이 없는 것처럼 보이지만 디스트럭처링과 삼항연산자를 섞어주면 쉽게 해결이 가능하다.

1
const [foo, bar] = (a === b) ? [a, b] : [b, a];

더 Geek 해보이기 위해 IIFE를 사용해보자. (-_-;)

1
2
3
4
5
6
7
const [foo, bar] = (function() {
if (a === b) {
return [a, b];
} else {
return [b, a];
}
})();

더구나 위에서 말했던 것처럼 주소값이 불변인 것이지 Object 내의 값이나 상태는 얼마든지 변할 수 있으므로 실제 레거시 코드를 const 우선의 코드로 수정하는 것도 어렵지 않다. 다만 객체 내부의 상태는 여전히 변경 가능하므로 모든 변수를 통제하고 있지는 못하다는 점을 상기해야겠다.

당연히 Object 내부 상태까지 불변값으로 고정시키는 것이 가장 바람직한 일이겠지만, 현재로서는 완벽한 방법이 없다. 원문에서는 객체의 키값까지 통제하기 위한 수단으로 Object.freeze를 언급하고 있지만, 이건 단기적인 해결책밖에는 안된다. 실질적으로는 페이스북이 만든 라이브러리인 Immutable를 사용하는 게 가장 나은 수단으로 보인다. TypeScript에서는 readonly 프로퍼티를 이용하면 불변 객체를 만들 수 있다. (이 경우엔 런타임 불변은 아니다.) 다만, 실질적으로 객체지향 프로그래밍 패러다임에서 완벽하게 불변 객체만을 사용해서 코딩하는 것은 정말 어렵기 때문에 이런 방법도 있다 정도로만 보는 게 좋을 것 같다.


  1. 1.const는 Constant의 약자 맞다.