본문 바로가기
이론/Frontend

innerHTML과 insertAdjacentHTML

by 유세지 2022. 3. 31.

그동안 DOM 요소를 변경하거나 추가할 때, 별 생각 없이 innerHTML을 자주 사용해오곤 하였습니다. 그런데 얼마 전, DOM 요소를 조작할 때 사용되는 메서드가 innerHTML 뿐이 아니라는 것을 알게되었습니다. 심지어 innerHTML 보다 성능도 좋다고하여 놀랐습니다. 어떠한 동작 원리로 메서드 간의 성능 차이가 나게 되었는지, DOM 요소를 효율적으로 조작하는 방법은 무엇인지 알아보겠습니다.

 

* 이 글은 각 방법의 동작 방식과 차이점에 초점을 두고 비교하는 글입니다. 단순 사용법이 궁금하시다면 MDN 공식 문서를 참고해주세요!

 

 

Element.innerHTML

innerHTML은 요소 내에 포함된 HTML (또는 XML) 마크업을 가져오거나 설정하는 Element의 속성입니다. 이 속성에는 해당 요소의 자식 요소들을 문자화시킨 DOMString이 들어가게 됩니다.

 

아래 사진은 이 블로그의 메인화면에서 포스트들이 있는 영역을 innerHTML을 통해 확인해 본 모습입니다.

 

innerHTML

 

각종 HTML 태그들이 문자열화(DOMString) 되어있는 모습이 콘솔에 출력되었습니다. innerHTML은 이런식으로 자식 요소들에 대한 정보를 가지고 있는 프로퍼티입니다.

 

그럼 이제 우리가 흔히 사용하던 방식으로 innerHTML을 통해 DOM 요소를 조작해보겠습니다.

 

const newDOMString = "<p>DOM을 변경했습니다!</p>";

document.element.innerHTML = newDOMString;

 

기존의 내용을 대체할 newDOMString 변수를 생성하여, element.innerHTML에 대입해주는 간단한 코드입니다.

위 코드를 실행시켰을때, 내부적으로는 어떤 동작이 일어나는지 간단히 그림으로 나타내면 이렇습니다.

 

 

코드가 실행되면 element가 기존에 가지고 있던 자식 요소를 파싱하고, 새 값으로 들어온 newDOMString 또한 파싱하여 DocumentFragment 객체를 생성합니다. 이렇게 파싱된 객체를 element의 innerHTML에 넣어줌으로써 element의 DOM 요소가 새로운 모습으로 변하게 됩니다.

 

 

Element.insertAdjacentHTML

insertAdjacentHTML은 innerHTML과 다르게, element의 자식 요소를 파싱하는 동작이 없습니다. 그저 오른편에 들어온 값만을 파싱하여 특정 위치의 DOM 트리 안에 대입할 뿐입니다. 코드로 나타내면 이렇습니다.

 

const newDOMString = "<p>DOM을 변경했습니다!</p>";

document.element.insertAdjacentHTML('beforeend', newDOMString);

 

아까와 비슷하지만, 이 코드는 element의 자식 요소를 대체하지 않는 대신 자식 요소의 끝 부분에 newDOMString을 추가해줍니다. 위 코드가 어떻게 동작하는지 그림으로 살펴보겠습니다.

 

 

 

innerHTML과 달리 자식 요소를 파싱하는 부분이 사라졌습니다. 두 방식의 속도 차이가 어디에서 기인하는지 이제야 알겠네요. 더군다나 DOM 조작은 비싼 비용을 치르기 때문에, 다른 요소보다도 크게 다가올 가능성이 있어 보입니다.

 

 

 

번외

이와 관련한 내용을 찾아보던 중, 흥미로운 블로그 글을 하나 발견했습니다.

 

MS 에서 권장하는 DHTML 자바스크립트 튜닝 12단계, https://rabbitchris.tistory.com/367 (래빗 크리스)

 

물론 여기서 소개한 방법은 insertAdjacentHTML이 innerHTML보다 빠른 이유와는 별개의 케이스이고, 글 자체도 2009년에 번역된 것을 보아 지금 적용한다고 해도 극적인 차이는 기대하기 어려울 것이라 예상되지만, 마침 성능에 관한 이슈로 이 포스트를 작성하는만큼 또 다른 방식을 적용했을 때 2~3배 이상의 차이가 나는 방법이 있다는 말은 제 호기심을 자극하기 충분했습니다.

 

아쉽게도 본문에 연결된 링크는 더 이상 연결되지 않았습니다. 하지만 본문에 테스트 코드가 남아 있으니 이것을 활용하여 확인해 보겠습니다.

 

느린 코드
빠른 코드

 

100개로는 유의미한 비교가 어려워 10000개의 요소를 단순 생성하여 실행 시간을 측정해보았는데, 확실히 createElement와 insertAdjacentElement를 사용한 코드 쪽이 약 60%정도 빠른 성능을 보여주었습니다. 2~3배까지는 모르겠지만 분명히 효과가 있네요. 두 코드의 차이라곤 DocumentFragment의 파싱 여부인데, 확실히 DOM 작업의 비용이 비싸다는 것을 알 수 있었습니다.

 

 

번외2

여기에 한 가지 더, 스택오버플로우에서 재밌는 글을 찾았는데 innerText보다도 더 빠른 textContext를 사용하라는 내용이었습니다.

 

Difference between textContent vs innerText, https://stackoverflow.com/questions/35213147

 

innerText는 비표준이었으며 (현재는 표준입니다.) 눈에 보이는 노드의 텍스트만 포함하기 때문에, 별도의 css 처리 과정이 들어가며 이에 따른 리플로우가 발생하게 됩니다. 그에 반해 textContext는 별다른 처리 과정 없이 요소의 텍스트 값을 그대로 가져오기 때문에 innerText보다 빠른 처리 속도를 가질 수 있다고 합니다.

 

MDN Docs - Node.textContent

 

 

그렇다면 위의 코드에서 innerText 대신 textContent를 사용하면 더 빨리 작업을 수행할 수 있을 것입니다.

바로 확인해보겠습니다.

 

 

더 빠른 코드

 

멋지네요. 처음 테스트 한 느린 코드보다 약 두 배 가량 빨라졌습니다.

 

 

정리

- innerHTML은 기존 요소를 파싱하기 때문에 비싼 DOM 작업을 필요로한다.

- 따라서 기존 요소를 파싱하지 않는 insertAdjacentHTML을 사용하면 작업을 빠르게 완료할 수 있다.

- 파싱 작업을 아예 배제하고, createElement와 insertAdjacentElement를 사용하면 더 빠르게 완료할 수 있다.

- innerText 대신 textContent를 쓰면 더더더 빠르게 완료할 수 있다.

 

참고 문서

MS 에서 권장하는 DHTML 자바스크립트 튜닝 12단계 - 래빗 크리스님 번역

Element.innerHTML - MDN Docs

Element.insertAdjacentHTML() - MDN Docs

Difference between textContent vs innerText

Node.textContent - MDN Docs

 

 

끝까지 읽어주셔서 감사합니다.

틀린 부분은 댓글로 알려주시면 감사하겠습니다!

반응형

댓글