Logo

자바스크립트 세트(Set) 완벽 가이드

세트(Set)는 자바스크립트에서 고유한 값들의 집합을 다루는 자료구조입니다. 이를 활용하면 데이터 중복을 제거하고 유일한 값들을 효과적으로 관리할 수 있는데요.

아무래도 세트가 ES6에서 추가된 기능이다 보니 아직도 많은 분들이 세트를 쓰면 딱인 경우에도 습관처럼 배열을 쓰시는 것 같습니다. 뿐만 아니라 굳이 세트를 쓸 필요가 없는 상황에서 단지 새로운 기능이라고 세트를 오용/납용하시는 주니어분들도 심심치 않게 볼 수 있는데요.

이번 포스팅에서는 다양한 예제를 통해서 자바스크립트의 Set을 어떻게 사용하는지 아주 꼼꼼하고 차근차근 알아보도록 하겠습니다.

세트 자료구조

먼저, 자바스크립트와 별개로 자료구조로서의 세트(set)에 대해서 가볍게 개념 정리를 하고 넘어가면 좋을 것 같습니다.

세트(set)는 한마디로 순서가 없는 중복되지 않은 데이터의 집합인데요. 배열(array)과 차이점을 이해하는 것이 무엇보다 중요합니다.

배열은 인덱스(index)를 통해서 특정 위치에 저장되어 있는 데이터에 접근이 가능하죠. 그리고 배열에는 동일한 값을 여러 번 저장할 수 있습니다. 값이 동일하더라도 인덱스가 틀리기 때문에 데이터의 중복이 문제되지 않습니다.

반면에 세트는 얼핏 보기에는 배열과 비슷해보일 수 있지만 사실 결이 아주 다른 자료구조입니다. 세트는 배열처럼 인덱스를 통해서 접근할 수가 없으며 중복된 데이터를 허용하지 않습니다. 따라서 기존에 세트에 저장되어 있는 값을 또 추가하면 아무 효력이 발생하지 않습니다.

세트 생성

자바스크립트에서 Set는 클래스(class)이므로 new 키워드와 생성자를 사용하여 객체를 생성할 수 있습니다.

const set = new Set(); // Set(0) {size: 0}

위와 같이 생성자의 인자로 아무 것도 넘기지 않으면 빈 세트가 만들어지며, 아래와 같이 배열을 인수로 넘기면 배열에 담긴 값으로 세트가 만들어집니다.

const numSet = new Set([1, 2, 3]); // Set(3) {1, 2, 3}

값 추가

세트에 새로운 값을 추가할 때는 add() 메서드를 사용하는데요. 위에서 설명드린 것처럼 세트에는 중복된 값이 추가되지 않으며, 유일한 값만 저장됩니다.

set.add(1); // Set(1) {1}
set.add("A"); // Set(2) {1, 'A'}
set.add(true); // Set(3) {1, 'A', true}

참고로 add() 메서드는 값을 추가한 후에 세트를 반환하기 때문에 아래와 같이 연쇄적으로 호출할 수도 있습니다.

set.add(1).add("A").add(true); // Set(3) {1, 'A', true}

값 삭제

Setdelete() 메서드를 사용하면 세트로 부터 특정 값을 삭제할 수 있는데요. delete() 메서드에 인자로 넘기는 값이 세트에 존재하여 성공적으로 삭제하였다면 true를 반환하고, 해당 값이 세트에 존재하지 않아서 삭제에 실패하였다면 false를 반환합니다.

set.delete(1); // true
set.delete(2); // false

값 존재 여부 확인

세트에 특정 값이 존재하는지 확인하려면 has() 메서드를 사용합니다. 보통 if 조건문이나 3항 연산자(ternary operator)와 많이 사용됩니다.

if (set.has("A")) {
  console.log("A는 세트에 존재합니다."); // A는 세트에 존재합니다.
}
const result = set.has("B") ? "YES" : "NO"; // NO

값의 개수 확인

세트의 size 속성을 통해서 해당 세트의 길이, 즉 얼마나 많은 값이 저장되어 있는지를 알아낼 수 있습니다.

console.log(set.size); // 2

모든 값 제거

Set 객체의 모든 값을 제거하려면 clear() 메서드를 사용합니다.

set.clear(); // Set(0) {size: 0}

세트 순회

세트에 저장되어 있는 모든 값을 순회하고 싶을 때는 어떻게 해야 할까요? 이 때는 for 루프문 안에서 of 연산자를 사용하면 됩니다.

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

배열처럼 세트도 forEach() 함수를 제공하고 있기 때문에 활용하실 수 있습니다.

numSet.forEach((num) => console.log(num));

배열을 세트로 변환

실제로 코딩을 하다보면 기존에 존재하는 배열로 부터 새로운 세트를 만들어야 하는 경우가 빈번한데요. 이 때는 Set() 생성자의 인자로 해당 배열이 할당된 변수를 넘기면 됩니다.

const array = [1, 2, 2, 3, 3, 3];
const set = new Set(array); // Set(3) {1, 2, 3}

여기서 중요한 부분은 이렇게 배열로 부터 세트를 만들어내면 중복 값이 모두 제거된다는 것입니다. 중복 값을 유지해야하는 상황에서는 절대로 배열을 세트로 변환하면 안되겠죠?

세트를 배열로 변환

그럼 반대로 세트를 배열로 변환해야 할 때는 어떻게 해야 할가요?

가장 쉬운 방법은 대괄호 안에서 세트를 상대로 전개(spread) 연산자를 사용하는 것입니다.

const array = [...set]; // [1, 2, 3]

전개 연산자를 별로 안 좋아하신다면 Array.from() 함수를 사용하는 방법도 있습니다.

const array = Array.from(set); // [1, 2, 3]

배열에서 중복 값 제거

세트는 중복 값을 허용하지 않기 때문에, 배열이나 문자열에서 중복된 값을 제거하는 데에 매우 유용하게 활용될 수 있습니다.

const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

집합 연산

여러 개의 세트를 상대로 다음과 같이 합집합, 교집합, 차집합을 구할 수 있습니다.

const set1 = new Set([1, 2, 3, 4, 5]);
const set2 = new Set([4, 5, 6, 7, 8]);

// 합집합
const union = new Set([...set1, ...set2]);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]

// 교집합
const intersection = new Set([...set1].filter((value) => set2.has(value)));
console.log([...intersection]); // [4, 5]

// 차집합
const difference = new Set([...set1].filter((value) => !set2.has(value)));
console.log([...difference]); // [1, 2, 3]

[업데이트] 최근에 union(), intersection(), difference()와 같은 집합 연산을 위한 메서드들이 세트에 추가되었습니다.

const set1 = new Set([1, 2, 3, 4, 5]);
const set2 = new Set([4, 5, 6, 7, 8]);

// 합집합
const union = set1.union(set2);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]

// 교집합
const intersection = set1.intersection(set2);
console.log([...intersection]); // [4, 5]

// 차집합
const difference = set1.difference(set2);
console.log([...difference]); // [1, 2, 3]

타입스크립트에서 세트 사용

타입스크립트를 사용하신다면 세트를 생성할 때 저장할 수 있는 자료형을 제한할 수 있는데요.

예를 들어, 다음과 같이 세트를 생성할 때 숫자형의 데이터만 저장할 수 있도록 타입 인자를 명시해주면 다른 자료형의 데이터를 추가하려고 하면 타입 에러가 발생하게 됩니다.

const numSet = new Set<number>();
numSet.add(1);
numSet.add("A"); // Argument of type 'string' is not assignable to parameter of type 'number'.(2345)
//     ^? (method) Set<number>.add(value: number): Set<number>

이러한 타입 안정성과 타입 추론을 통해 좀 더 신뢰할 수 있는 코드를 작성할 수 뿐만 아니라 코드 편집기에서 자동완성의 편리함도 누릴 수 있습니다.

마치면서

지금까지 자바스크립트에서 세트를 사용하는 방법에 대해서 자세히 살펴보았습니다. 이 정도만 숙지하시면 실전 프로그래밍에서에서 큰 부족함없이 세트를 활용하실 수 있으실 거에요. 본 포스팅을 통해서 세트 자료구조를 제대로 이해하고, 자바스크립트의 세트가 제공하는 기능들을 잘 활용하여 보다 효율적이고 간결한 코드를 작성할 수 있으셨으면 좋겠습니다.