원본 배열에 대하여

백승하·2023년 5월 18일
9
post-thumbnail

들어가기전에

자바스크립트에서 원본 배열이 바뀌는게 어떤 의미이고 왜 리액트 등에서는 원본 배열이 바뀌지 않도록 할까요?
이것을 알려면 자바스크립트의 데이터 타입에 따른 메모리에서의 저장 방식의 차이에 대해서 알아야합니다

원시타입 : 숫자, 문자, boolean, null, undefined 등

참조타입 : 배열, 객체, 함수 등 원시타입을 제외한 모든것

자바스크립트에서 원시타입들은 불변입니다. 한 번 변수에 할당된 값은 변경이 불가능하죠

네?? 불가능하다고요? 지금까지 잘 사용한거 같은데?

이것을 이해하려면 변수가 메모리에 어떻게 할당되는지 알아야합니다

원시타입의 메모리 할당

a = 10;  	// a는 10
b = a;  	// b도 10이 된다. 복사가 됨
                Stack
   		_____________________
      |    변수     |    값    |
  10A |     a      |    10   |
  10B |     b      |    10   |
      |______________________|

그럼 B값을 20으로 변경한다면?
                Stack
      _________________________
      |    변수     |    값    |
  10A |     a      |    10    |
  10B |     b      |    10    | 더 이상 사용 안됨
  10C |     b      |    20    |
      |_______________________|
      

새로운 주소에 값이 할당된다. 기존 10B 주소에 할당된 값이 절대 바뀌지 않는다는 말이다
이렇게 한 번 주소에 할당된 값은 바뀌지 않는다(불변이다)

그럼 10B 주소에 할당된 값은 어떻게 될까? 메모리에 계속 남아 있을까?
자바처럼 가바지컬렉터가 더 이상 사용되지 않는 변수처럼 처리한다

결론 : 숫자, 문자, boolean 등 원시타입은 완전히 새로운 주소에 값을 할당한다.

참조타입의 메모리 할당 방식

  a = [1, 2, 3];    // a는 배열로 생성.
  b = a;            // 배열 복사. a와 b가 서로 같은 주소를 가리키고 있다.

 Stack                               Heap
      _________________________            _________________________
      |    변수     |    값    |           |    변수     |    값     |
  20A |     a      |    30A   |       30A |     a      |  [1,2,3]  |
  20B |     b      |    30A   |           |            |           |
      |_______________________|           |________________________|
  

참조타입은 실제 값은 Heap 영역에 가지고 있다.
stack에 값은 Heap 영역의 주소값을 가지고 있는게 되는거죠.
따라서 참조타입의 값을 변경한다는 의미는 stack의 값이아닌 Heap 영역의 값을 변경한다는 의미입니다.

그러면 위의 예시에서 b를 변경하면 어떻게 될까요?

b.push(4); // b 배열에 4란 값을 하나 추가.
메모리에선? 어떻게 저장 되는걸까?

               Stack                               Heap
      _________________________            _________________________
      |    변수     |    값    |           |    변수     |    값     |
  20A |     a      |    30A   |       30A |     a      | [1,2,3,4] |
  20B |     b      |    30A   |           |            |           |
      |_______________________|           |________________________|

Heap 영역의 값이 변했다 4가 추가된것을 확인 할 수 있다.
근데 b가 아닌 a의 값이 바뀌었다 왜?
왜냐하면 stack b의 변수값은 30A다 그렇기 때문에 30A 주소로 가서 값을 바꿨다 정상이다
하지만 결론적으론 원본 배열인 a 변수(배열)의 값이 바뀌게 된것이다.
그래서 앞에서 push로 값이 추가되면 원본 배열이 바뀐다고 한것이다.

이런 이유로 원본 배열값이 바뀌게 된 것이다.

원본 배열을 건들지 않고 값을 변경할려면?

concat()나 spread 연산자를 사용하면 된다.

a = [1, 2, 3]
b = a;

b = b.concat(4); or b = [...b, 4];

여기서 a와 b값을 확인해보자

      
                    Stack                               Heap
          _________________________            _________________________
          |    변수     |    값    |           |    변수     |    값     |
      20A |     a      |    30A   |       30A |     a      |  [1,2,3]  |
      20B |     b      |    30A   |           |            |           |
          |_______________________|           |________________________|
   
      ```

여기까지는 똑같은 것을 확인 할 수 있다 하지만 b값이 변경되면.

                      Stack                               Heap
          _________________________            _________________________
          |    변수     |    값    |           |    변수     |    값     |
      20A |     a      |    30A   |       30A |     a      |  [1,2,3]  |
      20B |     b      |    30B   |       30B |     b      | [1,2,3,4] |
          |_______________________|           |________________________|

Stack 영역에서 b 변수가 가지고 있는 값(Heap의 주소)가 변경된다.
Heap영역에서 새로운 주소를 할당받아 새로운 값이 생긴다.
이젠 더 이상 b 변수는 a 변수의 주소를 참조하지 않게 된다.
따라서 원본 배열이 변경되지 않는다.

이렇게 값 변경할때 concat(), spread 연산자 등을 사용해도 되고
복사할때 spread 연산자, slice() 등을 사용하면 된다.

b = a; // 이렇게 대입연산자로 복사하지 말고
b = [...a]; // spread 연산자 사용하여 복사하면

                  Stack                               Heap
      _________________________            _________________________
      |    변수     |    값    |           |    변수     |    값     |
  20A |     a      |    30A   |       30A |     a      |  [1,2,3]  |
  20B |     b      |    30B   |       30B |     b      |  [1,2,3]  |
      |_______________________|           |________________________|
 

이렇게 되어 원본 배열에 영향을 주지 않게 된다.

결론 : 앞으로 배열, 객체 등의 복사는 spread 연산자, slice() 등을 사용. 배열, 객체 등의 값 변경은 concat(), spread 연산자 등을 사용하자.

글을 마치며

아직 글쓰는것이 미숙하여 글의 문맥이 이상했을수도 있습니다 ㅎㅎ
제가 잘못알고 있는 내용이나 틀린 부분이 있으면 피드백 부탁드립니다!!

profile
주니어 프론트엔드 개발자입니다

4개의 댓글

comment-user-thumbnail
2023년 5월 18일

글이 굉장히 깔끔하네요!!

1개의 답글
comment-user-thumbnail
2023년 5월 20일

특정 배열이 어느 변수에 값을 할당하면 얕은복사가 되어 같은 주소를 가리키고 있지만 concat과 spread 연산자를 사용하여 값을 추가하면 새로운 주소를 할당받아 서로 다른 값이 되는 것이 흥미롭네요! 좋은 글 감사합니다!!

1개의 답글