[번역] “const”의 속임수

강엽이·2023년 5월 30일
24
post-thumbnail

자바스크립트에서 할당과 뮤테이션의 차이에 대한 탐구

원문 : https://www.joshwcomeau.com/javascript/the-const-deception/

자바스크립트에서 const 키워드는 상수를 선언하는 데 사용됩니다. 상수는 종종 "변경할 수 없는 변수"로 생각됩니다.

const hi = 5;
hi = 10;
// 🛑 타입 에러: 상수 변수에 할당
console.log(hi);
// -> 5

그런데, 이상하게도 const를 사용하여 객체를 생성하면 자유롭게 변경할 수 있습니다.

const person = {
  name: 'Hassan',
};
person.name = 'Sujata';
// 동작할까요? 🤔
console.log(person);
// -> { name: 'Sujata' }

어떻게 person 변수를 변경할 수 있었을까요? 저는 const를 사용했는데요!

이 명백한 모순을 이해하기 위해서는 할당(assignment)뮤테이션(mutation)의 차이에 대해 알아야 합니다. 이것은 자바스크립트의 핵심 개념이며, 여러분이 이 차이를 명확하게 이해할 때 많은 것들을 이해할 수 있습니다.

시작해 봅시다!

대상 독자
이 블로그의 글은 초급/중급 자바스크립트 개발자를 대상으로 합니다. 적어도 몇 주 이상 자바스크립트로 작업했으며 기본 구문에 익숙하다고 가정하여 작성했습니다.

라벨로 사용되는 변수 이름

다음은 완벽하게 유효한 자바스크립트 프로그램입니다.

5;

다른 예제도 있습니다.

['apple', 'banana', 'cherry'];

이 두 가지 예에서 저는 숫자와 배열과 같은 몇 가지 항목을 만들고 있습니다. 코드가 실행되면 데이터가 생성되어 컴퓨터의 메모리에 저장됩니다.

하지만 이 프로그램들은 그다지 유용하지 않습니다. 데이터를 생성했지만 접근할 방법이 없습니다!

하지만 변수를 사용하면 나중에 참조할 수 있도록 작성한 항목에 라벨을 붙일 수 있습니다.

// 지금 만들어 봅시다...
const fruits = ['apple', 'banana', 'cherry'];
// ...그리고 나중에 참조합니다...
console.log(fruits);
// -> ['apple', 'banana', 'cherry']

처음 프로그래밍을 배울 때는 코드가 왼쪽에서 오른쪽으로 실행된다고 생각했습니다. 먼저 빈 상과와 같은 fruits 변수를 만들고 그 상자안에 배열을 넣습니다.

사실 이것은 올바른 멘탈 모델이 아닙니다. 배열이 먼저 생성된 다음 fruits 라벨을 가리킨다고 말하는 것이 더 정확합니다.

“변수”?
이 예제에서는 "변수"라는 용어를 사용하여 let 또는 const를 사용하든 상관없이 데이터에 지정된 이름을 나타낼 것입니다.

// 이것이 변수입니다.
let age = 26;
// 이것도 변수입니다.
const favouriteColor = 'hotpink';

이것이 혼란스럽다는 것을 알고 있습니다. "변수"라는 용어는 무언가 변할 수 있다는 것을 의미합니다! 안타깝게도 "변수"는 let 또는 const(이제는 사용되지 않는 var 키워드도)를 모두 포함하는 자바스크립트에서 확립된 포괄적인 용어입니다.

라벨 재할당

let 키워드를 사용하여 변수를 만들 때 라벨이 가리키는 "무언가"를 변경할 수 있습니다.

예를들어, fruits 라벨에 새로운 값을 지정할 수 있습니다.

원문 블로그에서는 오른쪽에 있는 값을 누르면 해당 라벨이 가르키는 데이터 값이 변경됩니다.
아래 링크로 접속하여 직접 동작 확인 가능합니다.
https://www.joshwcomeau.com/javascript/the-const-deception/#reassigning-our-labels-2

이를 재할당이라고 합니다. 우리는 사실 fruits 라벨이 완전히 다른 값을 가리켜야 한다고 말하고 있습니다.

// 라벨이 지정된 배열로 시작합니다.
  let fruits = ['apple', 'banana', 'cherry'];
// ...그리고 이 라벨을 새 값을 가리킵니다.
fruits = null;

데이터를 수정하는 것이 아니라 라벨을 수정하는 것입니다. 원래 배열에서 분리하여 새 배열에 연결합니다.

반대로 const로 생성된 변수는 재할당할 수 없습니다.

위의 예시와는 다르게 값을 눌러도 라벨이 가르키는 값이 변경되지 않습니다.

이것이 letconst의 근본적인 차이입니다. const를 사용할 때 변수 이름과 데이터 사이에 파괴될 수 없는 연결을 만듭니다.

하지만 여기서 중요한 것은 라벨이 손상되지 않은 상태로 유지되는 한 우리는 여전히 데이터 자체를 수정할 수 있습니다!

예를 들어 배열을 사용하면 해당 배열에서 문제 없이 항목을 추가/제거할 수 있습니다. fruits 변수는 여전히 동일한 배열에 연결되어 있습니다.


위의 이미지와 같이 배열에 값을 추가하고 삭제하는 것은 자유롭게 할 수 있습니다.
앞선 예시와 같이 원문 블로그에서 오른쪽 배열에 실제로 값을 추가하고 삭제할 수 있습니다.

위 이미지를 코드로 표현하면 다음과 같습니다.

// 'fruits' 라벨은 이 배열을 가리킵니다.
const fruits = ['apple'];
// 배열을 수정합니다.
fruits.push('banana', 'cherry', 'durian', 'eggplant', 'fig', 'grape', 'honeydew');

이를 뮤테이션이라고 합니다. 아이템을 추가/제거하여 배열 값을 편집하고 있습니다.

여기 배열 대신 객체를 사용하는 또 다른 예시가 있습니다. 라벨이 동일한 객체를 가리키면 객체 내의 키/값을 편집할 수 있습니다.


위의 이미지와 같이 객체 내부의 값은 수정할 수 있습니다.
앞선 예시와 같이 원문 블로그에서 타이틀 값을 직접 수정해볼 수 있습니다.

title 속성을 편집할 수 있습니다. 이는 다음 자바스크립트 코드에 해당합니다.

const event = {
  title: 'Change me!',
  startsAt: '2023-05-29T16:00:00Z',
  duration: 4,
  confirmed: true,
};

재할당(새로운 항목에 변수 이름을 지정)과 뮤테이션(항목 내의 데이터 편집) 사이에는 근본적인 차이가 있습니다.

const로 상수를 만들면 변수가 다시 할당되지 않을 것이라고 100% 확신할 수 있지만 뮤테이션에 대해서는 약속이 되어있지 않습니다. const는 뮤테이션을 전혀 차단하지 않습니다.

여기에 결점이 하나 더 있습니다. 문자열과 숫자와 같은 "기본" 데이터 유형은 불변입니다. 이것은 상황을 더욱 혼란스럽게 만듭니다. 다음 섹션에서 논의하겠습니다.

객체 및 배열 동결
그래서, 실망스럽게도 const 키워드는 뮤테이션으로부터 보호하지 못합니다. 데이터가 편집되지 않는다는 것을 보장할 수 있는 다른 방법이 있을까요?
Object.freeze() 라는 멋진 방법이 있습니다!

// 배열
const arr = Object.freeze([1, 2, 3]);
arr.push(4);
console.log(arr);
// -> [1, 2, 3]
// 객체
const person = Object.freeze({ name: 'Hassan' });
person.name = 'Sujata';
console.log(person);
// -> { name: 'Hassan' }

제가 아는 한, Object.freeze()는 완벽합니다. 이 방법을 사용하여 고정된 객체/배열을 수정할 수 없습니다.

또한 타입스크립트를 사용하는 경우 as const 단언을 사용하여 유사한 결과를 얻을 수 있습니다.

const arr = [1, 2, 3] as const;
arr.push(4);
// 🛑 에러: 'push' 속성은 존재하지 않습니다.
//           타입 'readonly [1, 2, 3]'.

모든 정적 타입과 마찬가지로 이러한 가드는 코드가 자바스크립트로 컴파일될 때 사라지므로 Object.freeze()와 동일한 보호 기능을 제공하지는 않습니다. 하지만 사용 사례에 따라 충분할 수도 있습니다.

Matt Pocock의 조언에 감사드립니다!

원시 데이터 타입

지금까지 우리가 본 모든 예시는 객체와 배열를 포함합니다. 하지만 문자열, 숫자 또는 불린 값과 같은 "원시" 데이터 타입이 있다면 어떨까요?

숫자를 예시로 들어보겠습니다.

let age = 36;
age = 37;

이걸 어떻게 해석할까요? age 라벨을 새로운 값으로 재할당 하는 것일까요? 아니면 36을 37로 편집하는 것일까요?

제가 설명해드리겠습니다.: 자바스크립트의 모든 원시 데이터 타입은 수정 불가능입니다. 숫자의 값을 "편집"하는 것은 불가능합니다. 변수를 다른 값으로만 재할당할 수 있습니다.

제가 생각하기에 좋은 방법이 있습니다. 모든 가능한 숫자들의 큰 목록이 있다고 가정해봅시다. 숫자 36을 age 변수에 할당했지만 목록의 다른 숫자를 가리킬 수 있습니다.

원문 블로그에서는 오른쪽에 있는 값을 누르면 해당 라벨이 가르키는 데이터 값이 변경됩니다.
아래 링크로 접속하여 직접 동작 확인 가능합니다.
https://www.joshwcomeau.com/javascript/the-const-deception/#primitive-data-types-3

분명한 것은, 브라우저가 문자 그대로 모든 가능한 숫자의 큰 인덱스를 가지고 있지 않다는 것입니다. 제가 여기서 설명하고 싶은 요점은 숫자 자체는 변경될 수 없다는 것입니다. 우리는 라벨이 가리키는 번호만 변경할 수 있습니다.

이는 string, boolean, null 등을 포함한 모든 원시 값 타입에 해당됩니다.

사고 실험
언급했듯이, 원시 값은 "불변"이므로 편집할 수 없습니다.

하지만 그들이 변경될 수 있다면요? 만약 숫자 자체를 변형할 수 있다면 구문은 어떻게 될까요?

다음과 같이 표시됩니다.

// 숫자 36의 값을 수정합니다.
36 = 37;
// 숫자 36은 더이상 존재하지 않습니다!
console.log(36); // 37

결국 "뮤테이션"의 전체적인 아이디어는 그 가치를 근본적으로 변화시킨다는 것입니다. 객체를 변형할 때 객체의 "본질"을 변경하고, 이러한 변경 사항을 참조할 때 확인할 수 있습니다.

const me = { age: 36 };
me.age = 37;
console.log(me);
// -> { age: 37 }

그래서 만약 우리가 자바스크립트에서 원시 값을 변형시킬 수 있다면, 그것은 본질적으로 특정 숫자를 덮어써서 다시는 참조될 수 없도록 하는 것을 의미합니다.

이것은 분명히 혼란스럽고 도움이 되지 않을 것이며, 이것이 자바스크립트에서 원시값(primitives)이 변경 불가능(immutable)한 이유입니다.

추가 자료

자바스크립트는 까다로운 언어이고, 익숙해지려면 오랜 시간이 걸립니다.

다음은 유용할 수 있는 몇 가지 자료입니다.

  • Just JavaScript, Dan Abramov와 Maggie Appleton의 글은 이러한 아이디어들을 깊이 파고드는 멋진 예시가 들어간 가이드입니다.
  • 제가 작성한 블로 글 중 “Statements Vs. Expressions”는 자바스크립트의 또 다른 일반적으로 잘못 이해되는 측면을 파고듭니다.
  • 만약 제가 가르치는 스타일이 마음에 든다면, 제 글중에 여러분들이 유용하다고 생각할수도 있는 몇 가지 과목이 있습니다.
profile
FE Engineer

1개의 댓글

comment-user-thumbnail
2023년 6월 8일

As i know by using "const," you indicate your intention that a variable should not be modified, making the code more readable and self-explanatory. It helps catch accidental modifications to variables that are not intended to be changed. The compiler or interpreter will throw an error if you attempt to modify a constant. MyCCPay

답글 달기