JavaScript에서의 변경 가능성과 불변성

JavaScript에서의 변경 가능성과 불변성

이 글은 JavaScript에서 변경 가능성과 불변성 개념을 설명합니다.

YouTube Video

JavaScript에서의 변경 가능성과 불변성

변경 가능하다는 것은 무엇인가요?

**변경 가능(mutable)**은 값이 수정될 수 있음을 의미합니다. **참조 타입(reference types)**인 객체(Object)와 배열(Array)은 변경 가능한 데이터 구조의 대표적인 예입니다.

변경 가능한 객체의 예시

1let person = { name: "Alice", age: 25 };
2person.age = 26;
3console.log(person); // { name: "Alice", age: 26 }

이 코드에서 person 객체의 age 속성은 25에서 26으로 변경됩니다. 객체는 참조로 전달되기 때문에(reference passed by), person 변수에 저장된 메모리 주소의 내용이 수정됩니다.

변경 가능한 배열의 예시

1let numbers = [1, 2, 3];
2numbers.push(4);
3console.log(numbers); // [1, 2, 3, 4]

이 코드에서는 push 메서드를 사용하여 원래 배열에 새 요소 4를 추가합니다. 이는 원본 배열을 수정하므로, 변경 가능한 작업에 해당됩니다.

함수 내부의 예시

 1// Function to append a value to an array
 2function append(arr, value) {
 3    arr.push(value); // Modify the original array
 4    console.log(arr);
 5}
 6
 7let numbers = [1, 2, 3];
 8append(numbers, 4);
 9
10console.log(numbers); // [1, 2, 3, 4] (original array is modified)

함수 내부에서 변경 가능한 작업을 수행하면 원본 배열도 수정됩니다.

불변하다는 것은 무엇인가요?

**불변(immutable)**은 값이 수정될 수 없음을 의미합니다. **원시 타입(primitive types)**은 본질적으로 불변합니다.

불변하는 원시 타입의 예시

1let str = "hello";
2str[0] = "H";
3console.log(str); // "hello"

str 문자열의 첫 번째 문자를 H로 변경하려는 시도는 문자열이 불변하기 때문에 실패합니다.

함수 내부의 예시

 1// Function to increment a number
 2function increment(num) {
 3    num++; // This modifies only the local copy of num
 4    console.log(num);
 5}
 6
 7let number = 10;
 8increment(number);
 9
10console.log(number); // 10 (original number remains unchanged)

숫자는 불변하기 때문에, 함수 내부의 작업이 원본 변수에 영향을 미치지 않습니다.

배열에서의 불변 작업

배열은 변경 가능하지만, 원본 배열을 수정하지 않고 새 배열을 생성하는 방식으로 불변 작업을 수행할 수 있습니다.

1// Create an array of numbers
2let numbers = [1, 2, 3];
3
4// Create a new array by spreading the original and adding a new element
5let newNumbers = [...numbers, 4];
6
7console.log(numbers); // [1, 2, 3] (original array is unchanged)
8console.log(newNumbers); // [1, 2, 3, 4] (new array with an added element)

여기에서 전개 구문(...)은 새로운 배열 newNumbers를 생성하는 데 사용됩니다. 원래의 numbers 배열이 수정되지 않기 때문에, 이는 불변 작업입니다.

불변 데이터 구조를 사용하는 것의 장점

예측 가능성 향상

불변 데이터는 변경될 수 없으므로, 예상치 못한 수정이 발생할 가능성이 줄어들어 버그의 위험을 감소시킵니다.

불변성을 기반으로 한 라이브러리와의 호환성

ReactRedux와 같은 라이브러리는 종종 불변 데이터를 염두에 두고 설계되므로, 올바르게 사용할 경우 상태 관리를 더 쉽게 만듭니다.

Object.freeze를 사용하여 객체를 불변으로 만들기

Object.freeze를 사용하면 객체에 대한 수정을 방지할 수 있습니다.

1// Create a frozen object (properties cannot be modified)
2const person = Object.freeze({ name: "Alice", age: 25 });
3
4// Attempt to modify a property (ignored in non-strict mode, error in strict mode)
5person.age = 26;
6
7console.log(person); // { name: "Alice", age: 25 }

하지만, Object.freeze얕은 동결만 수행하므로 중첩된 객체의 프로퍼티는 여전히 수정 가능합니다.

1// Create a frozen object with a nested object
2const user = Object.freeze({ profile: { name: "Bob" } });
3
4// Attempt to modify a nested property (this works because Object.freeze() is shallow)
5user.profile.name = "Charlie";
6
7console.log(user.profile.name); // "Charlie" (nested object is still mutable)

완전히 불변인 객체를 만들려면 깊은 동결이 필요합니다.

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze(obj) {
 3  Object.keys(obj).forEach(key => {
 4    if (typeof obj[key] === "object" && obj[key] !== null) {
 5      deepFreeze(obj[key]); // Recursively freeze nested objects
 6    }
 7  });
 8  return Object.freeze(obj); // Freeze the top-level object
 9}
10
11// Create a deeply frozen object
12const user = deepFreeze({ profile: { name: "Bob" } });
13
14// Attempt to modify a nested property (ignored)
15user.profile.name = "Charlie";
16
17console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

요약

  • 변경 가능한(Mutable) 데이터는 객체와 배열을 포함하여 수정이 가능합니다.
  • 불변(Immutable) 데이터는 문자열과 숫자 같은 원시 타입을 포함하여 수정할 수 없습니다.
  • 전개 구문 또는 map을 사용하면 불변 데이터 작업을 수행할 수 있습니다..
  • Object.freezedeepFreeze를 사용하여 객체의 수정을 방지할 수 있습니다.
  • 불변 데이터를 사용하면 더 예측 가능하고 오류가 적은 코드를 작성할 수 있습니다.

불변 디자인은 코드의 안전성과 가독성을 향상시키므로, 이를 잘 활용하세요!

위의 기사를 보면서 Visual Studio Code를 사용해 우리 유튜브 채널에서 함께 따라할 수 있습니다. 유튜브 채널도 확인해 주세요.

YouTube Video