Skip to content

Files

Latest commit

03a0f48 Β· Nov 5, 2023

History

History
185 lines (131 loc) Β· 5.42 KB

IMMUTABLE_OBJECT.md

File metadata and controls

185 lines (131 loc) Β· 5.42 KB

λΆˆλ³€ κ°μ²΄λž€?

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ λΆˆλ³€ 객체(Immutable Object)λŠ” ν•œ 번 μƒμ„±λœ ν›„μ—λŠ” κ·Έ μƒνƒœλ₯Ό λ³€κ²½ν•  수 μ—†λŠ” 객체λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. 즉, 객체 λ‚΄λΆ€μ˜ κ°’μ΄λ‚˜ μƒνƒœκ°€ λ³€κ²½λ˜μ§€ μ•ŠμŒμ„ 보μž₯ν•˜λŠ” κ°μ²΄μž…λ‹ˆλ‹€. λΆˆλ³€μ„±μ€ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μ€‘μš”ν•œ κ°œλ…μœΌλ‘œ, 특히 ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ κ°•μ‘°λ©λ‹ˆλ‹€.

λΆˆλ³€ 객체의 μ€‘μš”μ„±

  • 예츑 κ°€λŠ₯μ„±(Predictability): 객체가 λ³€κ²½λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, μ½”λ“œμ˜ λ‹€λ₯Έ λΆ€λΆ„μ—μ„œ 예기치 μ•Šμ€ 변경이 일어날 κ±±μ • 없이 객체λ₯Ό μžμ‹  있게 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ³€κ²½ 좔적(Change Detection): λΆˆλ³€ κ°μ²΄λŠ” μƒˆλ‘œμš΄ μƒνƒœλ₯Ό λ§Œλ“€ λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•˜λ―€λ‘œ, μ°Έμ‘° 비ꡐλ₯Ό 톡해 μ‰½κ²Œ λ³€κ²½ μ—¬λΆ€λ₯Ό 감지할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½(Thread Safety): λ™μ‹œμ„± ν”„λ‘œκ·Έλž˜λ°μ—μ„œ λΆˆλ³€ κ°μ²΄λŠ” μƒνƒœκ°€ λ³€κ²½λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 데이터 경쟁(race condition)μ΄λ‚˜ λ™μ‹œμ„± 였λ₯˜λ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€.
  • ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°(Functional Programming): ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ 핡심 원리 쀑 ν•˜λ‚˜λ‘œ, λΆ€μž‘μš©(side-effects) 없이 ν”„λ‘œκ·Έλž˜λ°ν•  수 있게 ν•©λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ λΆˆλ³€ 객체 생성

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” 기본적으둜 λΆˆλ³€ 객체λ₯Ό λ‚΄μž₯ν•˜κ³  μžˆμ§€ μ•Šμ§€λ§Œ, λΆˆλ³€μ„±μ„ κ°•μ œν•˜λŠ” 라이브러리(예: Immutable.js)λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜, Object.freeze(), Object.seal() 같은 λ‚΄μž₯ λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•΄μ„œ 객체의 λΆˆλ³€μ„±μ„ λΆ€λΆ„μ μœΌλ‘œ μœ μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Copy code
const obj = Object.freeze({ name: 'John' });

obj.name = 'Doe'; // 이 변경은 λ¬΄μ‹œλ©λ‹ˆλ‹€, objλŠ” λΆˆλ³€ κ°μ²΄μž…λ‹ˆλ‹€.
console.log(obj.name); // 'John'

κ·ΈλŸ¬λ‚˜ Object.freeze()λŠ” 얕은(shallow) λ™κ²°λ§Œ μˆ˜ν–‰ν•˜κΈ° λ•Œλ¬Έμ—, 객체 λ‚΄λΆ€μ˜ λ‹€λ₯Έ κ°μ²΄λŠ” μ—¬μ „νžˆ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ™„λ²½ν•œ λΆˆλ³€μ„±μ„ μœ„ν•΄μ„œλŠ” λͺ¨λ“  λ‚΄λΆ€ 객체에 λŒ€ν•΄μ„œλ„ μž¬κ·€μ μœΌλ‘œ 동결을 μ μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

λΆˆλ³€ 객체의 ν•œκ³„

  • μ„±λŠ₯: λΆˆλ³€ κ°μ²΄λŠ” μƒνƒœκ°€ λ°”λ€” λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ 객체λ₯Ό 생성해야 ν•˜λ―€λ‘œ, λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ 증가할 수 있으며, 가비지 μ»¬λ ‰μ…˜μ— λŒ€ν•œ 뢀담이 컀질 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 심측 λΆˆλ³€μ„±(Deep Immutability): μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ 심측 λΆˆλ³€ 객체λ₯Ό λ§Œλ“œλŠ” 것은 기본적인 λ°©λ²•μœΌλ‘œλŠ” κΉŒλ‹€λ‘­μŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ μ™„μ „ν•œ λΆˆλ³€μ„±μ„ κ΅¬ν˜„ν•˜λ €λ©΄ 좔가적인 λΌμ΄λΈŒλŸ¬λ¦¬λ‚˜ λ³΅μž‘ν•œ 둜직이 ν•„μš”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

얕은 볡사와 κΉŠμ€ 볡사

얕은 볡사(Shallow Copy)와 κΉŠμ€ 볡사(Deep Copy)λŠ” 객체λ₯Ό 볡사할 λ•Œ μ‚¬μš©λ˜λŠ” 두 가지 μ£Όμš” λ°©λ²•μž…λ‹ˆλ‹€. μ΄λ“€μ˜ μ°¨μ΄λŠ” λ³΅μ‚¬μ˜ κΉŠμ΄μ™€ 관련이 μžˆμŠ΅λ‹ˆλ‹€.

  • 얕은 볡사 : λ°”λ‘œ μ•„λž˜ λ‹¨κ³„μ˜ κ°’λ§Œ 볡사함.
  • κΉŠμ€ 볡사 : λ‚΄λΆ€μ˜ λͺ¨λ“  값듀을 ν•˜λ‚˜ν•˜λ‚˜ μ°Ύμ•„μ„œ μ „λΆ€ 볡사함.

Object.assign()

볡사 ν›„ 참쑰값이 λ‹€λ₯΄κΈ°μ— κΉŠμ€ 볡사가 이루어진닀고 λ³Ό 수 μžˆλ‹€. ν•˜μ§€λ§Œ 2차원 객체의 경우 κΉŠμ€ 볡사가 이루어지지 μ•ŠλŠ”λ‹€.

// 1차원 객체
const obj = { a: 1 };
const newObj = Object.assign({}, obj);

newObj.a = 2;

console.log(obj); // { a: 1 }
console.log(obj === newObj); // false

// 2차원 객체
const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const newObj = Object.assign({}, obj);

newObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }

Spread Operator

λ§ˆμ°¬κ°€μ§€λ‘œ 2차원 κ°μ²΄μ—μ„œ κΉŠμ€ 볡사가 이루어지지 μ•ŠλŠ”λ‹€.

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const newObj = { ...obj };

newObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }
console.log(obj.b.c === newObj.b.c); // true

JSON 객체 λ©”μ„œλ“œ

2차원 κ°μ²΄μ—μ„œ κΉŠμ€ 볡사가 이루어진닀. ν•˜μ§€λ§Œ ν•¨μˆ˜λ₯Ό λ§Œλ‚ κ²½μš° undefined둜 μ²˜λ¦¬ν•œλ‹€.

const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

const newObj = JSON.parse(JSON.stringify(obj));

newObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 2 } }
console.log(obj.b.c === newObj.b.c); // false

const obj = {
  a: 1,
  b: {
    c: 2,
  },
  func: function () {
    return this.a;
  },
};

const newObj = JSON.parse(JSON.stringify(obj));

console.log(newObj.func); // undefined

μž¬κ·€ ν•¨μˆ˜

객체의 κΉŠμ€ 볡사λ₯Ό λ‹€λ₯Έ 문제 없이 ν•˜λ €λ©΄, μž¬κ·€ ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜λŠ” 것이 μ’‹λ‹€.

function deepCopy(obj) {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  let copy = {};
  for (let key in obj) {
    copy[key] = deepCopy(obj[key]);
  }
  return copy;
}

const obj = {
  a: 1,
  b: {
    c: 2,
  },
  func: function () {
    return this.a;
  },
};

const newObj = deepCopy(obj);

newObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === newObj.b.c); // false

lodash λͺ¨λ“ˆμ˜ cloneDeep()

lodash λͺ¨λ“ˆμ˜ cloneDeep() λ©”μ„œλ“œλ₯Ό ν™œμš©ν•΄μ„œ, 객체의 κΉŠμ€ 볡사λ₯Ό ν•  수 μžˆλ‹€.

const lodash = require("lodash");

const obj = {
  a: 1,
  b: {
    c: 2,
  },
  func: function () {
    return this.a;
  },
};

const newObj = lodash.cloneDeep(obj);

newObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === newObj.b.c); // false

Reference


Back