HTML 엔티티는 파서가 잘못 해석할 수 있는 문자를 HTML에서 표현하는 메커니즘입니다. 태그를 시작하는 것으로 잘못 해석되는 꺾쇠 괄호, 다른 엔티티의 시작으로 잘못 해석되는 앰퍼샌드, 인코딩 변환으로 손실될 수 있는 비 ASCII 문자가 대상입니다. 엔티티를 잘못 처리하면 레이아웃 깨짐·문자 깨짐, 최악의 경우 XSS 취약점으로 이어집니다.

HTML 엔티티란

HTML 엔티티는 &로 시작하고 ;로 끝나는 문자열로, 단일 문자를 나타냅니다. 두 가지 형식이 있습니다:

이름 있는 엔티티는 설명적인 키워드를 사용합니다:

&lt;<
&gt;    → >
&amp;   → &
&quot;  → "
&nbsp;  → (줄 바꿈 없는 공백)

숫자 엔티티는 유니코드 코드 포인트를 10진수 또는 16진수로 사용합니다:

&#60;<   (10진수)
&#x3C;<   (16진수)
&#169;  → ©
&#x00A9; → ©

둘 다 동일한 출력을 생성합니다. 이름 있는 엔티티가 더 읽기 쉽고, 숫자 엔티티는 이름이 없는 문자를 포함한 모든 유니코드 문자에 사용할 수 있습니다.

HTML 엔티티가 중요한 이유

HTML 구조 깨짐 방지

<>는 HTML에서 특별한 의미를 갖습니다. 소스 코드 표시 등 리터럴 꺾쇠 괄호를 표시하려면 이스케이프해야 합니다:

<!-- 잘못됨: 브라우저가 불완전한 태그로 파싱 -->
Using <strong> instead of <b> is preferred.

<!-- 올바름 -->
<p>Using &lt;strong&gt; instead of &lt;b&gt; is preferred.</p>

XSS 취약점 방지

사용자가 입력한 콘텐츠를 HTML에 삽입하기 전에 이스케이프하지 않는 것은 가장 일반적인 웹 보안 버그 중 하나입니다. 사용자가 <script>alert(1)</script>를 입력하고 코드가 그것을 그대로 출력하면, 모든 방문자의 브라우저에서 그 스크립트가 실행됩니다.

<!-- 위험: 사용자 입력을 그대로 출력 -->
<p>Hello, <%= username %></p>

<!-- 안전: HTML 인코딩 -->
<p>Hello, <%= htmlEncode(username) %></p>

HTML에 삽입되는 사용자 입력에 대해 항상 다음 5개 문자를 인코딩하세요:

문자엔티티
&&amp;
<&lt;
>&gt;
"&quot;
'&#39;

문서 문자 세트 외부 문자 표시

UTF-8이 보편화되기 전, ISO-8859-1(Latin-1)로 제공된 문서에서는 ©·· 등을 직접 표현할 수 없었고 엔티티가 해결책이었습니다. 오늘날 UTF-8 제공이 표준이지만, 입력하기 어렵거나 텍스트 프로세서에서 제거될 수 있는 문자에는 엔티티가 여전히 유용합니다.

HTML 엔티티 빠른 참조

예약 문자

문자이름엔티티숫자 참조
<소보다 작음&lt;&#60;
>소보다 큼&gt;&#62;
&앰퍼샌드&amp;&#38;
"큰따옴표&quot;&#34;
'작은따옴표&#39;&#x27;

타이포그래피

문자이름엔티티용도
줄 바꿈 없는 공백&nbsp;단어 간 줄 바꿈 방지
엠 대시&mdash;문장 구분, 범위
엔 대시&ndash;숫자 범위(2010–2024)
말줄임표&hellip;텍스트 잘림
"왼쪽 큰따옴표&ldquo;인용
"오른쪽 큰따옴표&rdquo;인용
'아포스트로피&rsquo;단축형

기호

문자이름엔티티
©저작권&copy;
®등록 상표&reg;
상표&trade;
유로&euro;
£파운드&pound;
¥엔/위안&yen;
°&deg;
±플러스마이너스&plusmn;
×곱하기&times;
÷나누기&divide;
오른쪽 화살표&rarr;

수학 기호

문자이름엔티티
이하&le;
이상&ge;
같지 않음&ne;
무한대&infin;
합계&sum;
제곱근&radic;

코드로 인코딩·디코딩하기

JavaScript

브라우저 DOM이 HTML 엔티티 인코딩을 처리합니다:

// 인코딩(HTML 이스케이프)
function htmlEncode(str) {
  const div = document.createElement('div');
  div.appendChild(document.createTextNode(str));
  return div.innerHTML;
}

htmlEncode('<script>alert(1)</script>');
// "&lt;script&gt;alert(1)&lt;/script&gt;"

// 디코딩(HTML 언이스케이프)
function htmlDecode(str) {
  const div = document.createElement('div');
  div.innerHTML = str;
  return div.textContent;
}

htmlDecode('&lt;b&gt;bold&lt;/b&gt;');
// "<b>bold</b>"

Node.js(DOM 없음)에서는 라이브러리를 사용합니다:

npm install he
import he from 'he';

he.encode('<script>alert(1)</script>');
// "&lt;script&gt;alert(1)&lt;&#x2F;script&gt;"

he.decode('&lt;b&gt;bold&lt;/b&gt;');
// "<b>bold</b>"

Python

Python 표준 라이브러리로 일반적인 경우를 처리할 수 있습니다:

import html

# 인코딩
html.escape('<script>alert(1)</script>')
# '&lt;script&gt;alert(1)&lt;/script&gt;'

# 작은따옴표를 포함하여 인코딩
html.escape("it's a test", quote=True)
# 'it&#x27;s a test'

# 디코딩
html.unescape('&lt;b&gt;Hello &amp; World&lt;/b&gt;')
# '<b>Hello & World</b>'

PHP

// HTML 컨텍스트용 인코딩
htmlspecialchars('<script>alert(1)</script>', ENT_QUOTES, 'UTF-8');
// &lt;script&gt;alert(1)&lt;/script&gt;

// 적용 가능한 모든 문자 인코딩
htmlentities('© 2024', ENT_QUOTES, 'UTF-8');
// &copy; 2024

// 디코딩
html_entity_decode('&lt;b&gt;bold&lt;/b&gt;', ENT_QUOTES, 'UTF-8');
// <b>bold</b>

인코딩해야 할 때와 하지 않아도 될 때

항상 인코딩: 신뢰할 수 없는 입력(사용자 이름·검색 쿼리·폼 값·API 데이터)을 HTML에 삽입할 때.

이중 인코딩하지 말 것. 콘텐츠가 이미 인코딩되어 있다면(데이터베이스에 &lt;로 저장된 경우), 다시 인코딩하면 &amp;lt;가 되어 < 대신 리터럴 &lt;로 표시됩니다.

비 ASCII 텍스트에는 엔티티 대신 UTF-8 사용. UTF-8로 제공하고 저장하면 ©··를 엔티티 없이 HTML에 직접 사용할 수 있습니다. 레거시나 제한된 환경에서만 엔티티가 필요합니다.

&nbsp;는 아껴 사용. &nbsp;는 HTML 이메일의 간격 해결책이나 줄 바꿈 방지에 자주 사용되지만, 현대 HTML/CSS에서는 white-space: nowrap이나 word-break 속성이 더 유지보수하기 쉽습니다.

빠른 검색 도구

문자의 엔티티를 찾거나 소스 코드에서 발견한 엔티티를 디코딩하려면 전용 인코더/디코더가 가장 빠릅니다. ZeroTool HTML 엔티티 도구 사용해보기 →

HTML 엔티티가 포함된 텍스트를 붙여 넣어 디코딩하거나, 특수 문자를 입력·붙여 넣어 엔티티 표현을 얻을 수 있습니다. 용도:

  • CMS 내보내기에서 깨진 HTML 디코딩
  • 시각적으로만 알고 있는 기호의 올바른 엔티티 찾기
  • 템플릿 이스케이프가 올바르게 작동하는지 확인

정리

HTML 엔티티는 두 가지 중요한 역할을 합니다. HTML 구조를 깨지 않고 예약 문자를 표시하는 것과, 사용자 입력을 이스케이프하여 XSS를 방지하는 것입니다. 현대 UTF-8 문서에서는 이스케이프에 기본적으로 &lt;·&gt;·&amp;·&quot;·&#39;만 필요하며, 나머지 문자 세트는 직접 삽입할 수 있습니다.

HTML 엔티티를 즉시 인코딩·디코딩하기 →