본문으로 건너뛰기

HTML Entity Converter

HTML 특수문자를 엔티티로 변환하거나 디코딩하는 도구입니다. XSS 공격 방지, 안전한 데이터 표시, HTML 코드 표시에 필수!

🔒 HTML Encode

Output will appear here...

💡 Common HTML Entities

CharacterNamedDecimalHex
<&lt;&#60;&#x3C;
>&gt;&#62;&#x3E;
&&amp;&#38;&#x26;
"&quot;&#34;&#x22;
'&apos;&#39;&#x27;
©&copy;&#169;&#xA9;
&euro;&#8364;&#x20AC;
🔒 Prevent XSS

Encode user input to prevent cross-site scripting attacks

📝 Display HTML

Show HTML code as text without executing it

🔄 Data Transfer

Safely transmit special characters in URLs and forms

🌐 Internationalization

Handle unicode characters across different systems

HTML 엔티티란?

HTML 엔티티는 HTML에서 특수한 의미를 가진 문자나 표시하기 어려운 문자를 안전하게 표현하는 방법입니다.

3가지 표현 방식

1. Named Entities (이름 엔티티)

&lt;    →  <
&gt; → >
&amp; → &
&quot; → "
&nbsp; → (공백)

2. Decimal Entities (10진수 엔티티)

&#60;   →  <
&#62; → >
&#38; → &
&#34; → "
&#169; → ©

3. Hexadecimal Entities (16진수 엔티티)

&#x3C;  →  <
&#x3E; → >
&#x26; → &
&#x22; → "
&#xA9; → ©

언제 사용하나요?

1. XSS 공격 방지

위험한 코드:

<!-- 사용자 입력을 그대로 표시 -->
<div>사용자 입력: <script>alert('XSS');</script></div>

안전한 코드:

<!-- 인코딩된 입력 -->
<div>사용자 입력: &lt;script&gt;alert('XSS');&lt;/script&gt;</div>

결과:

화면에 표시: <script>alert('XSS');</script>
스크립트 실행되지 않음 ✅

2. HTML 코드 표시

블로그 글에 코드 예시 표시:

<!-- 인코딩 전 -->
<div class="example">Hello World</div>

<!-- 인코딩 후 -->
&lt;div class=&quot;example&quot;&gt;Hello World&lt;/div&gt;

<!-- 화면에 표시됨 -->
<div class="example">Hello World</div>

3. 특수문자 안전 전송

URL 파라미터:

// ❌ 잘못된 예
const url = `https://api.com/search?q=<script>`;

// ✅ 올바른 예
const url = `https://api.com/search?q=&lt;script&gt;`;

4. 다국어 문자 표현

<!-- Unicode 문자를 HTML 엔티티로 -->
안녕하세요 → &#xC548;&#xB155;&#xD558;&#xC138;&#xC694;
© 2024 → &#169; 2024
€ 100 → &#8364; 100

주요 HTML 엔티티

필수 5개

문자NamedDecimalHex설명
<&lt;&#60;&#x3C;Less than
>&gt;&#62;&#x3E;Greater than
&&amp;&#38;&#x26;Ampersand
"&quot;&#34;&#x22;Quote
'&apos;&#39;&#x27;Apostrophe

공백 문자

문자NamedDecimal설명
(공백)&nbsp;&#160;Non-breaking space
&ensp;&#8194;En space (중간 너비)
&emsp;&#8195;Em space (넓은 너비)
&thinsp;&#8201;Thin space (좁은 너비)

기호 및 심볼

문자NamedDecimal설명
©&copy;&#169;Copyright
®&reg;&#174;Registered
&trade;&#8482;Trademark
&euro;&#8364;Euro
£&pound;&#163;Pound
¥&yen;&#165;Yen
§&sect;&#167;Section
&para;&#182;Paragraph

수학 기호

문자NamedDecimal설명
×&times;&#215;곱하기
÷&divide;&#247;나누기
±&plusmn;&#177;Plus-minus
&ne;&#8800;Not equal
&asymp;&#8776;Almost equal
&le;&#8804;Less than or equal
&ge;&#8805;Greater than or equal
&infin;&#8734;Infinity
&sum;&#8721;Summation
&radic;&#8730;Square root

화살표

문자NamedDecimal설명
&larr;&#8592;Left arrow
&rarr;&#8594;Right arrow
&uarr;&#8593;Up arrow
&darr;&#8595;Down arrow
&harr;&#8596;Left-right arrow
&lArr;&#8656;Double left arrow
&rArr;&#8658;Double right arrow

실전 활용 예시

1. 사용자 입력 안전하게 표시

// Node.js/Express 예시
const express = require('express');
const he = require('he');

app.post('/comment', (req, res) => {
const userComment = req.body.comment;

// ❌ 위험: XSS 취약
const html = `<div>${userComment}</div>`;

// ✅ 안전: HTML 엔티티 인코딩
const safeHtml = `<div>${he.encode(userComment)}</div>`;

res.send(safeHtml);
});

예시:

// 사용자 입력
input = '<script>alert("XSS")</script>';

// 인코딩 후
output = '&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;';

// 화면 표시
<script>alert("XSS")</script> (텍스트로 표시, 실행 안 됨)

2. React에서 안전한 HTML 렌더링

import DOMPurify from 'dompurify';
import he from 'he';

// ❌ 위험: dangerouslySetInnerHTML
function UnsafeComponent({ userContent }) {
return <div dangerouslySetInnerHTML={{ __html: userContent }} />;
}

// ✅ 안전: HTML 엔티티 인코딩
function SafeComponent({ userContent }) {
const encoded = he.encode(userContent);
return <div>{encoded}</div>;
}

// ✅ 안전: 새니타이징 + 허용된 태그만
function CleanHTMLComponent({ userContent }) {
const clean = DOMPurify.sanitize(userContent);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

3. 데이터베이스 저장 전략

// 전략 1: 저장 시 인코딩 (권장하지 않음)
function saveComment(comment) {
const encoded = he.encode(comment);
db.insert({ comment: encoded });
}

// 전략 2: 원본 저장, 표시 시 인코딩 (권장)
function saveComment(comment) {
// 원본 그대로 저장
db.insert({ comment: comment });
}

function displayComment(comment) {
// 표시할 때만 인코딩
return he.encode(comment);
}

이유:

원본 저장의 장점:
1. 검색 가능 (인코딩된 텍스트는 검색 어려움)
2. 편집 가능 (사용자가 다시 편집할 때 읽기 쉬움)
3. 다양한 출력 형식 지원 (JSON, XML, PDF 등)
4. 인코딩 방식 변경 가능

4. API 응답 처리

// API에서 받은 데이터 처리
fetch('/api/content')
.then(res => res.json())
.then(data => {
// ✅ HTML 엔티티 디코딩
const decoded = he.decode(data.content);
console.log(decoded);

// 화면에 표시할 때는 다시 인코딩
document.getElementById('content').textContent = decoded;
});

5. 이메일 템플릿

<!-- 이메일 HTML 템플릿 -->
<html>
<body>
<h1>주문 확인</h1>
<p>고객명: ${customerName}</p>
<p>주문 내용: ${orderDetails}</p>
</body>
</html>
const he = require('he');

function generateEmail(customerName, orderDetails) {
// 사용자 데이터 인코딩
const safeName = he.encode(customerName);
const safeDetails = he.encode(orderDetails);

return `
<html>
<body>
<h1>주문 확인</h1>
<p>고객명: ${safeName}</p>
<p>주문 내용: ${safeDetails}</p>
</body>
</html>
`;
}

// 예시
generateEmail(
'John <script>alert("XSS")</script>',
'Product <img src=x onerror=alert(1)>'
);
// → 안전하게 인코딩됨

6. XML/RSS 피드

function generateRSSFeed(articles) {
const items = articles.map(article => {
// XML 특수문자 인코딩
const title = he.encode(article.title);
const description = he.encode(article.description);

return `
<item>
<title>${title}</title>
<description>${description}</description>
<link>${article.url}</link>
</item>
`;
}).join('');

return `
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
${items}
</channel>
</rss>
`;
}

인코딩 모드 비교

Named Entities (이름)

입력: <div>Hello & goodbye!</div>
출력: &lt;div&gt;Hello &amp; goodbye!&lt;/div&gt;

✅ 장점:
- 가독성 좋음
- 의미 명확
- 널리 지원됨

❌ 단점:
- 모든 문자에 이름이 있는 것은 아님
- 약간 더 긴 길이

Decimal Entities (10진수)

입력: <div>Hello & goodbye!</div>
출력: &#60;div&#62;Hello &#38; goodbye!&#60;/div&#62;

✅ 장점:
- 모든 Unicode 문자 표현 가능
- 수치적으로 명확

❌ 단점:
- 가독성 낮음
- 직관적이지 않음

Hexadecimal Entities (16진수)

입력: <div>Hello & goodbye!</div>
출력: &#x3C;div&#x3E;Hello &#x26; goodbye!&#x3C;/div&#x3E;

✅ 장점:
- 모든 Unicode 문자 표현 가능
- Unicode 코드포인트와 일치

❌ 단점:
- 가독성 낮음
- 16진수 이해 필요

엔코딩 레벨

기본 엔코딩 (권장)

// 필수 문자만 인코딩: < > & " '
he.encode('<div class="test">Hello & goodbye!</div>');
// → &lt;div class=&quot;test&quot;&gt;Hello &amp; goodbye!&lt;/div&gt;

전체 엔코딩

// 모든 ASCII가 아닌 문자도 인코딩
he.encode('<div>Hello 世界!</div>', { encodeEverything: true });
// → &#x3C;div&#x3E;Hello &#x4E16;&#x754C;!&#x3C;/div&#x3E;

선택적 인코딩

// 특정 문자만 인코딩
function encodeOnlyTags(text) {
return text
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}

encodeOnlyTags('<div>Hello & goodbye!</div>');
// → &lt;div&gt;Hello & goodbye!&lt;/div&gt;

XSS 공격 방지

위험한 패턴들

// 1. 직접 HTML 삽입
element.innerHTML = userInput; // ❌ 위험

// 2. 속성값에 삽입
element.setAttribute('title', userInput); // ❌ 위험

// 3. URL에 삽입
window.location = userInput; // ❌ 위험

// 4. eval 사용
eval(userInput); // ❌ 매우 위험

안전한 방법

// 1. textContent 사용 (HTML 해석 안 함)
element.textContent = userInput; // ✅ 안전

// 2. HTML 엔티티 인코딩
const safe = he.encode(userInput);
element.innerHTML = safe; // ✅ 안전

// 3. 새니타이저 사용
const clean = DOMPurify.sanitize(userInput);
element.innerHTML = clean; // ✅ 안전

// 4. Content Security Policy (CSP)
// HTTP 헤더에 추가:
// Content-Security-Policy: default-src 'self'

XSS 공격 예시와 방어

// 공격 예시 1: Script 태그 삽입
const attack1 = '<script>alert("XSS")</script>';
const safe1 = he.encode(attack1);
// → &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// 공격 예시 2: 이벤트 핸들러
const attack2 = '<img src=x onerror="alert(\'XSS\')">';
const safe2 = he.encode(attack2);
// → &lt;img src=x onerror=&quot;alert(&#x27;XSS&#x27;)&quot;&gt;

// 공격 예시 3: JavaScript URL
const attack3 = '<a href="javascript:alert(\'XSS\')">Link</a>';
const safe3 = he.encode(attack3);
// → &lt;a href=&quot;javascript:alert(&#x27;XSS&#x27;)&quot;&gt;Link&lt;/a&gt;

// 공격 예시 4: Data URI
const attack4 = '<iframe src="data:text/html,<script>alert(1)</script>"></iframe>';
const safe4 = he.encode(attack4);
// → 모든 태그와 속성이 인코딩됨

라이브러리 사용법

Node.js (he)

npm install he
const he = require('he');

// 인코딩
const encoded = he.encode('<div>Hello & "World"</div>');
console.log(encoded);
// → &lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;

// 디코딩
const decoded = he.decode('&lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;');
console.log(decoded);
// → <div>Hello & "World"</div>

// 옵션
const encoded2 = he.encode('<div>Hello 世界</div>', {
useNamedReferences: true, // Named 사용
decimal: false, // Decimal 사용 안 함
encodeEverything: true, // 모든 문자 인코딩
allowUnsafeSymbols: false // 안전하지 않은 기호 허용 안 함
});

Python (html)

import html

# 인코딩
encoded = html.escape('<div>Hello & "World"</div>')
print(encoded)
# → &lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;

# 디코딩
decoded = html.unescape('&lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;')
print(decoded)
# → <div>Hello & "World"</div>

PHP

<?php
// 인코딩
$encoded = htmlspecialchars('<div>Hello & "World"</div>', ENT_QUOTES);
echo $encoded;
// → &lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;

// 디코딩
$decoded = html_entity_decode('&lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;');
echo $decoded;
// → <div>Hello & "World"</div>
?>

Java

import org.apache.commons.text.StringEscapeUtils;

// 인코딩
String encoded = StringEscapeUtils.escapeHtml4("<div>Hello & \"World\"</div>");
System.out.println(encoded);
// → &lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;

// 디코딩
String decoded = StringEscapeUtils.unescapeHtml4("&lt;div&gt;Hello &amp; &quot;World&quot;&lt;/div&gt;");
System.out.println(decoded);
// → <div>Hello & "World"</div>

사용 팁

1. 입력과 출력 분리

// ❌ 저장 시 인코딩
function saveUserInput(input) {
const encoded = he.encode(input);
db.save(encoded); // 검색 어려움
}

// ✅ 표시 시 인코딩
function saveUserInput(input) {
db.save(input); // 원본 저장
}

function displayUserInput(input) {
return he.encode(input); // 표시 시 인코딩
}

2. 이중 인코딩 주의

const text = '<div>Hello</div>';

// 첫 번째 인코딩
const encoded1 = he.encode(text);
console.log(encoded1);
// → &lt;div&gt;Hello&lt;/div&gt;

// ❌ 이중 인코딩 (주의!)
const encoded2 = he.encode(encoded1);
console.log(encoded2);
// → &amp;lt;div&amp;gt;Hello&amp;lt;/div&amp;gt;

// 화면 표시
// → &lt;div&gt;Hello&lt;/div&gt; (의도하지 않은 결과)

3. 컨텍스트별 처리

// HTML 본문
const htmlContent = he.encode(userInput);

// HTML 속성
const attrValue = he.encode(userInput, { useNamedReferences: true });

// JavaScript 문자열
const jsString = JSON.stringify(userInput);

// URL 파라미터
const urlParam = encodeURIComponent(userInput);

4. 성능 최적화

// 작은 텍스트: 매번 인코딩
function displaySmallText(text) {
return he.encode(text);
}

// 큰 텍스트: 캐싱
const cache = new Map();

function displayLargeText(text) {
if (cache.has(text)) {
return cache.get(text);
}

const encoded = he.encode(text);
cache.set(text, encoded);
return encoded;
}

트러블슈팅

문제 1: 한글이 깨짐

// ❌ 잘못된 인코딩
const text = '안녕하세요';
const encoded = he.encode(text, { encodeEverything: true });
// → &#xC548;&#xB155;&#xD558;&#xC138;&#xC694;

// ✅ 기본 인코딩 (한글은 그대로)
const encoded = he.encode(text);
// → 안녕하세요

문제 2: Named entity가 표시됨

// 브라우저가 &nbsp; 등을 인식 못하는 경우
// → Decimal이나 Hex 사용

const encoded = he.encode(text, {
useNamedReferences: false,
decimal: true
});

문제 3: 이미 인코딩된 텍스트

// 디코딩 후 다시 인코딩
const text = '&lt;div&gt;Hello&lt;/div&gt;';
const decoded = he.decode(text); // <div>Hello</div>
const reencoded = he.encode(decoded); // 다시 인코딩

관련 도구

추가 리소스

베스트 프랙티스

1. 출력 인코딩 (Output Encoding)

// 모든 사용자 입력은 출력 시 인코딩
function renderUserContent(content) {
return `<div class="user-content">${he.encode(content)}</div>`;
}

2. 입력 검증 (Input Validation)

// 인코딩과 별개로 입력 검증도 수행
function validateAndEncode(input) {
// 1. 입력 검증
if (input.length > 1000) {
throw new Error('Input too long');
}

// 2. 인코딩
return he.encode(input);
}

3. 계층적 보안 (Defense in Depth)

// 여러 보안 계층 적용
function secureSave(userInput) {
// 1. 입력 검증
validateInput(userInput);

// 2. 원본 저장
db.save(userInput);

// 3. 출력 시 인코딩
return he.encode(userInput);

// 4. CSP 헤더 설정 (서버)
// 5. HTTPS 사용
}

4. 프레임워크 활용

// React는 기본적으로 XSS 방지
function Component({ userInput }) {
// ✅ 자동으로 이스케이프됨
return <div>{userInput}</div>;

// ❌ dangerouslySetInnerHTML은 주의
// return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
}