[JavaScript] 불변 객체 만들기

Donghwa Kim·2022년 11월 28일
0

JavaScript 동작원리

목록 보기
3/7

본 글은 정재남님의 저서 <코어 자바스크립트>를 읽고 정리한 내용입니다.

본 글에서 사용한 메모리 이미지들은 이해를 돕기 위해 메모리를 아주 간략화한 예 입니다.

들어가기 전에


이전 포스팅에서 참조형 데이터에 대해서 알아보았다.

참조형 데이터는 가변성을 띈다. 여기서 말하는 가변은 데이터 자체를 바꾸는 것이 아니라, 객체의 내부 프로퍼티를 변경할 때만 성립한다.

그렇다면 객체의 불변성을 확보해 주어야 할 때는 어떻게 해야 할까?

함수의 매개변수로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우가 종종 발생한다.

  • 예: 정보가 바뀌었으면 알림을 전송해야 하는 경우, 바뀌기 전의 정보와 바뀐 후의 정보를 모두 보여줘야 하는 경우

이번 포스팅에서는 위와 같은 경우에 어떻게 불변객체를 만들 수 있을지 알아보려고 한다.

불변 객체의 문제점


var user = {
  name: 'donghwa',
  gender: 'male'
};

var changeName = function (user, newName) {
  var newUser = user;
  newUser.name = newName;
  
  return newUser;
};

var user2 = changeName(user, 'john');

if (user !== user2) {
  console.log('유저 정보가 변경되었습니다.');

}

console.log(user.name, user2.name); // john, john
console.log(user === user2); // true

위 코드는 객체의 가변성으로 인한 문제점을 보여주는 간단한 예시이다.

위 코드에서는 changeName 이라는 함수를 통해서 username을 바꾸고 그 결과를 user2에 대입하고 있다.

그리고 유저 정보가 변경되면 로그에 출력을 하도록 코드를 작성하였지만 user와 user2는 같은 객체를 기리키고 있기 때문에 그냥 지나쳐 버린다.

예를 들어, 정보가 변경된 경우 바로 알림을 해 줘야 하는 경우에는 위와 같은 코드로는 제대로 된 기능을 할 수 없다.

서로 다른 객체를 가리키도록 해야 한다.

불변 객체 만들기


방법1

var user = {
  name: 'donghwa',
  gender: 'male'
};

var changeName = function (user, newName) {
  return {
    name: newName,
    gender: user.gender
  }
};

var user2 = changeName(user, 'john');

if (user !== user2) {
  console.log('유저 정보가 변경되었습니다.'); // 유저 정보가 변경되었습니다.

}

console.log(user.name, user2.name); // donghwa, john
console.log(user === user2); // false

방법1 에서는 changeName이 새로운 객체를 반환하도록 수정했다. user와 user2는 이제 다른 객체이므로 안전하게 변경 전과 변경 후를 비교할 수 있다.

하지만 문제점도 있다. 변경될 필요가 없는 정보를 하드코딩으로 입력을 한 점이다. 이렇게 되면 객체의 속성이 많아질 수록 입력해야 하는 수고가 늘어날 것이다.

방법2

var copyObject = function (target) {
  var result = {};

  for (var prop in target) {
    result[prop] = target[prop];
  }

  return result;
}

위 코드는 for...in 구문을 활용하여, target 객체의 프로퍼티를 복사하여 새로운 객체를 반환하는 방법이다. 하드코딩을 하지 않아도 된다는 점에서 방법1의 문제점을 해결했지만, 몇가지 아쉬운 점들이 있다.

  • 예: 모든 프로퍼티를 복사해야 하는점, 얕은 복사만 수행한다는 점

방법3

Spread Operator를 활용하는 방법이다. 방법 2와 비슷하지만, 일부 프로퍼티의 값을 수정하고 싶을 때 더 유용하게 쓰일 것 같다.

var myUser = {
  name: 'donghwa',
  gender: 'male'
};

var copyObject2 = function (target, newName) {
  return {
    ...target,
    name: newName,
  }
}

var newUser = copyObject2(myUser, 'john');

console.log(newUser); // { name: 'john', gender: 'male' }
console.log(myUser === newUser); // false

정리


이번 포스팅에서는 객체를 불변객체 처럼 쓸 수 있는 몇 가지 방법들과 한계점을 알아보았다.

위에 열거한 방법들 말고도 const 키워드와 라이브러리를 조합해 시스템 상의 제약을 거는 방법도 있지만 이번 포스팅에서는 다루지 않았다.

다음 포스팅에서는 오늘 열거한 방법들의 한계점을 보완할 수 있는 깊은 복사에 대해 알아보도록 하겠다.

References


profile
Slow but steady wins the race🏃‍♂️

0개의 댓글