JS로 DOM 요소를 삽입할 때 : insertAdjacentHTML

김서진·2021년 9월 5일
15

kimyo_javascript

목록 보기
2/5
post-custom-banner

요소(element)의 내용을 변경하는 대신 HTML을 문서(document)에 삽입하려면, insertAdjacentHTML() 메서드를 사용하십시오.

innerHTML에 대한 MDN 문서를 읽다가 요소 변경이 아닌 요소 삽입만을 해야 하는 상황이라면 insertAdjacentHTML을 사용하라는 문구를 읽고 이 글을 작성하게 되었다. 이 글을 읽고 많은 분들이 상황에 맞는 DOM 메소드를 사용하시길 바란다!

또한 아래에 다양한 예시를 나름 써봤지만, 읽다가 궁금한 부분이 생기신다면 개발자 도구를 열고 아무 HTML 요소에게 함수를 직접 사용해보시면서 셀프 실습을 해보셔도 좋을 것 같다.

Element.innerHTML

https://developer.mozilla.org/ko/docs/Web/API/Element/innerHTML

문법

const body = document.querySelector("body")
console.log(body.innerHTML)
body.innerHTML = ""
const foodArray = ["김밥", "방어", "사과", "오렌지"]
const FOOD_TEMPLATE = (food) => '<div class="list_food">'+food+'</div>'
foodArray.forEach(food => body.innerHTML += FOOD_TEMPLATE(food))

동작

.innerHTML은 요소 내에 포함 된 HTML을 가져오고, 문자열처럼 = 연산자나 += 연산자로 해당 내용을 추가하거나 변경할 수 있도록 해준다.
innerHTML이 실행되면 해당 요소 내의 DOM Tree가 초기화되고 대입해준 값으로 대체된다.

첫번째 예제를 실행할 경우, document의 body 부분이 ""으로 대체되었다. 개발자 도구에 첫 예제를 입력한다면 화면 내의 내용이 전부 사라질 것이다.

두번째 예제를 실행할 경우, body 안의 내용이 다음과 같이 변경될 것이다.

<div class="list_food">김밥</div>
<div class="list_food">방어</div>
<div class="list_food">사과</div>
<div class="list_food">오렌지</div>

두번째 예제의 forEach문은 좋은 코드일까?
innerHTML에 값이 할당될 때마다 DOM Tree는 새로 그려진다.
코드 상으로는 += 연산자로 간단하게 표현되었지만, 실제 동작은 그렇지 않을 것이다.

문제의 forEach문의 실제 동작을 풀어서 쓰면 다음과 같을 것이다.

body.innerHTML = '<div class="list_food">김밥</div>'
body.innerHTML = '<div class="list_food">김밥</div><div class="list_food">방어</div>'
body.innerHTML = '<div class="list_food">김밥</div><div class="list_food">방어</div><div class="list_food">사과</div>'
body.innerHTML = '<div class="list_food">김밥</div><div class="list_food">방어</div><div class="list_food">사과</div><div class="list_food">오렌지</div>'

이렇게 innerHTML 값이 여러번 바뀌고, 바뀔 때마다 DOM Tree가 교체된다면 좋은 방법이라고 보기 힘들 것이다.

물론, 위 방법을 보완하기 위하여 다음과 같이 함수를 쓸 수도 있을 것이다.

const foodArray = ["김밥", "방어", "사과", "오렌지"]
const FOOD_TEMPLATE = (food) => '<div class="list_food">'+food+'</div>'
body.innerHTML = foodArray.reduce((acc, cur)=>acc+=FOOD_TEMPLATE(cur), "")

reduce 함수로 HTML을 합친 후 innerHTML에 할당하면 여러번 돔 트리가 교체되는 것을 막을 수 있다.
그러나 새 요소가 첫 렌더링 이후로 추가되어야 하는 상황이라면 다른 해결책이 필요할 것이다.

첫 예제처럼 HTML 요소의 내용을 수정하거나 삭제할 때에는 innerHTML을 사용해야 하겠지만, 두번째 예제처럼 HTML 요소에게 삽입이 일어날 때는 innerHTML 대신 다른 DOM 메서드를 사용할 수 있다.

Element.insertAdjacentHTML

https://developer.mozilla.org/ko/docs/Web/API/Element/insertAdjacentHTML

문법

element.insertAdjacentHTML(position, text);

position : position의 값은 beforebegin, afterbegin, beforeend, afterend만 사용할 수 있으며, 각각 값에 해당하는 위치는 아래 그림에 표시해 두었다.
text : 해당 위치에 삽입될 HTML 요소의 text값

위에 쓰인 예제를 insertAdjacentHTML을 사용해 다시 작성한다면

const foodArray = ["김밥", "방어", "사과", "오렌지"]
const FOOD_TEMPLATE = (food) => '<div class="list_food">'+food+'</div>'
foodArray.forEach(food => body.insertAdjacentHTML("beforeend", FOOD_TEMPLATE(food)))

동작

메소드 이름 그대로 인접한 곳에 HTML을 삽입할 때 사용하는 함수다.
자매품으로 insertAdjacentElement, insertAdjacentText가 있다.

요소 아래의 돔 트리를 싹 갈아엎는 innerHTML과 달리 돔 프라그먼트를 삽입하기만 하는 insertAdjacentHTML 메소드를 사용하면 이미 추가된 요소들을 다시 파싱하고 돔 트리에 넣지 않아도 된다는 큰 이점을 보여준다.

마무리

결론

물론 innerHTML을 사용해야 하는 상황이 있고, 템플릿 대신 노드 자체를 생성하여 appendChild해야 더 좋을 상황도 있을 것이다. 필자는 insertAdjacentHTML을 알지 못하고 innerHTML만 후두리찹찹 쓰던 사람으로써 반성하며 알맞은 DOM 메서드를 써보자는 마음과 동작을 제대로 이해해보자는 결심과 MDN에게 사랑과 관심을 기울일 것을 다짐한다는 의미에서 이 글을 작성하였고, 읽게 되시는 여러분께도 도움이 되길 바란다.

더 읽어보면 좋은 자료

https://w3c.github.io/DOM-Parsing/#insertadjacenthtml()
https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer

profile
뭐라도 더 하자~
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 5월 14일

깊은 통찰력이 느껴지는 글입니다. innerHtml += 과 insertAdjcentHTML의 차이가 궁금하였는데 좋은 정보 얻고 갑니다 !!

답글 달기