Set 생성자 및 add 메서드 동작에 대한 의문사항
📌 Set 생성자의 동작 방식
자바스크립트의 Set 생성자(new Set())는 다음과 같이 동작한다.
- 인수 없음: new Set()처럼 인수를 전달하지 않으면 빈(Empty) Set 객체가 생성된다.
- 하나의 이터러블 인수: new Set(iterable)처럼 하나의 이터러블(Iterable) 객체(예: 배열 Array, 문자열 String, 다른 Set 등)를 인수로 전달하면, 해당 이터러블의 요소들을 순회하면서 고유한 값들만 추출하여 새로운 Set 객체를 생성한다.
- ⚠️ 여러 개의 인수 또는 이터러블이 아닌 인수: Set 생성자는 오직 첫 번째 인수만 확인한다.
- 만약 첫 번째 인수가 이터러블이면, 그 이터러블의 요소들로 Set을 만든다. 두 번째 이후의 인수들은 모두 무시된다.
- 만약 첫 번째 인수가 이터러블이 아니거나(null, undefined, 숫자 등. 단, 문자열은 이터러블임), 인수가 아예 없으면 그에 따라 동작한다 (이터러블 아니면 보통 오류 발생, 없으면 빈 Set).
🔎1️⃣ 그렇다면 SET은 인수가 여러개 일 경우 맨 처음의 인수만 취사선택한다는 얘기인가?
✨ 여러 개의 인수가 전달될 경우
- Set 생성자는 오직 첫 번째 인수만 살펴본다.
- 두 번째, 세 번째 이후의 모든 인수들은 완전히 무시된다.
- 만약 첫 번째 인수가 이터러블(Iterable) 객체라면, 그 객체의 요소들을 기반으로 Set을 생성한다.
- 만약 첫 번째 인수가 이터러블이 아니거나(null, undefined 등) 존재하지 않으면, 그에 맞춰 동작한다 (보통 오류가 발생하거나 예상치 못한 결과가 나올 수 있다).
// 인수를 1개만 선택
const mySet = new Set('red', 'green', 'blue');
console.log(mySet); // 출력: Set { 'r', 'e', 'd' }
// 올바른 방법: 배열을 단일 인수로 전달
const mySetCorrect = new Set(['red', 'green', 'blue']);
console.log(mySetCorrect); // 출력: Set { 'red', 'green', 'blue' }
⚠️ 따라서 new Set('red', 'green', 'blue')와 같이 여러 인수를 전달하는 것은 Set 생성자의 의도된 사용 방식이 아니며, 첫 번째 인수('red')만 처리되고 나머지는 버려지는 예상치 못한 동작으로 이어지는 것이다.
이는 Set 생성자의 명세(Specification) 자체가 new Set([iterable]) 형태로, 선택적으로 하나의 이터러블만을 받도록 정의되어 있기 때문이다. 그 정의에 맞지 않는 추가적인 인수들은 고려 대상에서 제외되는 것이다.
🔎2️⃣ 그렇다면 SET에 문자가 아닌 인자를 넣을 수 있는가?
그렇다, Set은 모든 타입의 값을 저장할 수 있다
✨ Set 객체 안에 저장되는 요소
- 모든 타입 저장 가능: Set 객체는 자바스크립트의 모든 데이터 타입의 값을 요소(Element)로 저장할 수 있다.
- 원시 타입: number, string, boolean, null, undefined, symbol, bigint
- 참조 타입: Object, Array, Function, 다른 Set, Map 등
- 고유한 값 유지: Set의 가장 큰 특징은 고유한 값만 저장한다는 것이다. 동일한 값을 여러 번 추가하려고 해도 한 번만 저장된다.
- 값의 동일성 여부는 기본적으로 === (Strict Equality Comparison) 와 유사하게 판단하지만, 한 가지 중요한 예외가 있다: NaN은 === 비교 시 자신과도 false가 나오지만, Set에서는 NaN을 동일한 값으로 취급하여 하나만 저장한다. 또한 -0과 +0은 동일한 값으로 취급한다.
const myDiverseSet = new Set();
// 다양한 타입의 값 추가
myDiverseSet.add(100); // 숫자
myDiverseSet.add("world"); // 문자열
myDiverseSet.add(true); // 불리언
myDiverseSet.add(null); // null
myDiverseSet.add(undefined); // undefined
const mySymbol = Symbol("id");
myDiverseSet.add(mySymbol); // 심볼
const myObj = { key: "value" };
myDiverseSet.add(myObj); // 객체
const myArray = [1, 2];
myDiverseSet.add(myArray); // 배열
myDiverseSet.add(NaN); // NaN
myDiverseSet.add(NaN); // 두 번째 NaN은 추가되지 않음
console.log(myDiverseSet);
// 출력 예시 (순서는 다를 수 있음):
// Set { 100, 'world', true, null, undefined, Symbol(id), { key: 'value' }, [ 1, 2 ], NaN }
console.log(myDiverseSet.has(NaN)); // 출력: true
console.log(myDiverseSet.size); // 출력: 9 (중복된 NaN은 제외됨)
💡 결론
Set 객체 자체는 어떤 타입의 값이든 저장할 수 있는 유연한 자료구조이다. 'red', 'green', 'blue' 같은 문자열뿐만 아니라 숫자, 불리언, 객체, 배열, null, undefined 등 모든 값을 고유하게 저장하는 데 사용된다.
이전 예시 new Set('red', 'green', 'blue')가 Set { 'r', 'e', 'd' }가 된 이유는 Set이 문자만 저장할 수 있어서가 아니라, Set 생성자가 첫 번째 인수인 문자열 'red'만을 이터러블로 처리하여 각 문자를 요소로 만들었기 때문이다.
🔎3️⃣ new Set(21); 은 안되는 이유가 무엇인가?
new Set(21); 코드가 오류(TypeError)를 발생시키는 주된 이유는 전달된 인수 21이 원시 타입(Primitive Type)인 숫자(Number)이며, 숫자는 이터러블(Iterable) 객체가 아니기 때문이다.
- Set 생성자의 요구사항: new Set() 생성자는 초기 요소를 채우기 위해 선택적으로 하나의 이터러블 객체를 인수로 받는다. 인수가 전달되면, 생성자는 그 인수를 순회하여 각 요소를 새로운 Set 객체에 추가하려고 시도한다.
- 숫자의 비-이터러블 특성: 숫자 21은 그 자체로 순회할 수 있는 요소들을 가지고 있지 않다. 즉, [Symbol.iterator] 메서드가 없어 이터러블 프로토콜을 따르지 않는다.
- 결과: Set 생성자가 숫자 21을 순회하려고 시도하지만 실패하므로, TypeError: number 21 is not iterable 와 같은 오류가 발생한다.
💡 이터러블 예시: Array, String, Set, Map 등은 이터러블 객체이므로 new Set()의 인수로 사용될 수 있다.
🔎4️⃣new Set() 이 만드는 것 : 빈 객체? 빈 배열?
new Set() (인수 없이 호출 시)은 빈 객체({})도 아니고 빈 배열([])도 아니다.
- 빈 Set 객체: new Set()은 비어 있는(empty) Set 객체를 생성하여 반환한다.
- Set의 정체: Set은 자바스크립트의 내장 객체(Built-in Object) 유형 중 하나로, 배열이나 일반 객체와는 다른 고유한 특성을 가진다. 가장 큰 특징은 고유한 값(Unique Value)만을 저장하는 컬렉션(Collection)이라는 점이다.
- 고유 메서드 및 속성: Set 객체는 자신만의 메서드(add(), has(), delete(), clear() 등)와 속성(size)을 가진다. 이는 배열이나 일반 객체의 메서드/속성과는 다르다.
🔎5️⃣ Add로 이터러블 데이터가 아닌 데이터를 추가할 수 있는 이유가 무엇인가?
add() 메서드로 100같은 원시 타입 데이터를 추가할 수 있는 이유는 Set 생성자가 초기화를 위해 이터러블을 요구하는 것과, 생성된 Set 객체의 add() 메서드가 단일 값을 추가하는 것은 역할과 요구사항이 다르기 때문이다.
- new Set(iterable) (생성자):
- 역할: 초기화 단계에서 여러 개의 요소를 한 번에 받아 Set을 구성한다.
- 요구사항: 어떤 요소들을 넣어야 할지 알려면 그 요소들을 순회할 수 있어야 하므로, 이터러블 객체가 필요하다. 생성자는 이터러블을 순회하며 각 요소를 내부적으로 add한다.
- mySet.add(value) (메서드):
- 역할: 이미 생성된 Set 객체에 하나의 특정 요소를 추가한다.
- 요구사항: 추가하려는 값(value) 자체만 명확하면 된다. 이 값이 이터러블일 필요는 전혀 없다. Set 자체가 모든 타입의 값(원시 타입, 참조 타입 모두)을 저장할 수 있도록 설계되었기 때문에, add() 메서드는 전달받은 value가 어떤 타입이든 (예: 숫자 100) Set의 규칙(고유성)에 따라 추가하려고 시도한다.
따라서 new Set()으로 빈 Set을 만든 후, .add(100)을 사용하여 원시 타입인 숫자 100을 요소로 추가하는 것은 Set의 정상적인 사용 방법이다.
💡 요약:
- 생성자는 "어떤 요소들로 Set을 만들까?" -> 요소 목록을 순회해야 하므로 이터러블 필요.
- add 메서드는 "Set에 이 특정 값 하나를 넣자." -> 추가할 값 하나만 명시하면 되므로 이터러블 불필요.