[JS] 배열 객체 제대로 비교하기

Yunhye Park·2024년 3월 19일
0

Back To Basic

목록 보기
5/10
post-thumbnail

최근에 진행한 프로젝트에서 Todo List가 있는 컴포넌트를 다른 컴포넌트에서 실시간으로 연동하는 파트를 맡았다. 생성이나 삭제 요청이 발생할 때마다 배열 비교가 필요했고, 객체를 문자열로 변환(JSON.stringfy)이 필요했다. 특히 순서의 변화도 감지해야 하는 상황이라서.

왜 단순히 동등연산자로 비교할 수 없는가? 이유는 데이터 타입에 있다. 자바스크립트의 타입은 크게 원시형과 참조형으로 나뉜다.

원시형이라 함은,

  • string
  • number
  • bigint
  • boolean
  • undefined
  • symbol
  • null

이렇게 7가지다. 이 타입은 아래와 같은 특징이 있다.

1. 원시 값

변경 불가한 값 (read only)

  • 변수, 즉 하나의 값에 배정된 메모리 공간은 선언 > 할당 > 재할당을 거치며 계속 그 위치가 바뀐다. 이미 특정 메모리 주소에 할당된 원시 값은 바뀔 수 없으므로, 변수의 위치는 할당 값마다 바뀔 수밖에 없는 것이다.
  • 변수가 차지하는 메모리 공간에 실제 값을 저장한다. 그래서 원시 값을 갖는 변수(ex. string, number)를 다른 변수에 할당 시 원시 값을 복사한다. 이를 '값에 의한 전달(pass by value)'이라고도 표현할 수 있다.
var score = 80; // 선언 및 할당
var scoreCopy = score;

score = 100; // 재할당 : 메모리 위치 변화
console.log(score); //100
console.log(scoreCopy); //80

각 변수는 별개의 메모리 주소에서 각자 원시 '값'을 가지고 있다. 타입, 값 모두 같더라도 값만 전달했으므로 동기화되지 않는다.

cf. 사실 정확히 말하자면 식별자(변수)는 메모리 값이 아니라 주소를 기억한다. 주소는 고유하지만 값은 고유하지 않아서다. 전달 받은 메모리 주소를 통해 메모리 공간에 접근해 값을 참조하고, 그 값을 새 주소에 저장한 것이다.
그러나 차치하고, 다른 공간에 저장된 별개의 값이라서 서로 영향을 주고받지 않는다는 사실이 가장 중요하다.

그럼 객체는 메모리 할당 면에서 어떤 특징을 가질까?

2. 객체

변경 가능한 값

객체는 재할당 없이 직접 값을 변경할 수 있어서 추가, 삭제, 갱신이 가능하다.

이게 뭔 말인가 하면,

  • 변수에 할당 시 확보된 메모리 공간에 참조 값을 저장한다.
  • 메모리 주소를 통해 그 공간에 들어서면 참조 값에 접근할 수 있다. 이는 실제 객체가 저장된 메모리 공간 주소이다. 즉 주소 값을 얻는다.
    • 그래서 원시 값의 변수가 '변수의 값은 ~이다'고 말하는 것과 달리 '변수는 객체를 참조한다/변수는 객체를 가리킨다'라고 표현한다.
    • 객체를 가리키는 변수를 다른 변수에 할당 시 참조 값이 복사 (참조에 의한 전달 pass by reference)
  • 이처럼 동적인 기능이 가능하기에 코드 작성 시엔 편하지만, 유지에 어려움이 많다. 할당할 메모리 크기를 전혀 예측할 수 없기 때문에 원시 값처럼 복사한 것을 개별적으로 저장하면 메모리 효율성과 성능이 떨어진다.
  • 하여 같은 메모리 주소를 여러 개의 식별자가 공유한다. 메모리 주소가 저장된 위치는 다르지만 주소 내용은 같다. 고로 기존 객체의 변화에 복사한 객체도 영향 받는다.
var person = {
	name: 'k'
};

var person2 = {
	name: 'k'
};

위 객체 둘을 그림으로 표현하자면,

말로 풀이하자면 이렇다.

  1. 변수가 있는 메모리 공간에 값이 아닌 참조 주소(0x00002320)가 있다.
    ➡️ 객체는 변수의 메모리 위치는 달라도 프로퍼티가 같다면 똑같은 메모리 주소를 참조할 수 있다.

    cf. 물론 원시 값도 엄밀히 말하자면 주소를 기억하는 건 마찬가지이나(주소는 유일무이한데 값 자체는 동일할 수 있어서 헷갈릴 가능성 존재) 주소를 통해 그 값에 접근한다는 점이 다르다.

  2. 기존 객체(person)를 복사한 객체(person2) 모두 똑같은 값을 참조(0x00002320)한다.
    ➡️ 그래서 기존 객체의 값을 바꾸면 복사한 객체도 영향을 받는다.
    ex. name 프로퍼티 값을 'P'로 변경 -> person2의 name도 P로 변경됨

이렇다보니 객체에는 아래와 같은 개념이 따라온다.

  • 얕은 복사(shallow copy) : 한 단계까지만 복사, 즉 주소의 복사
  • 깊은 복사(deep copy) : 중첩된 객체까지 복사, 즉 주소에 할당된 값 복사

다시 문제로 돌아와서, 그럼 기존 배열과 새 배열을 비교하려면 어떤 복사를 해야 할까? 바로 깊은 복사다. 할당된 주소가 아닌 객체의 값 내지는 요소를 비교할 수 있도록 문자열 변환 과정이 필요한 것도 이래서다.

끝으로 프로젝트에서 사용한 코드 일부를 예시로 기록하며 자바스크립트 배열 비교 포스팅을 마친다!

적용 코드

// Todo 객체 타입 정의
export interface TodoContent {
  todoId: number;
  todoContent: string;
  todoDate: string;
  nickname: string;
  userId: number;
}
[];

// Todo 비교
  const compareArrays = (arr1: TodoContent[], arr2: TodoContent[]): boolean => {
    return JSON.stringify(arr1) === JSON.stringify(arr2);
  };

📖 참고 서적 📖

자바스크립트 딥 다이브

profile
일단 해보는 편

0개의 댓글

관련 채용 정보