인턴

[성능 최적화⚙️] Set 활용해보기

choijming21 2025. 4. 14. 18:44

 데이터 검색 성능은 웹 애플리케이션의 사용자 경험에 직접적인 영향을 미치는 중요한 요소입니다. 특히 데이터 목록이 많아질수록 검색 알고리즘의 효율성이 더욱 중요해집니다. 이 글에서는 JavaScript에서 배열의 includes() 메서드와 Set 객체의 has() 메서드의 차이점을 살펴보고, 실제 사용 사례를 통해 성능 최적화 방법을 알아보겠습니다.

 

 

 

 

✅ 배열과 Set의 기본 개념

 

☑️ 배열(Array)

배열은 JavaScript에서 가장 기본적인 자료구조로, 여러 값을 순서대로 저장합니다. 배열에서 특정 값을 찾을 때는 주로 includes() 메서드를 사용합니다.

const favorites = ["BTC", "ETH", "XRP"];
console.log(favorites.includes("BTC")); // true
console.log(favorites.includes("SOL")); // false

 

 

☑️ Set 객체

Set은 ES6에서 도입된 자료구조로, 중복되지 않는 값들의 집합을 저장합니다. Set의 주요 특징은 해시 테이블 기반으로 동작하여 값 검색이 매우 빠르다는 점입니다.

const favSet = new Set(["BTC", "ETH", "XRP"]);
console.log(favSet.has("BTC")); // true
console.log(favSet.has("SOL")); // false

 

 

☑️ 시간 복잡도 비교

두 메서드이 가장 큰 차이점은 시간 복잡도에 있습니다.

메서드 시간 복잡도 설명
Array.includes() O(n) 배열의 모든 요소를 순차적으로 검사해야 함
Set.has() O(1) 해시 테이블 기반으로 상수 시간에 검색 가능

 

 

☑️ 실습

금융 데이터를 표시하는 웹 애플리케이션을 예로 들어보겠습니다. 사용자가 즐켜찾기로 등록한 암호 화폐 목록과 전체 암호 화폐 목록이 있다고 가정해 보겠습니다.

 

 

☑️ 비효율적 방식: Array.includes()

// 사용자가 즐겨찾기한 암호화폐 심볼 목록
const favorites = ["BTC", "ETH", "XRP", /* ... 더 많은 항목 */];

// 전체 암호화폐 목록에서 즐겨찾기 여부 확인
const facorites = mockAssets.filter(asset => favorites.includes(asset.symbol));

 

🍂 이 방식의 문제점:

  • 각 asset 마다 favorites 배열 전체를 순회해야 함
  • mockAssets가 500개, favorites가 100개 라면 최대 50,000번의 비교 연산이 발생
  • 목록이 많을수록 성능이 급격히 저하됨

 

☑️ 최적화된 방식: Set.has()

// 사용자가 즐겨찾기한 암호화폐 심볼 목록을 Set으로 변환
const favSet = new Set(favorites);

// 전체 암호화폐 목록에서 즐겨찾기 여부 확인
const favoriteCryptos = mockAssets.filter(asset => favSet.has(asset.symbol));

 

🌱 이 방식의 장점:

  • 각 asset마다 상수 시간 O(1)에 검색 가능
  • Set은 한번만 생성하고, 이후 모든 검색은 즉시 수행됨
  • 데이터가 많아질수록 성능 차이가 극대화됨

 

☑️ 실제 구현 예시

// 상태 관리에서 즐겨찾기 목록 가져오기
const favorites = useAssetStore((state) => state.favorites);

// Set으로 변환하여 검색 최적화
const favSet = new Set(favorites);

// 각 자산을 순회하며 즐겨찾기 여부 확인
return mockAssets.map(asset => {
  const isFav = favSet.has(asset.symbol);
  
  return (
    <div key={asset.symbol}>
      {asset.name} ({asset.symbol})
      <span>{isFav ? '★' : '☆'}</span>
    </div>
  );
});

 

 

☑️ 성능 측정 결과

실제 성능 차이를 확인하기 위해 간단한 벤치마크를 실행해보았습니다.

// 테스트 데이터 준비
const largeArray = Array.from({ length: 10000 }, (_, i) => `item-${i}`);
const searchItems = Array.from({ length: 1000 }, (_, i) => `item-${i * 5}`);
const searchSet = new Set(searchItems);

// 배열 방식 성능 측정
console.time('Array includes');
const arrayResults = largeArray.filter(item => searchItems.includes(item));
console.timeEnd('Array includes');

// Set 방식 성능 측정
console.time('Set has');
const setResults = largeArray.filter(item => searchSet.has(item));
console.timeEnd('Set has');

 

실행 결과:

  • Array includes: 약 320ms
  • Set has: 약 5ms

이는 Set을 사용한 방식이 배열을 사용한 방식보다 약 64배 빠르다는 것을 보여줍니다!

 

 

 

 

 

✅ 결론

  • 소규모 데이터의 경우 두 방식의 성능 차이는 미미하므로 가독성과 편의성을 우선할 수 있습니다.
  • 대규모 데이터 또는 빈번한 검색이 필요한 경우:
    • 원본 데이터는 배열로 관리
    • 검색이 필요할 때 Set으로 변환하여 사용하는 것이 최적의 방법입니다.
  • 실시간 필터링이나 대량 렌더링이 필요한 UI 컴포넌트에서는 Set 사용을 적극 고려해야 합니다.