인터랙티브 자바스크립트라는 수업시간에 getElementsByClassName()이라는 메서드에 대해 배웠습니다. 이 메서드는 유사배열이라는 형태로 값을 반환한다고 하는데, 그 이유가 궁금해서 찾아본 내용입니다.
getElementsByClassName() 메서드가 유사배열이라는 형태로 값을 반환하는 이유는 getElementsByClassName()은 문서 안에서 같은 클래스를 가진 요소가 몇 개일지 미리 알 수 없기 때문입니다. 요소는 1개일 수도 있고, 100개일 수도 있지만, 결국 중요한 사실은 자바스크립트 코드를 실행하기 전에는 요소의 개수를 확인할 수 없다는 점입니다. 그래서 브라우저는 찾은 요소들을 순서대로 나열해서 담아줄 그릇을 필요로하게 됩니다. 이때 배열이 아니라 HTMLCollection이라는 걸 사용해서 반환합니다.
여기서 핵심은, 이 메서드는 1990년대 DOM 표준(DOM Level 1~2)이 만들어질 때부터 있던 아주 오래된 Web API라는 점입니다. 그 당시엔 지금 우리가 쓰는 Array.prototype.map, forEach 같은 배열 메서드들이 자바스크립트에 아예 존재하지 않았습니다. DOM은 사실 자바스크립트 전용으로 설계된 게 아니라 여러 언어(Java, VBScript 등)에서도 쓸 수 있게 언어 중립적으로 설계되었거든요. 그래서 배열이라는 자바스크립트 고유의 자료구조 대신, DOM 자체의 규격인 HTMLCollection이라는 형태로 정의되었습니다. 이게 지금까지 하위 호환성 때문에 그대로 유지되고 있는 거고요.
유사배열은 이름 그대로 생김새는 배열과 같이 보이지만, 배열이 아닌 것을 의미합니다.
const boxes = document.getElementsByClassName('box');
boxes.forEach(box => console.log(box)); // ❌ TypeError: boxes.forEach is not a function
getElementsByClassName()이 반환하는 HTMLCollection은 살아있는(live) 컬렉션입니다. 즉, DOM이 바뀌면 이 컬렉션도 자동으로 실시간 갱신되죠. 반면 querySelectorAll()이 반환하는 NodeList는 정적(static)이라서 나중에 DOM이 바뀌어도 처음 찾은 스냅샷 그대로 유지되는 모습을 보입니다. 이 부분도 헷갈리기 쉬운 부분이니까 기억해두면 좋아요!
map, filter 같은 배열 메서드가 필요할 때는, 진짜 배열로 변환하는 방식을 통해 배열 메서드를 사용할 수 있습니다. 가장 많이 쓰는 방법 두가지를 소개해 볼게요.
const boxes = document.getElementsByClassName('box');
// 방법 1: Array.from() 사용 (가장 많이 씀)
const boxArray = Array.from(boxes);
// 방법 2: 스프레드 문법 사용
const boxArray2 = [...boxes];
// 이제 배열 메서드 다 사용 가능!
boxArray.forEach(box => console.log(box));
참고로 실무에서 순수 HTML/CSS/JS로 작업할 때는 getElementsByClassName보다 querySelectorAll을 더 많이 쓰는 편이라고 합니다.
참고
나의 친구 클로드! 🤣