HTML 엔티티 인코더/디코더

HTML 특수문자를 엔티티로 인코딩·디코딩

텍스트HTML 엔티티

HTML 엔티티 인코더/디코더 소개

인코딩은 & < > " ' 다섯 글자를 &amp; &lt; &gt; &quot; &#39;로 바꾸고, ©·€·— 같은 자주 쓰는 기호 22종은 이름 있는 엔티티로, 그 밖의 ASCII 밖 글자(한글, 이모지 등)는 &#54620; 같은 10진수 숫자 엔티티로 바꿉니다. 디코딩은 브라우저와 같은 규칙을 따릅니다. HTML 표준의 이름 2,125개 전체와 10진수·16진수 숫자 엔티티를 되돌리고, 표에 없는 이름은 원문 그대로 둡니다.

HTML 엔티티를 쓰는 이유는 두 가지입니다. 하나는 문법 문자(<, &)를 글자로 보여 주기 위해서, 다른 하나는 사용자 입력이 태그나 스크립트로 해석되지 않게 막기 위해서입니다. 두 번째는 보안 문제라서, 어느 글자를 어디서 바꿔야 하는지 정확히 아는 것이 중요합니다.

XSS를 막는 데 실제로 중요한 글자

OWASP의 XSS 방지 가이드는 출력 위치(컨텍스트)에 따라 필요한 처리가 다르다고 설명합니다. 태그 사이 본문에 넣을 때는 <와 &만 바꿔도 새 태그가 열리지 않습니다. 따옴표로 감싼 속성값 안이라면 그 따옴표(" 또는 ')와 &가 핵심입니다. 따옴표를 빼먹으면 이름="값" 뒤에 onmouseover=... 같은 새 속성을 끼워 넣을 수 있습니다. 이 도구는 다섯 글자를 모두 바꾸므로 본문과 따옴표로 감싼 속성 양쪽에서 안전한 출력을 만듭니다.

반대로 엔티티 인코딩으로 막을 수 없는 곳이 있습니다.

  • 따옴표 없는 속성값: <div class=값> 에서는 공백, /, = 같은 글자로도 속성을 끊을 수 있습니다. 속성은 항상 따옴표로 감싸세요.
  • <script>와 <style> 안: 이 요소의 내용은 엔티티를 해석하지 않는 원시 텍스트입니다. &lt;는 그대로 &lt;라는 네 글자로 남고, 막아야 할 것은 </script> 문자열입니다. JavaScript 값으로 넣을 때는 JSON 직렬화와 \u003c 이스케이프를 씁니다.
  • href, src 속성의 URL: javascript:alert(1)에는 바꿀 글자가 하나도 없습니다. 게다가 속성값의 엔티티는 브라우저가 먼저 풀어 버리므로 javascript&#58;처럼 숨겨도 실행됩니다. URL은 https: 같은 허용 스킴인지 따로 검사해야 합니다.

&#39;와 &apos;

작은따옴표에 &#39;를 쓰는 이유는 역사적입니다. &apos;는 XML에는 처음부터 있었지만 HTML 4에는 없어서 오래된 브라우저가 알아보지 못했습니다. 지금의 HTML 표준(WHATWG)에는 &apos;가 있어 둘 다 됩니다. 이메일 본문처럼 오래된 렌더러를 거칠 수 있는 곳이라면 &#39;가 안전합니다.

디코딩이 브라우저 규칙을 따르는 부분

HTML 표준의 이름 있는 문자 참조는 2,125개이고, 그중 &amp, &lt, &copy, &eacute 등 106개는 옛 문서와의 호환 때문에 세미콜론이 없어도 풀립니다. 이 도구도 같은 표를 써서 &hearts;, &eacute;, 세미콜론 없는 &copy를 모두 되돌립니다. 세미콜론이 없으면 표에서 가장 긴 옛 이름만 바꾸므로 &notit;은 ¬it;가 됩니다. 단, 브라우저는 속성값 안에서는 &copy=처럼 뒤에 = 이나 영숫자가 붙은 경우 풀지 않는데, 이 도구는 항상 본문 텍스트 규칙으로 풉니다.

숫자 참조도 표준대로 처리합니다. &#128;~&#159;는 옛 문서가 뜻한 Windows-1252 글자로 바뀌어 &#150;은 –(en dash, U+2013), &#146;은 ’가 됩니다. &#0;, 서로게이트 값(&#xD800;), U+10FFFF를 넘는 값은 �(U+FFFD)가 됩니다. 숫자 참조 뒤의 세미콜론도 브라우저처럼 생략할 수 있습니다.

&amp;amp;가 보인다면: 이중 인코딩

게시판 제목에 「Tom &amp; Jerry」가 그대로 보이거나 &amp;amp;가 쌓이는 현상은 인코딩을 두 번 해서 생깁니다. 서버 템플릿이 자동으로 이스케이프하는데 저장할 때 또 인코딩했거나, 이미 인코딩된 값을 innerHTML이 아닌 textContent로 넣은 경우입니다. 원칙은 「저장은 원문으로, 출력할 때 한 번」입니다. 데이터가 이미 망가졌다면 이 도구로 디코딩을 한 번씩 반복해 원문을 확인할 수 있습니다.

한글을 숫자 엔티티로 바꿀 필요가 있을까

페이지가 UTF-8로 선언되어 있다면 한글이나 이모지를 엔티티로 바꿀 이유는 없습니다. 한 글자가 UTF-8로 3바이트인데 &#54620;은 8바이트라 파일만 커집니다. 문자 집합을 통제할 수 없는 곳, 예를 들어 인코딩 선언이 없는 HTML 조각을 다른 시스템에 붙여 넣어야 할 때에만 쓸모가 있습니다.

예시

댓글 입력을 안전하게 출력

입력
<img src=x onerror=alert(1)>
결과
&lt;img src=x onerror=alert(1)&gt;

<가 바뀌어 태그가 열리지 않고 글자로 보입니다. 본문 컨텍스트에서는 이것으로 충분합니다.

속성값 탈출 시도

입력
" onfocus="alert(1)
결과
&quot; onfocus=&quot;alert(1)

value="..." 안에 넣어도 따옴표가 닫히지 않습니다. 속성을 따옴표로 감쌌을 때만 성립합니다.

한글과 기호 섞인 문장

입력
한 © 2026 — ok
결과
&#54620; &copy; 2026 &mdash; ok

한(U+D55C)은 10진수 54620. ©와 —는 이름 있는 엔티티로 바뀝니다.

Windows-1252 숫자 참조

입력
It&#146;s
결과
It’s

&#128;~&#159; 구간은 Windows-1252로 읽습니다. 숫자 그대로 풀면 U+0092라는 보이지 않는 제어 문자가 됩니다.

사용 방법

  1. 텍스트를 붙여 넣습니다.
  2. 인코딩 또는 디코딩을 고릅니다.
  3. 결과를 복사해 HTML이나 코드에 넣습니다.

자주 묻는 질문

XSS를 막으려면 이것만으로 충분한가요?

태그 사이 본문과 따옴표로 감싼 속성값에서는 충분합니다. 스크립트 블록, URL 속성, 따옴표 없는 속성, CSS 안은 따로 처리해야 합니다. 가능하면 자동 이스케이프하는 템플릿 엔진과 textContent를 쓰세요.

&nbsp;는 일반 공백과 무엇이 다른가요?

U+00A0로, 이 자리에서는 줄을 바꾸지 않고 연속으로 써도 합쳐지지 않습니다. 복사하면 일반 공백이 아니라서 검색이나 비교가 어긋날 수 있습니다.

&hearts 처럼 세미콜론을 빼면 왜 안 풀리나요?

세미콜론 없이 풀리는 것은 HTML 표준이 정한 옛 이름 106개(&amp, &copy, &eacute 등)뿐이고 &hearts는 여기에 없습니다. 브라우저도 똑같이 원문으로 둡니다. &hearts;처럼 세미콜론을 붙이세요.

10진수와 16진수 중 무엇을 써야 하나요?

결과는 같습니다. 유니코드 표기(U+2665)와 맞추기 쉬운 &#x2665;를 많이 씁니다. 이 도구는 인코딩 시 10진수를 씁니다.

관련 가이드

출처

기준일:

개인정보와 저장 방식