Array.prototype.includes vs indexOf 성능 비교

배경

레거시 코드를 리팩토링하면서 배열 내 값 존재 여부를 확인하는 로직들을 정리하게 됐다. 기존에는 indexOf를 사용했는데, ES6의 includes로 바꾸면 가독성이 더 좋아진다.

// 기존
if (roles.indexOf('admin') !== -1) {
  // ...
}

// 변경 후
if (roles.includes('admin')) {
  // ...
}

코드는 확실히 깔끔해졌는데, 성능 차이가 있는지 확인이 필요했다.

벤치마크

const arr = Array.from({ length: 10000 }, (_, i) => i);
const target = 9999;

console.time('indexOf');
for (let i = 0; i < 100000; i++) {
  arr.indexOf(target) !== -1;
}
console.timeEnd('indexOf');

console.time('includes');
for (let i = 0; i < 100000; i++) {
  arr.includes(target);
}
console.timeEnd('includes');

Node 8.11에서 테스트한 결과, 두 메서드의 성능 차이는 거의 없었다. 오히려 includes가 미세하게 빠른 경우도 있었다.

NaN 처리 차이

벤치마크보다 중요한 차이점을 발견했다.

const arr = [1, 2, NaN];

arr.indexOf(NaN);  // -1
arr.includes(NaN); // true

indexOf는 NaN을 찾지 못하지만 includes는 정확히 찾는다. 이 부분 때문에라도 includes를 사용하는 게 맞다고 판단했다.

결론

성능 차이는 무시할 수준이고, 가독성과 정확성 면에서 includes가 우위에 있다. 팀 컨벤션에 includes 사용을 추가했고, 기존 코드도 점진적으로 마이그레이션하기로 했다. 다만 IE11 지원이 필요한 프로젝트에서는 polyfill이 필요하다.