[번역] innerHTML의 새로운 대안

Saetbyeol·2024년 7월 17일
30

translations.zip

목록 보기
17/19
post-thumbnail

원저자의 허락을 받아 New alternatives to innerHTML을 한국어로 번역한 글입니다. 🙂

브라우저 지원 참고 사항: setHTMLUnsafe모든 브라우저에서 지원됩니다. setHTML은 아직 표준화 작업이 진행 중이며, 현재는 파이어폭스에서 플래그를 활성화해야만 사용할 수 있습니다. getHTML은 버전 125부터 크롬과 엣지에서 지원됩니다.

최근 브라우저에 setHTMLUnsafe 메서드가 새롭게 추가되었습니다. 여기서 안전하지 않다(unsafe)는 것은 innerHTML과 마찬가지로 입력 값 검증(sanitization)을 수행하지 않음을 의미합니다. 이 명칭은 기존 브라우저 API의 네이밍 방식과 차이가 있습니다. 예를 들어, innerHTML을 굳이 innerHTMLUnsafe라고 하지 않았고, evalevalUnsafe()라고 하지 않았습니다. 그렇다고 해서 setHTMLUnsafe은 기존 메서드들보다 더 위험한 건 아닙니다. 오래된 메서드들과 다르게 안전한 메서드(setHTML)와 안전하지 않은 메서드(setHTMLUnsafe)가 있기 때문에 메서드 이름에 'unsafe'를 명시하는 방식을 사용한 것입니다.

Sanitizer API 명세에는 다음과 같은 구절이 있습니다.

"안전한" 메서드는 스크립트를 실행하는 마크업을 생성하지 않습니다. 즉, XSS(Cross-Site Scripting)로부터 안전해야 합니다.

텍스트 <input>과 사용자가 입력한 값에 따라 DOM을 변경하는 자바스크립트 코드를 갖는 HTML form이 있다고 가정해봅시다.

form.addEventListener('submit', function(event) {
    event.preventDefault();
    const markup = `<h2>${input.value}</h2>`
    div.innerHTML = markup;
    });

만약 사용자가 input에 <img src=doesnotexist>와 같이 입력했다면, 해당 자바스크립트 코드는 브라우저에서 작동하게 되며 .setHTMLUnsafe() 메서드도 동일한 위험을 지니고 있습니다.

이 간단한 예에서는 코드가 사용자의 브라우저에서만 실행되고 있습니다. 하지만 이러한 종류의 입력 값이 데이터베이스에 저장되어 다른 사용자에게 동적 콘텐츠로 보여지는 데 사용된다면, 다른 사용자의 브라우저에서 임의의 잠재적 악성 자바스크립트가 실행될 수 있습니다.

setHTML를 사용하면, DOM에 삽입되는 것은 <img src="doesnotexist">뿐입니다. 이미지는 페이지에 삽입되지만 자바스크립트는 제거됩니다.

Sanitizer API는 아직 개발 중이지만, 이러한 배경지식은 setHTMLUnsafe의 이름의 맥락을 이해하는 데 도움이 됩니다.

setHTMLUnsafe

이미 innerHTML이 있고 (다행히도) setHTML가 개발되고 있는데 왜 setHTMLUnsafe가 필요할까요? 바로 선언적 섀도 DOM(declarative shadow DOM) 때문입니다.

HTML <template> 요소는 다음 두 가지 방식으로 사용될 수 있습니다.

  • 렌더링 되지는 않지만 추후 자바스크립트로 사용할 수 있는 HTML 조각(fragment)을 보관합니다.
  • 섀도 DOM을 즉시 생성합니다. <template>shadowrootmode 속성을 포함하는 경우, 섀도 루트에서 해당 요소는 DOM 내의 요소 콘텐츠로 대체됩니다.

innerHTML은 전자의 방식에서는 잘 작동하지만 후자의 방식을 처리하진 못합니다.

const main = document.querySelector('main');
main.innerHTML = `
    <h2>I am in the Light DOM</h2>
    <div>
    <template shadowrootmode="open">
        <style>
        h2 { color: blue; }
        </style>
        <h2>Shadow DOM</h2>
    </template>
    </div>`

innerHTML은 페이지에 <template>를 삽입할 순 있지만 여전히 <template> 요소로 남아있습니다. 이 <template> 요소는 내용도 렌더링되지 않고, shadowrootmode 속성 여부에 관계없이 섀도 DOM으로 변환되지도 않습니다.

setHTML<template>과 그 콘텐츠를 의도적으로 제거합니다.

const main = document.querySelector('main');
main.setHTML(`
     <h2>I am in the Light DOM</h2>
    <div>
    <template shadowrootmode="open">
        <style>
        h2 { color: blue; }
        </style>
        <h2>Shadow DOM</h2>
    </template>
    </div>`);

위 예제에서 main의 콘텐츠는 h2와 빈 div 뿐입니다. template은 "안전하지 않은 노드"로 취급됩니다.

이것이 바로 브라우저가 setHTMLUnsafe 메서드를 추가한 이유입니다. 페이지에 선언적 섀도 DOM을 동적으로 추가하기 위함입니다.

main.setHTMLUnsafe(`
    <h2>I am in the Light DOM</h2>
    <div>
    <template shadowrootmode="open">
        <style>
        h2 { color: blue; }
        </style>
        <h2>Shadow DOM</h2>
    </template>
    </div>
`);

setHTMLUnsafe 메서드를 사용하면 <template>의 콘텐츠는 섀도 DOM 내부에 렌더링됩니다.

getHTML

setHTMLsetHTMLUnsafeinnerHTML을 완전히 대체할 순 없습니다. innerHTML은 HTML을 설정하고(set) 가져올(get) 수 있기 때문입니다. setHTMLsetHTMLUnsafe을 보완하는 함수가 getHTML입니다(안전하지 않은 버전은 없습니다).

const main = document.querySelector('main');
const html = main.getHTML();

기본적으로 getHTML은 섀도 DOM 내에 있는 어떠한 마크업도 반환하지 않지만, 구성을 다르게 하여 제어할 수 있습니다.

const main = document.querySelector('main');
const html = main.getHTML({serializableShadowRoots: true} );

serializableShadowRoots 프로퍼티를 true로 설정하면 직렬화가 가능한 모든 섀도 DOM 트리를 직렬화합니다.

shadowrootserializable 속성을 사용하여 template 요소의 직렬화를 가능하게 만들 수 있습니다.

<template shadowrootmode="open" shadowrootserializable>

이와 비슷하게 자바스크립트 attachShadow 메서드에는 serializable boolean 옵션이 있습니다.

this.attachShadow({ mode: "open", serializable: true });

섀도 루트 배열을 전달하여 특정한 섀도 DOM 트리만 직렬화할 수도 있습니다.

const markup= main.getHTML({
    shadowRoots: [document.querySelector('.example').shadowRoot]
});

배열의 모든 섀도 루트는 직렬화가 가능하다고 명시적으로 표시되어 있지 않더라도 직렬화됩니다.

2개의 댓글

comment-user-thumbnail
2024년 7월 22일

이거 오해 일으킬까봐 답글을 안 달 수가 없는데, 원문도 마찬가지고,
setHTML 에서 쓰이는 HTML Sanitizer API 표준은 중단되었습니다.
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Sanitizer_API
https://developer.chrome.com/blog/sanitizer-api-deprecation
즉, HTML 소독(Sanitize) 기능은 브라우저 자체로 없고 별도로 사용해야 합니다.
변조 가능성이 있어서 신뢰해서도 안되고요.

답글 달기
comment-user-thumbnail
2024년 7월 29일

This is an excellent article. This is, in my opinion, one of the best posts ever written. Your work is excellent and inspiring. Thank you very much try play game purble place https://purble-place.io

답글 달기