DOM Collection을 탐색할 때는 while을 사용하자

야밤에 굉장히 이상한 버그를 겪었다. 우리 회사에서 제공하는 기능 중에 Fancy Anywhere라는 기능이 있다. 문서에 우리가 제공하는 스크립트 포함시키고, 이미지에 특정한 클래스 이름을 붙이면 관련 상품으로 연결되는 기능이다. 따라서 당연히 문서에 있는 이미지 전체를 탐색하는 코드가 포함되어 있다. 오늘의 문제는 여기서 발생했다.

어떤 환경에서도 잘 동작하여야 하며, 그러면서도 가벼움을 유지해야 하는 스크립트이므로, 코드는 어떠한 프레임워크의 도움도 없는 순수한 DOM API로만 그리고 최대한 하위 호환성을 보장하도록 작성했다. 그래서 이미지를 가져오는 방법도 document.images 혹은 document.getElementsByTagName('img')와 같은 굉장히 전통적인 방법을 사용했다. 이렇게 DOM 노드의 컬렉션(collection)을 구한 후 for문을 사용해 이 배열같은 녀석을 훑었다.

document.images 크기 버그
document.images 크기 버그

분명 수백 번 수천 번은 족히 작성해왔을 코드인데 에러가 발생한다. 로직의 문제라면 수정하면 되지만 그것도 아니다. document.images를 콘솔에 출력해보면 분명 위의 이미지에서 보듯 39개의 이미지가 있는 컬렉션이 반환되는데 document.images.length를 출력해보면 17이 나타난다. document.images가 문제인가 싶어 document.getElementsByTagName('img')으로 바꾸었으나 결과는 같았다. 위의 과정이 DOMContentLoaded 이벤트에 걸려있는데 혹시 이벤트 발생 시점이 문제인가 싶어 함수 실행을 최대 2초까지 지연시켜 보았으나 개수가 25개로 조금 늘어났을 뿐 역시 결과는 크게 다르지 않았다. 최근 크롬이 Blink 엔진으로 변경된 탓인가 싶었으나 파이어폭스에서도 동일한 문제가 발생하였으므로 이 가설 역시 맞지 않았다. 몇 번의 테스트 결과 다음과 같은 가능성을 도출했다.

1. 해당 환경(텀블러 페이지였음)에서 DOM Collection을 조작하는 코드가 포함되어 있을 것이다.
2. 자바스크립트 또는 DOM API 자체의 문제이다.

하지만 스펙 자체가 이상한 것이 아니라면 구현체가 다른 크롬과 파이어폭스에서 동일한 현상이 발생하기 어려울 것이라 2번은 확률이 적다. 따라서 현재로서는 1번이 가장 공산이 큰 가설인데 잠깐 테스트를 해보았으나 DOM Collection의 프로토타입을 조작하는 코드는 찾기 어려웠다. 결국 두 가설 모두 증명할 길이 없어 문제의 원인은 찾지 못한 채 length를 사용하지 않는 방향으로 코드를 수정하기로 했다.

기존 코드는 다음과 같이 DOM Collection을 탐색했었다(물론, 정확히 같은 코드는 아니다).

for(i=0, c=document.images.length; i < c; i++) {
   im = document.images[i];
   ... blah blah ...
}

이 코드를 다음과 같이 수정하였다.

i = 0;
while(im=document.images[i++]) {
   ... blah blah ...
}

물론 이 문제가 특정한 환경에서만 발생하는 것일 수도 있다. 그렇다고 해도 DOM Collection이 외부 요인에 의해 비정상적으로 동작할 수 있다는 사실은 변하지 않으므로 가능하면 for문 대신 while문을 사용하는 것을 권장한다.

댓글을 남겨주세요