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]

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

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

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

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>

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

마치면서

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