Home

JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법

Published in javascript
October 02, 2025
2 min read
JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법

안녕하세요, 여러분! “코딩하는곰”입니다. 오늘은 JavaScript 개발을 하면서 정말 자주 마주치지만, 생각보다 해결이 쉽지 않은 “배열 중복 제거” 문제에 대해 깊이 있게 알아보려고 합니다. 여러분도 분명 Set이나 filter를 사용해봤지만, 왜인지 중복이 제대로 제거되지 않는 경험을 해보셨을 거예요. 이번 포스팅에서는 그 이유를 낱낱이 파헤쳐보고, 다양한 상황에 맞는 완벽한 해결책을 제시해드리겠습니다.

왜 Set이나 filter로 중복 제거가 안 될까?

JavaScript에서 배열 중복 제거를 시도할 때 가장 흔히 사용하는 방법이 Set 객체나 filter 메소드입니다. 하지만 많은 개발자들이 이러한 방법이 제대로 작동하지 않는 경우를 경험합니다. 그 이유를 하나씩 파헤쳐보겠습니다.

1. 참조 타입(Reference Type)의 함정

가장 큰 문제는 객체, 배열, 함수와 같은 참조 타입을 다룰 때 발생합니다. JavaScript에서 참조 타입은 값이 아닌 메모리 주소로 비교됩니다.

const array1 = [{id: 1}, {id: 2}, {id: 1}];
const array2 = [{id: 1}, {id: 2}, {id: 1}];
// Set을 사용한 중복 제거 시도
const uniqueWithSet = [...new Set(array1)];
console.log(uniqueWithSet.length); // 3 - 중복 제거 실패!
// filter를 사용한 중복 제거 시도
const uniqueWithFilter = array1.filter((item, index) =>
array1.findIndex(obj => obj.id === item.id) === index
);
console.log(uniqueWithFilter.length); // 2 - 성공!

위 예제에서 볼 수 있듯이, Set은 객체의 참조를 비교하기 때문에 내용이 같아도 다른 메모리 주소를 가진 객체는 다른 값으로 인식합니다.

2. 데이터 타입 불일치 문제

숫자와 문자열, null과 undefined 등 서로 다른 데이터 타입이 혼합되어 있을 때 예상치 못한 동작이 발생할 수 있습니다.

const mixedArray = [1, '1', 2, '2', null, undefined, null];
const uniqueSet = [...new Set(mixedArray)];
console.log(uniqueSet); // [1, '1', 2, '2', null, undefined]

숫자 1과 문자열 ‘1’은 Set에서 다른 값으로 처리됩니다. 이는 의도한 동작일 수 있지만, 때로는 개발자가 원하는 결과와 다를 수 있습니다.

3. NaN의 특이한 동작

JavaScript에서 NaN은 자기 자신과도 같지 않은 특이한 값입니다.

console.log(NaN === NaN); // false
const arrayWithNaN = [1, 2, NaN, 3, NaN, 4];
const uniqueArray = [...new Set(arrayWithNaN)];
console.log(uniqueArray); // [1, 2, NaN, 3, NaN, 4] - 중복 제거 실패!

Set 내부에서는 SameValueZero 비교 알고리즘을 사용하기 때문에 NaN은 항상 같은 값으로 처리되어야 하지만, 일부 환경에서는 예상과 다르게 동작할 수 있습니다.

JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법
JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법


🔧 새로운 기술을 배우고 싶다면, (자바8 출시 10년) 람다가 가져온 함수형 프로그래밍의 혁명적 변화를 참고해보세요.

객체 배열의 중복 제거: 전문가의 솔루션

객체로 구성된 배열에서 중복을 제거하는 것은 특히 까다롭습니다. 여러 가지 전문적인 접근법을 살펴보겠습니다.

1. JSON.stringify를 활용한 방법

가장 간단하면서도 효과적인 방법 중 하나입니다.

const objectsArray = [
{id: 1, name: 'John'},
{id: 2, name: 'Jane'},
{id: 1, name: 'John'},
{id: 3, name: 'Bob'}
];
const uniqueObjects = objectsArray.reduce((acc, current) => {
const stringified = JSON.stringify(current);
if (!acc.has(stringified)) {
acc.add(stringified);
acc.result.push(current);
}
return acc;
}, { result: [], has: new Set() }).result;
console.log(uniqueObjects);
// [{id: 1, name: 'John'}, {id: 2, name: 'Jane'}, {id: 3, name: 'Bob'}]

2. Lodash의 uniqBy 함수 활용

실무에서는 Lodash 라이브러리의 uniqBy 함수를 사용하는 것이 가장 안정적입니다.

// Lodash 사용 예제
const _ = require('lodash');
const users = [
{id: 1, name: 'John', age: 25},
{id: 2, name: 'Jane', age: 30},
{id: 1, name: 'John', age: 25},
{id: 3, name: 'Bob', age: 35}
];
const uniqueUsers = _.uniqBy(users, 'id');
console.log(uniqueUsers);
// id 기준으로 중복 제거된 배열

3. 커스텀 비교 함수를 사용한 고급 기법

더 복잡한 비교가 필요할 때는 커스텀 비교 함수를 작성할 수 있습니다.

function deepEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || obj1 === null ||
typeof obj2 !== 'object' || obj2 === null) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
function removeDuplicatesWithCustomCompare(array, compareFn) {
return array.filter((item, index) =>
index === array.findIndex(obj => compareFn(obj, item))
);
}
// 사용 예제
const complexArray = [
{user: {id: 1, profile: {name: 'John'}}},
{user: {id: 2, profile: {name: 'Jane'}}},
{user: {id: 1, profile: {name: 'John'}}}
];
const uniqueComplex = removeDuplicatesWithCustomCompare(complexArray, deepEqual);

JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법
JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법


시간을 측정해야 하는 다양한 상황에서, 간단하고 직관적인 스톱워치를 활용하면 편리합니다.

성능 최적화와 실무 적용 팁

대규모 데이터를 다룰 때는 성능이 매우 중요합니다. 다양한 상황에 맞는 최적화 기법을 알아보겠습니다.

1. Map 객체를 활용한 고성능 중복 제거

Map 객체는 키-값 쌍을 저장하는 데 최적화되어 있어 대용량 데이터 처리에 효과적입니다.

function removeDuplicatesWithMap(array, keyFn) {
const map = new Map();
for (const item of array) {
const key = keyFn ? keyFn(item) : JSON.stringify(item);
if (!map.has(key)) {
map.set(key, item);
}
}
return Array.from(map.values());
}
// 사용 예제
const largeArray = [
{id: 1, timestamp: '2023-01-01'},
{id: 2, timestamp: '2023-01-02'},
{id: 1, timestamp: '2023-01-01'}, // 중복
{id: 3, timestamp: '2023-01-03'}
];
// id 기준으로 중복 제거
const uniqueLargeArray = removeDuplicatesWithMap(largeArray, item => item.id);

2. 인덱싱을 활용한 성능 개선

데이터베이스처럼 인덱스를 생성하여 검색 성능을大幅 향상시킬 수 있습니다.

class Deduplicator {
constructor(keyFields = []) {
this.keyFields = keyFields;
}
deduplicate(array) {
const index = new Map();
const result = [];
for (const item of array) {
const key = this.generateKey(item);
if (!index.has(key)) {
index.set(key, true);
result.push(item);
}
}
return result;
}
generateKey(item) {
if (this.keyFields.length === 0) {
return JSON.stringify(item);
}
return this.keyFields.map(field => {
const value = this.getNestedValue(item, field);
return `${field}:${JSON.stringify(value)}`;
}).join('|');
}
getNestedValue(obj, path) {
return path.split('.').reduce((current, key) =>
current && current[key], obj
);
}
}
// 사용 예제
const deduplicator = new Deduplicator(['id', 'user.profile.name']);
const complexData = [
{id: 1, user: {profile: {name: 'John'}}},
{id: 2, user: {profile: {name: 'Jane'}}},
{id: 1, user: {profile: {name: 'John'}}} // 중복
];
const uniqueData = deduplicator.deduplicate(complexData);

3. 실무에서 자주 마주치는 시나리오별 솔루션

시나리오 1: API 응답 데이터 중복 제거

async function fetchAndDeduplicateData() {
try {
const responses = await Promise.all([
fetch('/api/users'),
fetch('/api/alt-users')
]);
const allUsers = await Promise.all(
responses.map(response => response.json())
);
// 2D 배열을 1D로 평탄화하고 중복 제거
const flattenedUsers = allUsers.flat();
const uniqueUsers = removeDuplicatesWithMap(
flattenedUsers,
user => user.id
);
return uniqueUsers;
} catch (error) {
console.error('Data fetching failed:', error);
throw error;
}
}

시나리오 2: 실시간 스트림 데이터 중복 제거

class StreamDeduplicator {
constructor() {
this.seenIds = new Set();
this.windowSize = 1000; // 최근 1000개 아이디만 기억
this.idQueue = [];
}
process(item) {
if (this.seenIds.has(item.id)) {
return null; // 중복된 항목
}
// 새 항목 처리
this.seenIds.add(item.id);
this.idQueue.push(item.id);
// 윈도우 크기 유지
if (this.idQueue.length > this.windowSize) {
const oldestId = this.idQueue.shift();
this.seenIds.delete(oldestId);
}
return item;
}
}

JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법
JavaScript 배열 중복 제거가 안 될 때? 99%가 놓치는 핵심 원인과 해결법


📌 영양제 선택이 어려울 때 참고하면 좋은, 고업 액상 철분를 참고해보세요.

지금까지 JavaScript 배열 중복 제거의 다양한 측면을 깊이 있게 살펴보았습니다. 단순히 Set이나 filter 메소드를 사용하는 것에서 그치는 것이 아니라, 데이터의 특성과 상황에 맞는 최적의 방법을 선택하는 것이 얼마나 중요한지 이해하셨을 거라 생각합니다. 기억하세요, 완벽한 중복 제거 솔루션은 존재하지 않습니다. 여러분의 데이터 구조, 성능 요구사항, 브라우저 호환성 등을 종합적으로 고려하여 가장 적합한 방법을 선택해야 합니다. 이 글이 여러분의 개발 여정에 도움이 되셨다면, “코딩하는곰”의 다음 포스팅도 기대해주세요! 궁금한 점이 있으시면 언제든지 댓글로 질문해주시면 성심껏 답변드리겠습니다. 행복한 코딩 되세요! 🐻

📣 지금 화제가 되고 있는 문화행사는 바로, 영천보현산별빛축제를 참고해보세요.









최상의 건강을 위한 영양가득한 식품과 정보! life-plus.co.kr 바로가기
최상의 건강을 위한 영양가득한 식품과 정보! life-plus.co.kr 바로가기



다채로운 문화축제와 공연 소식을 공유하는 블로그! culturestage.co.kr 바로가기
다채로운 문화축제와 공연 소식을 공유하는 블로그! culturestage.co.kr 바로가기



비트코인 세계로의 첫걸음! 지금 가입하고 거래 수수료 할인 혜택 받으세요! bitget.com 바로가기
비트코인 세계로의 첫걸음! 지금 가입하고 거래 수수료 할인 혜택 받으세요! bitget.com 바로가기




Tags

#developer#coding#javascript

Share

Previous Article
(파이썬 클로저 완벽 가이드) nonlocal 키워드로 중첩 함수의 변수 스코프 마스터하기

Table Of Contents

1
왜 Set이나 filter로 중복 제거가 안 될까?
2
객체 배열의 중복 제거: 전문가의 솔루션
3
성능 최적화와 실무 적용 팁

Related Posts

(실전 프로젝트) localStorage를 활용한 나만의 메모장 웹 앱 만들기 - 데이터 저장부터 불러오기까지 완벽 구현
December 30, 2025
2 min