본문 바로가기
{Javascript}

02.03.23{개발일기} Object와 Map 바르게 알고 사용하기

by Davey 2023. 2. 4.
728x90

서론


Map객체와 Object 객체의 차이는 무엇일까요? 물론 Object와 Map은 둘다 유사하게 getter와 setter 메소드를 갖고 있고, 삭제 및 수정도 가능합니다. 하지만 Map을 사용하는 특별한 이유들이 MDN 공식문서에 잘 설명되어 있음을 확인할 수 있었습니다.

대괄호 표기법의 위험성


Object로 키-값을 관리하면 악성해커의 경우 쉽게 대괄호표기법을 활용해 키와 값을 재정의하여 보안에 취약할 수 있습니다. 무슨 근거로 보안에 취약한지 궁금하여 관련내용을 MDN에서 더 찾아보았는데, 이를 이해하기 위해선 먼저 Object 키에 접근하는 대괄호 표기법과 점표기법의 기능 차이부터 짚고 넘어가야 했습니다.

대괄호 표기법 vs 점표기법



'점표기법'은 변수로 특정 프로퍼티 값을 수정 불가능하지만,
'대괄호 표기법'은 변수로도 프로퍼티 값을 재정의 할 수 있다.

무려 4100명씩이나 해당 포스팅에 좋아요를 누른데에는 이유가 있었다.



이를 Object에 확장해서 생각해보면 'Object'는 대괄호 표기법을 활용해 '새로운 변수로도 객체를 재정의 가능'함을 의미합니다. 만약 이런 특징을 악용해 악성해커가 나쁜 의도를 가지고 객체값을 재정의 한다면 어떻게 될까요? 보안에 몹시 치명적일 수 밖에 없습니다. 이를 객체주입공격이라고 정의합니다. 하지만, 'Map'은 대괄호 표기법이든 점표기법이든 두가지 모두다 객체값 설정이 불가능하기 때문에, 상대적으로 보안에 덜 취약하다고 이해할 수 있습니다. 객체주입공격과도 무관함을 확장해서 고려해 볼 수 있습니다.

순회


Object는 이터레이션 프로토콜을 구현하지 않기 때문에, for ... of 문을 사용하여 순회가 불가능하다. 대신 for ... in으로 순회가 가능한데, 이때 순회되는 값들은 객체 고유의 프로퍼티 뿐만 아니라 상속받은 객체의 프로퍼티도 순회한다. 이러한 특징때문에 이터레이션 프로토콜이 객체에 구현된 경우, for ... of문 사용을 권장한다고 한다. 하지만 Object는 사용 불가능하기 때문에, 이터레이션 프로토콜이 구비되어 있고, for ...of문 사용가능한 Map의 존재는 더욱 돋보일 수 밖에 없다. 아래는 ES6 객체리터럴 문법인 __proto__를 사용해 직접상속을 구현한 객체 순회 예시코드이다.

 

const isSoldout = { state: true };

const stock = {
  company: 'Tesla',
  owner: 'Illon Musk',
  address: 'CA in U.S.',
  call: 'Elon Reeve Musk',
  // ES6 객체리터럴 문법 : __proto__ 접근자 프로퍼티를 사용하여 직접 상속 구현
  ___proto__: isSoldout
}

for (let info in stock){
  console.log(info);
}

// company
// owner
// anddress
// call
// isSoldout


for (let info of stock) {
  console.log(info);
}

//Uncaught TypeError: stock is not iterable



참고로, 자바스크립트에서는 '배열'도 Object 타입에 속하기 때문에 '프로퍼티'를 가질 수 있는데, 'for ... in'으로 순회하게 되면 배열의 요소뿐만 아니라 '프로퍼티까지 순회'된다. 하지만 forEach()와 for ...of 문을 사용하면 프로퍼티는 제외된 요소값들만 순회 가능하다는 점도 참고하자.

 

const nums = [1, 2, 3];
nums.float = 17.1234; // 배열도 객체이므로 프로퍼티를 가질 수 있다.

for (const num in nums) {
  // 프로퍼티 float도 출력된다.
  console.log(num);
}

// 1
// 2
// 3
// 17.1234

for (const num of nums) {
  console.log(num);
}

// 1
// 2
// 3

// forEach 메서드는 프로퍼티는 제외한 요소만 출력한다.

nums.forEach( num => console.log(num)); // 1 2 3


키유형


Map은 키유형이 '유연'하다. 이는 다양한 문제상황에서 데이터 키값을 커스터마이징하여 'getter'와 'setter'가 가능하다는 장점이 있다. 이와 달리 Array는 String과 Symbol 타입 범위가 제한적이다.


키순서


Map에서 키는 단순하고 직관적인 방식으로 정렬된다. Map 객체는 항목을 '삽입한 순서대로' 키와 값이 할당된다. 고정타입의 경우 Array가 상황에 따라선 더 좋을 수 도 있을 것으로 예상된다.


크기


Object 아이템수는 수동적으로 항목을 추가 삭제하여 관리해야하는 불편함이 있다. size 프로퍼티 값이 따로 없기 때문에, Object 객체의 size를 알기 위해선 Object.keys의 length값을 활용해야 한다. Map은 size 프로퍼티 값이 존재하므로 크기를 쉽게 얻을 수 있는 장점이 있다.

성능


개발자로써 자료구조의 성능에 대해 고민하지 않을 수 없다. 기본적으로 Object는 빈번한 키-값 쌍의 추가 및 삭제에 최적화되어 있지 않다고 한다. 이와 달리 Map은 키-값 쌍의 빈번한 추가 및 삭제에도 최적화되어 시간복잡도가 낮은 점이 특징이다.


직렬화


보통 JSON객체를 HTTP통신할 때 표준객체로 사용하게 된다. 이때, Object 객체는 JSON.stringfy()와 JSON.parse()를 통한 JSON객체로 변환이 가능하다. Map도 마찬가지로 JSON 객체로 직렬화 가능한 장점이 있다. 다만, JSON.stringfy()에 두번째 인자로 replacer라는 인자를 넣어줘야 직렬화 가능하다고 한다.



참고 출처 링크


[객체주입공격 개요]
https://codeburst.io/javascript-quickie-dot-notation-vs-bracket-notation-333641c0f781
[MDN Map객체 설명]
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Map
[대괄호표기법의 위험성]
https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/the-dangers-of-square-bracket-notation.md


Copyright ⓒ 2023 by bluevulpe

728x90

댓글