데이터 불변성(Immutability)

심채운·2022년 7월 20일
0

JavaScript

목록 보기
8/14

데이터 불변성

원시데이터 : String, Number, Boolean, undefined, null
참조형데이터 : Object, Array, Function(콜백함수)

원시형

새로운 원시 데이터를 사용했을때 만약 그 원시데이터가 기존의 메모리 주소에 들어있다면 그 원시데이터를 새로운 메모리에 새롭게 만드는게 아니고 기존의 존재하는 메모리 주소만 바라보도록 만드는 것이다. 즉 원시 데이터들은 새롭게 만들어지는 것이 아니고 항상 불변한다는 개념이 된다. (한 번 메모리에 만들어졌다는 전제하에)

참조형

참조형은 원시형데이터와 다르게 새로운 값을 만들때마다 그것이 새로운 메모리주소에 할당되는 구조를 가진다. 참조형 데이터는 결국 불변성이 없다 즉 가변성이다.
주의 : 같은 메모리를 바라보고 있는 여러개 변수들이 있을때 한쪽에 변수에 있는 값을 수정하게 되면 다른 값에서 확인할대 의도하지 않게 이미 값이 바뀌어져 있는 상태가 될 수 있다.

let a = {k:1}
let b = {k:1}
console.log(a,b, a===b) //{ k: 1 } { k: 1 } false

a.k =7
b=a
console.log(a,b, a===b) //{ k: 7 } { k: 7 } true

a.k =2
console.log(a,b, a===b) //{ k: 2 } { k: 2 } true

let c = b
console.log(a,b,c, a===c) //{ k: 2 } { k: 2 } { k: 2 } true

a.k =9
console.log(a,b,c, a===c) //{ k: 9 } { k: 9 } { k: 9 } true

복사

참조형 데이터를 할당 연산자로 다른 변수에 할당할때 그야말로 메모리 주소만 옮겨가게 하는 의도라면 상관이 없지만 그렇지 않고 a라는 변수와 b라는 변수를 별개로 구분해서 관리하고 싶다면 "복사"라는 개념을 사용해야한다. 복사는 대표적으로 "얕은복사"와 "깊은복사"로 나뉜다.

얕은복사(Shallow copy), 깊은복사(Deep copy)

const user = {
  name : 'Sim',
  age : 24,
  emails : ['doogadooga@gmail.com']
}
const copyUser = user
console.log(copyUser === user) //true

user.age = 22
console.log('user', user) //user { name: 'Sim', age: 22, emails: [ 'doogadooga@gmail.com' ] }
console.log('copyUser', copyUser) //copyUser { name: 'Sim', age: 22, emails: [ 'doogadooga@gmail.com' ] }

user라는 객체데이터의 내용을 수정했는데 copyUser도 수정이 되어져버리는 문제가 발생. 이유는 user라는 변수와 copyUser라는 변수가 같은 메모리주소를 바라보고 있기 때문이다. 그래서 한쪽에서 수정했을때 다른쪽에 영향을 주는것이다. 그래서

const user = {
  name : 'Sim',
  age : 24,
  emails : ['doogadooga@gmail.com']
}
const copyUser = Object.assign({}, user)
console.log(copyUser === user) //false

user.age = 22
console.log('user', user) //user { name: 'Sim', age: 22, emails: [ 'doogadooga@gmail.com' ] }
console.log('copyUser', copyUser) //copyUser { name: 'Sim', age: 24, emails: [ 'doogadooga@gmail.com' ] }

이렇게 Object.assign()메소드를 활용해 영향을 미치지 않게 복사를 하면 된다. assign 뿐만 아니라 전개연산자를 사용해도 된다.

const user = {
  name : 'Sim',
  age : 24,
  emails : ['doogadooga@gmail.com']
}
const copyUser = {...user}
console.log(copyUser === user) //false

user.age = 22
console.log('user', user) //user { name: 'Sim', age: 22, emails: [ 'doogadooga@gmail.com' ] }
console.log('copyUser', copyUser) //copyUser { name: 'Sim', age: 24, emails: [ 'doogadooga@gmail.com' ] }

위에 방법들은 정확하게 얕은복사이다.

user.emails.push('scw@naver.com')
console.log(user.emails === copyUser.emails) //true

왜 true 일까? user안에 새로운 참조형인 emails를 복사처리를 안했기 때문에 그래서 같은 메모리 주소만 공유하고 있는것이다.

console.log('user', user)//user {
                          //   name: 'Sim',
                          //   age: 22,
                          //   emails: [ 'doogadooga@gmail.com', 'scw@naver.com' ]
                          // }
console.log('copyUser', copyUser) //copyUser {
                                  //   name: 'Sim',
                                  //   age: 24,
                                  //   emails: [ 'doogadooga@gmail.com', 'scw@naver.com' ]
                                  // }

이때 필요한것이 바로 얕은 복사가 아니라 깊은 복사이다.
얕은복사는 겉에 표면만 복사하는 것이기 때문에 속의 내용은 전혀 복사가 되지 않았다.깊은 복사는 내부에 있는 모든 참조형관계까지 새로운 메모리로 복사를 해서 만드는 것이다.

깊은 복사를 하기 위해선 npm i rodash를 이용해 rodash패키지를 설치한 후에 node_modules패키지에서 갖고올 수 있게 import를 써준다.

import _ from 'lodash'

copyUser뒤에 _ 기호에 lodash기능을붙여주고 뒤에다 .cloneDeep(user) 메소드를 작성
그래서 결과는

import _ from 'lodash'

const user = {
  name : 'Sim',
  age : 24,
  emails : ['doogadooga@gmail.com']
}
const copyUser = _.cloneDeep(user)
console.log(copyUser === user) //false

user.age = 22
console.log('user', user) //user { name: 'Sim', age: 22, emails: [ 'doogadooga@gmail.com' ] }
console.log('copyUser', copyUser) //copyUser { name: 'Sim', age: 24, emails: [ 'doogadooga@gmail.com' ] }

user.emails.push('scw@naver.com')
console.log(user.emails === copyUser.emails) //false
console.log('user', user)//user {
                          //   name: 'Sim',
                          //   age: 22,
                          //   emails: [ 'doogadooga@gmail.com', 'scw@naver.com' ]
                          // }
console.log('copyUser', copyUser) //copyUser { name: 'Sim', age: 24, emails: [ 'doogadooga@gmail.com' ] }

참고로 npm을 사용해 패키지모듈에서 import하고 node.js에서 작업할때는 ES6 모듈가져오기를 사용할 수 있도록 package.json 파일에서 'type'속성을 설정해야한다.

{
  //Cannot use import statement outside a module (Error text)
  "name": "scss-test",
  "version": "1.0.0",
  "description": "",
  "type" : "module",
  "main": "index.js",
  "scripts": {
    "dev": "parcel index.html",
    "build": "parcel build index.html"
  },
  "keywords": [],
  
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "parcel-bundler": "^1.12.5",
    "sass": "^1.53.0"
  },
  "dependencies": {
    "lodash": "^4.17.21"
  }
}
profile
불가능, 그것은 사실이 아니라 하나의 의견일 뿐이다. - 무하마드 알리

0개의 댓글