요즘IT
위시켓
AIDP
콘텐츠프로덕트 밸리
요즘 작가들컬렉션물어봐
놀이터
콘텐츠
프로덕트 밸리
요즘 작가들
컬렉션
물어봐
놀이터
새로 나온
인기
개발
AI
IT서비스
기획
디자인
비즈니스
프로덕트
커리어
트렌드
스타트업
서비스 전체보기
위시켓요즘ITAIDP
고객 문의
02-6925-4867
10:00-18:00주말·공휴일 제외
yozm_help@wishket.com
요즘IT
요즘IT 소개작가 지원
기타 문의
콘텐츠 제안하기광고 상품 보기
요즘IT 슬랙봇크롬 확장 프로그램
이용약관
개인정보 처리방침
청소년보호정책
㈜위시켓
대표이사 : 박우범
서울특별시 강남구 테헤란로 211 3층 ㈜위시켓
사업자등록번호 : 209-81-57303
통신판매업신고 : 제2018-서울강남-02337 호
직업정보제공사업 신고번호 : J1200020180019
제호 : 요즘IT
발행인 : 박우범
편집인 : 노희선
청소년보호책임자 : 박우범
인터넷신문등록번호 : 서울,아54129
등록일 : 2022년 01월 23일
발행일 : 2021년 01월 10일
© 2013 Wishket Corp.
로그인
요즘IT 소개
콘텐츠 제안하기
광고 상품 보기
개발

Object vs Map vs Set, 언제 무엇을 써야 할까?

효빈
9분
2시간 전
231
에디터가 직접 고른 실무 인사이트 매주 목요일에 만나요.
newsletter_profile0명 뉴스레터 구독 중

자바스크립트는 객체(Object)와 배열(Array)만으로도 대부분의 기능을 구현할 수 있는 언어입니다. 실제로 많은 프로젝트에서 “데이터는 객체에 담고, 목록은 배열로 돌린다”라는 방식만으로도 개발이 잘 굴러갑니다. 그런데 ES6 이후 Map과 Set이라는 컬렉션 타입이 추가되면서 선택지가 늘어났고, 그 순간부터 컬렉션 선택은 단순한 취향이 아니라 설계의 문제가 되었습니다. 

 

“객체로도 충분한데 Map을 왜 써야 하지?”, “중복 제거는 배열로 filter 돌리면 되지 않나?” 같은 고민은 대부분 한 번쯤 해보셨을 텐데요, 이 고민은 결국 키 타입의 제약, 접근 패턴, 직렬화 필요, 코드 의도 표현 같은 요소들로 연결됩니다.

 

특히 React/Next.js 환경에서 API 응답을 가공해 빠르게 조회해야 하거나, 캐시를 만들어 동일한 요청을 반복하지 않도록 막아야 하거나, UI에서 선택 상태를 효율적으로 관리해야 하는 순간이 자주 찾아옵니다. 이럴 때 어떤 컬렉션을 쓰느냐에 따라 코드가 “그냥 동작하는 수준”에서 끝날 수도 있고, 팀이 함께 읽기 쉬운 구조로 정리될 수도 있습니다. 이번 글에서는 Object, Map, Set을 비교하면서 각각이 강해지는 순간을 잡아보고, 마지막에는 실무에서 바로 적용할 수 있는 선택 기준까지 정리해 보겠습니다.

 

미리 요점만 콕 집어보면?

  • 자바스크립트는 객체(Object)와 배열(Array)만으로도 대부분의 기능을 구현할 수 있는 언어입니다.
  • ES6 이후 Map과 Set이라는 컬렉션 타입이 추가되면서 선택지는 늘어났고, 컬렉션 선택은 설계의 문제가 되었습니다.
  • Object, Map, Set은 서로 대체 관계가 아니라 데이터의 성격과 사용 목적에 따라 강점이 달라지는 도구입니다.
 

자바스크립트 컬렉션을 한 번에 조망하기

<출처: Maya Shavin Medium, ES6 — Map vs Object — What and when?>

 

이 글을 제대로 이해하려면 먼저 Object, Map, Set이 각각 “무엇을 위해 존재하는 도구인지”를 한 번에 정리하고 가는 것이 좋습니다. 같은 ‘저장소’처럼 보여도 설계 목적이 다르고, 그 차이가 API 형태와 사용 패턴, 그리고 팀 코드에서의 의도 표현 방식까지 이어지기 때문입니다. 여기서 큰 그림을 잡아두면, 이후 장에서 “왜 이런 상황엔 Map이 더 깔끔한가”, “왜 Set이 선택 상태에 잘 맞는가”가 자연스럽게 연결됩니다.

 

1) Object·Map·Set은 “저장 목적”이 다르다

Object는 원래부터 “프로퍼티를 가진 객체”로 설계된 존재라서 레코드(record)나 설정(options), 그리고 JSON과 호환되는 데이터 표현에 강합니다. 반면 Map은 “키-값 저장소”라는 목적이 훨씬 명확한 자료구조입니다. get/set/has/delete/size 같은 API가 “이건 사전이다”라는 의도를 그대로 드러내죠. Set은 “중복 없는 값의 집합”을 표현하기 위해 만들어졌기 때문에, 포함 여부를 확인하는 has를 중심으로 상태를 깔끔하게 표현할 수 있습니다. 즉, 컬렉션을 고른다는 건 “지금 저장하려는 게 레코드인지, 인덱스/캐시인지, 선택 집합인지”를 결정하는 행위에 가깝습니다.

 

2) 실무에서 컬렉션 선택이 갈리는 대표 장면들

실무에서 갈림길은 생각보다 뻔하게 반복됩니다. API에서 목록을 받아왔는데 화면에서는 id로 자주 찾고 싶다거나, 체크박스에서 선택된 항목들을 토글해야 한다거나, 동일한 요청을 반복하지 않도록 결과를 캐싱해야 하는 상황이 그렇습니다. 배열과 객체만으로도 해결은 가능하지만, 그 순간부터 find, includes, filter, Object.keys 같은 코드 조각이 여기저기서 반복되면서 의도가 흐려지고 유지보수가 어려워지기 시작합니다. 반대로 로컬스토리지나 서버 전송처럼 JSON 직렬화가 필요한 경우에는 Object/Array가 유리하고, Map/Set을 쓰면 경계에서 변환 규칙을 추가로 설계해야 합니다. 이런 식으로 “요구사항의 형태”가 컬렉션 선택을 사실상 결정합니다.

 

 

객체(Object)로 키-값을 관리할 때의 특징

이제부터는 각 컬렉션을 하나씩 뜯어보겠습니다. 먼저 Object는 가장 익숙한 도구이고, 실제로 많은 경우 ‘정답’이기도 합니다. 다만 Object를 ‘사전(dictionary)’처럼 적극적으로 쓰기 시작하는 순간, 키 제약과 프로토타입 영향, 크기 확인/순회 방식 같은 특성이 성능과 버그 가능성에 영향을 줄 수 있습니다. Object가 강한 지점과 주의할 지점을 함께 잡아두면, “언제 Map으로 넘어가야 하는지” 기준이 훨씬 명확해집니다.

 

1) 객체 키의 한계와 함정(키 변환, 프로토타입 영향)

객체의 프로퍼티 키는 결국 문자열 또는 심볼만 됩니다. 숫자 키를 넣어도 내부적으로 문자열로 변환되어 저장되기 때문에, “키의 타입이 유지될 것”이라고 기대하면 미묘한 버그가 날 수 있습니다. 또한 Object는 기본적으로 Object.prototype을 상속받기 때문에, 사전처럼 쓰는 상황에서는 프로토타입 체인 영향이 함정이 되기도 합니다. 특히 in 연산자는 프로토타입까지 훑기 때문에 “내가 직접 넣은 키인지”를 판단할 때 조심해야 하고, 안전하게 검사하려면 Object.hasOwn 같은 도구를 써야 합니다.

 

const dict = { a: 1 };

// 프로토타입까지 탐색하므로 true가 될 수 있음
console.log("toString" in dict); // true

// 내 프로퍼티인지 확인하려면 hasOwn 계열을 사용
console.log(Object.hasOwn(dict, "toString")); // false
console.log(Object.hasOwn(dict, "a")); // true

 

또는 아예 프로토타입이 없는 “순수 딕셔너리”가 필요하다면 Object.create(null)을 쓰기도 합니다. 이런 선택이 필요하다는 것 자체가, Object가 딕셔너리 전용 도구는 아니라는 의미입니다.

 

const safeDict = Object.create(null);
safeDict["toString"] = "ok";
console.log(safeDict.toString); // undefined (프로토타입이 없으므로 메서드가 없음)
console.log(safeDict["toString"]); // "ok"

 

2) 직렬화(JSON)와 궁합이 좋은 이유, 그리고 순회/크기 확인 포인트

Object의 가장 큰 장점은 JSON과의 자연스러운 호환입니다. 서버로 데이터를 전송하거나 로컬스토리지에 저장할 때 JSON.stringify를 바로 적용할 수 있다는 점은 실무에서 매우 강력합니다. 반면, 크기 확인은 보통 Object.keys(obj).length를 사용하게 되는데, 이 과정에서 키 배열을 만들기 때문에 “아주 자주” 호출되는 상황에서는 비용이 누적될 수 있습니다. 순회도 마찬가지로 Object.entries로 배열을 만들고 순회하는 패턴이 많기 때문에, 접근 패턴이 복잡해질수록 Map이 더 맞는 상황이 생깁니다.

 

const userSettings = { theme: "dark", lang: "ko" };
localStorage.setItem("settings", JSON.stringify(userSettings));

 

 

Map이 필요한 순간: 객체를 대체하는 키-값 저장소

Object가 “레코드/JSON 데이터”에 강하다면, Map은 “딕셔너리/인덱스/캐시”에 강합니다. Map은 API 설계 자체가 키–값 저장소에 최적화되어 있고, 키 타입 제한이 없으며, 크기 확인과 삭제/조회가 명확합니다. 그래서 “이게 옵션 객체인지, 인덱스인지” 같은 애매함 없이, 자료구조의 의도를 코드에 남길 수 있습니다. 특히 React/Next.js에서 목록을 조회용 인덱스로 바꾸거나, 캐시를 운영하는 순간 Map의 장점이 크게 드러납니다.

 

1) “모든 타입이 키”가 되는 Map의 결정적 차이

Map은 키로 어떤 값이든 사용할 수 있습니다. 숫자, 문자열은 물론이고 객체, 함수도 키가 됩니다. 즉, 객체 키처럼 문자열 변환에 의존하지 않습니다. 또한 size가 기본 제공되기 때문에 크기 확인이 자연스럽고, has/get/set/delete가 표준 API로 존재해 코드 의도가 분명해집니다.

 

const map = new Map();

const objKey = { id: 1 };
map.set(objKey, "value");

console.log(map.get(objKey)); // "value"
console.log(map.size);        // 1

 

2) 실전 패턴: API 응답을 인덱싱하고 캐시로 쓰는 방법

실무에서 가장 자주 쓰는 패턴은 “배열로 온 API 응답을 Map 인덱스로 바꿔서 조회 비용과 코드 중복을 줄이는 것”입니다. 게시글 목록을 배열로 들고 있다면 특정 게시글을 찾을 때마다 find가 필요하지만, Map 인덱스를 만들어두면 조회 의도가 훨씬 선명해집니다.

 

const posts = [
  { id: 101, title: "A" },
  { id: 102, title: "B" },
  { id: 103, title: "C" },
];

const postById = new Map(posts.map((p) => [p.id, p]));

console.log(postById.get(102)); // { id: 102, title: "B" }
console.log(postById.has(999

 

Next.js에서도 같은 원리가 적용됩니다. 서버에서 받은 목록을 UI 렌더링용 배열로 유지하되, 상세 뷰나 편집 화면에서 id로 빠르게 접근해야 한다면 Map 인덱스를 함께 유지하는 방식이 생산적입니다. 또한 캐시를 만들 때도 Map은 유용합니다. 요청 키를 문자열로 정규화해 Map에 저장하면 중복 호출을 줄일 수 있습니다.

 

const cache = new Map();

async function fetchWithCache(url) {
  if (cache.has(url)) return cache.get(url);

  const res = await fetch(url);
  const data = await res.json();
  cache.set(url, data);
  return data;
}

 

다만 Map은 JSON 직렬화가 기본으로는 안 됩니다. 로컬스토리지에 저장해야 한다면 Array.from(map.entries()) 같은 형태로 변환하는 규칙을 팀에서 합의해 두는 것이 좋습니다.

 

 

Set이 필요한 순간: 중복 없는 집합과 선택 상태

Set은 “중복 없는 값”이라는 요구사항을 가장 직접적으로 표현하는 컬렉션입니다. 배열로도 중복 제거를 할 수는 있지만, 반복적으로 includes, filter가 등장하면서 코드가 길어지고 의도가 흐려질 때가 많습니다. 반면 Set을 쓰면 “포함 여부 확인”이 has로 고정되고, 추가/삭제도 add/delete로 고정되기 때문에, 코드를 보는 순간 이 데이터가 “집합(set)”이라는 게 명확해집니다. React에서 선택 상태나 방문 여부, 토글 가능한 상태를 다룰 때 특히 잘 맞습니다.

 

1) 중복 제거/포함 체크에서 Set이 더 자연스러운 이유

배열로 포함 여부를 검사하는 코드는 흔하지만, 포함 여부 검사 자체가 로직의 중심이 되기 시작하면 Set이 더 자연스럽습니다. 예를 들어, 사용자가 입력한 태그를 중복 없이 관리하거나, 특정 ID가 이미 처리되었는지 빠르게 확인해야 하는 상황은 Set이 딱 맞습니다.

 

const tags = new Set(["js", "react"]);
tags.add("next");
tags.add("react"); // 중복 추가해도 변화 없음

console.log(tags.has("react")); // true
console.log(tags.size);         // 3

 

2) 실전 패턴: 선택된 ID 집합, 방문 여부 플래그 관리

React UI에서 체크박스 선택 상태는 Set이 특히 빛납니다. 배열로 선택 상태를 관리하면 토글마다 배열을 복사하고 필터링해야 하며, 코드가 장황해집니다. Set은 토글 로직이 매우 직관적입니다. 다만 React state로 쓸 때는 불변성을 지키기 위해 “새 Set을 만들어 교체”하는 패턴을 쓰는 게 일반적입니다.

 

let selected = new Set([1, 3]);

function toggle(id) {
  const next = new Set(selected);
  if (next.has(id)) next.delete(id);
  else next.add(id);
  selected = next;
}

toggle(3);
toggle(2);

console.log([...selected]); // [1, 2]

 

방문 여부 플래그도 같은 구조로 풀립니다. 이미 처리한 ID를 Set에 넣어두고, 다음에 들어오면 무시하는 방식은 중복 실행을 막는 데 효과적입니다.

 

 

객체 vs Map vs Set, 무엇을 선택할 것인가

여기까지 보면 “각각의 장점”은 이해되지만, 실무에서는 결국 “그래서 지금은 뭘 쓰지?”가 가장 중요합니다. 그래서 마지막은 체크리스트 방식으로 정리하는 게 좋습니다. 기준을 몇 개만 고정해 두면, 어떤 팀원이 와도 동일한 결론을 낼 수 있고, 컬렉션 선택이 취향 논쟁이 아니라 설계 결정으로 정착됩니다. 특히 Next.js처럼 서버-클라이언트 경계를 자주 넘나드는 환경에서는 직렬화 요구가 선택을 좌우하는 경우가 많습니다.

 

<출처: 퍼플렉시티>

 

1) 선택 체크리스트: 키 타입 · 접근 패턴 · 직렬화 요구

키 타입부터 봅니다. “문자열/심볼 키만 있으면 된다”는 경우가 많고, 이때는 Object가 충분히 좋습니다. 반대로 “키가 숫자/객체/함수 등 다양한 타입일 수 있다”, 또는 “인덱스/캐시라는 의도를 API로 명확히 드러내고 싶다”면 Map이 맞습니다. 값의 집합을 다루고, 중복을 허용하지 않으며, 포함 여부 체크가 로직의 중심이라면 Set이 맞습니다. 

 

그 다음으로 접근 패턴을 봅니다. 순회가 중심이면 배열이 편하고, 조회가 중심이면 Map이 편합니다. 마지막으로 직렬화 요구를 확인합니다. JSON 저장/전송이 필수라면 Object/Array가 유리하고, Map/Set을 쓰더라도 경계에서 변환 규칙을 설계해야 합니다.

 

2) React/Next.js 실전 시나리오로 빠르게 결론 내리기

예를 들어 API에서 게시글 리스트가 배열로 내려왔고, 화면은 리스트를 렌더링해야 하니 배열은 그대로 두는 게 자연스럽습니다. 그런데 상세 패널이나 편집 모달에서 특정 항목을 id로 계속 찾아야 한다면, 조회용 인덱스(Map)를 함께 만들어두면 코드가 깔끔해집니다. 체크박스 선택 상태나 방문 여부는 Set이 의도를 잘 드러내므로, 상태 관리에서 버그를 줄이는 데 도움이 됩니다. 

 

반대로 폼 입력값이나 설정처럼 결국 서버에 보내거나 로컬스토리지에 저장해야 한다면 Object가 가장 안전합니다. 이런 기준을 팀에 공유해두면 “왜 Map/Set을 쓰는지”가 명확해지고, 코드 리뷰도 훨씬 빨라집니다.

 

 

마치며

Object, Map, Set은 서로 대체 관계가 아니라, 데이터의 성격과 사용 목적에 따라 강점이 달라지는 도구입니다. JSON 직렬화와 단순 레코드에는 Object가, 다양한 타입 키와 빈번한 조회/삭제가 필요한 인덱스·캐시에는 Map이, 중복 없는 값 집합과 포함 여부 체크가 핵심인 상태에는 Set이 특히 잘 맞습니다. 

 

컬렉션 선택 기준을 체크리스트처럼 갖고 있으면 “익숙해서”가 아니라 “요구사항에 맞아서” 컬렉션을 선택할 수 있고, 그 선택은 코드의 의도를 선명하게 만들며 유지보수성을 높여줍니다. 다음에 “이거 그냥 객체로 할까?”라는 생각이 들 때, 오늘 정리한 기준을 한 번만 적용해 보셔도 훨씬 깔끔한 구조로 이어질 가능성이 큽니다.


<참고> 

  • Maya Shavin Medium, ES6 — Map vs Object — What and when?
  • https://infoscis.github.io/2018/01/27/ecmascript-6-sets-and-maps/

 

©️요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

));
// false
console
.log(postById.size);
// 3