innerHTML vs createElement

윤상준·2022년 3월 20일
2

JavaScript

목록 보기
1/2
post-thumbnail

DOM에 새로운 요소를 추가하는 방법

바닐라JS에서 DOM에 새로운 요소를 추가하는 방법은 다양하다. 나는 보통 innerHTMLcreateElement()를 많이 사용했는데, 지금까지 둘의 차이점을 잘 몰랐던 채로 사용해왔기에 이번에 알아보았다.

createElement()

craeteElement()를 통해 요소를 생성하고 그 안에 새로운 요소를 추가할 수 있다.

container 클래스를 갖고 있는 div 태그가 있다고 가정해보자.

<div class="container"></div>

createElement()를 사용한다면 다음과 같이 새로운 요소를 추가할 수 있다.

let div = document.querySelector('.container');

let p = document.createElement('p');
p.textContent = 'JS DOM';
div.appendChild(p);

새로운 p태그를 생성하고, 'JS DOM'이라는 텍스트를 추가한 후 컨테이너의 자식으로 추가한다.

innerHTML

특정 요소를 innerHTML로 접근해서 innerHTML = '새로운 요소' 식으로 직접 변경하거나, innerHTML += '새로운 요소' 식으로 추가할 수 있다.

다음과 같이 직접 변경하거나,

let div = document.querySelector('.container');
div.innerHTML = '<p>JS DOM</p>';

새로 추가할 수 있다.

let div = document.querySelector('.container');
div.innerHTML += '<p>JS DOM</p>';

innerHTML vs createElement()

두 방식은 어떤 차이가 있을까?

1. createElement()가 더 효율적이다.

innerHTML을 사용하면 createElement()에 비해 더 깔끔하고 간편하게 요소를 추가할 수 있다.

let div = document.querySelector('.container');
div.innerHTML += '<p class="note">JS DOM</p>';

하지만 innerHTML은 DOM을 직접적으로 수정 (Modify)하기 때문에, innerHTML이 실행될 때마다 모든 DOM 요소가 전부 재분석 (reparse)되고 재생성 (recreate)된다.

이처럼 매번 reparse와 recreate가 일어난다면, 규모가 매우 큰 프로젝트에서는 심각한 성능 문제와 예기치 못한 버그를 야기할 수 있다.

따라서 createElement()처럼 요소를 새롭게 생성하고 DOM 트리에 추가하는 방식이 더 효율적이다.

2. innerHTML로 추가된 요소는 자동으로 이벤트 등록이 되지 않는다.

innerHTML로 추가한 요소는 사전에 작성한 이벤트가 자동으로 적용되지 않는다. 따라서 하나하나 추적해서 수동으로 적용시켜줘야 한다.

앞서 얘기했듯이 innerHTML은 실행될 때마다 모든 DOM 요소가 전부 재분석 (reparse)되고 재생성 (recreate)되기 때문이다.

쉽게 말해 기존에 있던 HTML은 날아가고 새로운 HTML로 갈아끼워진다. 따라서 기존에 있던 HTML에 적용시켜놓았던 이벤트 핸들러는 전부 사라지게 된다.

3. createElement()가 더 안전하다.

innerHTML신뢰할 수 있는 출처 (데이터베이스)로부터 얻은 데이터를 DOM 트리에 추가하는 경우에만 사용하는 것이 바람직하다.

만약 아무런 컨트롤을 할 수 없는 데이터를 innerHTML로 추가했을 경우, 악성 코드가 추가되어 피해를 입을 가능성이 있다. (cross-site scripting (XSS))

이에 대해 MDN에서는 다음과 같은 예시와 함께 경고하고 있다.

const name = "<img src='x' onerror='alert(1)'>";
el.innerHTML = name; // shows the alert

경고: 프로젝트가 보안 점검을 거치게 되는 프로젝트인 경우, innerHTML 을 사용하면 코드가 거부 될 가능성이 높습니다. 예를 들어, 브라우저 확장에서 innerHTML을 사용하고 addons.mozilla.org에 확장을 제출하면 자동 검토 프로세스를 통과하지 못합니다.

4. DocumentFragment를 사용해보도록 하자.

다음과 같이 반복문을 통해 1000개의 DOM 요소를 추가하는 상황이 있다.

let div = document.querySelector('.container');

for (let i = 0; i < 1000; i++) {
   let p = document.createElement('p');
   p.textContent = `Paragraph ${i}`;
   div.appendChild(p);
}

하지만 이 방식은 1000번의 반복 동안 매번 새로운 요소를 생성하고 바로 DOM 트리에 추가한다. 따라서 매번 DOM의 렌더링이 (스타일링, 페인팅, 레이아웃) 새로 발생하므로 매우 비효율적이다.

대신에, DocumentFragment를 사용하여 DOM 요소를 사전에 미리 구성해놓고 마지막에 한방에 DOM 트리에 추가하는 방식을 사용할 수 있다.

let div = document.querySelector('.container');

// compose DOM nodes
let fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
   let p = document.createElement('p');
   p.textContent = `Paragraph ${i}`;
   fragment.appendChild(p);
}

// append the fragment to the DOM tree
div.appendChild(fragment);

createDocumentFragment()로 생성한 DocumentFragment는 DOM 트리에 추가하기 전까지는 DOM 트리와 아무런 관련이 없다. 따라서 브라우저 상에서 어떠한 일도 발생하지 않으므로, 매번 렌더링을 새로 할 필요가 없다.

비슷한 방식으로 insertAdjacentHTML을 사용할 수도 있다.

출처

https://www.javascripttutorial.net/javascript-dom/javascript-innerhtml-vs-createelement/
https://stackoverflow.com/questions/2946656/advantages-of-createelement-over-innerhtml
https://betterprogramming.pub/dom-manipulation-the-dangers-of-innerhtml-602f4119d905

profile
하고싶은건 많은데 시간이 없다!

0개의 댓글