JavaScript / innerText와 innerHTML 차이와 사용법

innerText는 화면에 렌더링되는 텍스트를, innerHTML은 요소 안의 HTML 마크업을 읽거나 바꾸는 속성입니다. 일반 문자열을 화면에 표시하려면 HTML을 해석하지 않는 textContent를 먼저 검토하세요. 세 속성은 값을 대입하면 기존 자식 내용을 교체하므로, 태그를 유지하려는 작업인지 텍스트만 바꾸려는 작업인지 구분해야 합니다.

읽기와 쓰기 문법

const element = document.getElementById("message");

if (element !== null) {
  const visibleText = element.innerText;
  const markup = element.innerHTML;
  element.innerText = "새로운 내용";
}

이들은 메서드가 아니라 속성이므로 innerText()처럼 괄호를 붙여 호출하지 않습니다. getElementById()로 찾는 ID가 없으면 null이 반환되므로, 요소를 찾았는지 확인하고 속성에 접근합니다. 스크립트가 요소보다 먼저 실행되는 문제도 같은 오류를 일으킬 수 있습니다.

DOM(Document Object Model, 문서 객체 모델)은 브라우저가 문서를 요소와 텍스트 노드로 표현한 구조입니다. 여기서 HTML을 읽는다는 것은 원본 파일의 문자열을 그대로 가져온다는 뜻이 아니라, 현재 DOM의 자식 내용을 HTML 문자열로 직렬화한다는 뜻입니다. 브라우저가 태그나 속성 표현을 정규화할 수 있으므로 원본 소스와 글자 단위로 같다고 가정하지 마세요.

innerText·textContent·innerHTML 선택 기준

속성 읽을 때 쓸 때 주로 사용할 상황
innerText 렌더링 상태와 줄바꿈 등을 반영한 텍스트 문자열로 교체하며 줄바꿈을 br 요소로 표현 사용자에게 보이는 텍스트를 읽을 때
textContent 숨겨진 자손을 포함한 텍스트 내용 HTML을 해석하지 않는 텍스트로 교체 일반 문자열이나 계산 결과를 표시할 때
innerHTML 자식 요소의 HTML 마크업 문자열을 HTML로 해석해 자식 요소를 교체 출처와 내용을 통제한 마크업을 넣을 때

렌더링은 CSS와 문서 구조를 반영해 화면을 만드는 과정입니다. 표시 중인 요소의 숨겨진 자손은 보통 innerText 결과에서 빠지지만, textContent에는 포함됩니다. 다만 읽는 요소 자체가 숨겨졌거나 문서에 연결되지 않아 렌더링되지 않는 경우에는 innerText가 textContent와 같은 내용을 반환할 수 있습니다. 자세한 적용 조건은 MDN의 innerText 설명을 참고하세요.

innerText는 스타일에 따른 결과가 필요하므로 값을 읽을 때 최신 레이아웃 계산을 유발할 수 있습니다. 반복해서 많은 요소의 문자열을 모으는 작업에서는 화면 표시 여부가 꼭 필요한지 판단하세요. 또한 textContent는 script나 style 안의 텍스트도 읽을 수 있으므로, 문서 전체에서 눈에 보이는 글자만 추출하는 기능과는 다릅니다.

같은 문자열로 결과를 비교하는 전체 예제

아래 전체 코드를 UTF-8로 저장한 text-html.html 파일에 넣고 브라우저로 여세요. 별도 라이브러리나 서버는 필요하지 않습니다. 처음에는 세 속성으로 읽은 결과를 보여 주고, 버튼을 누르면 아래 출력 영역을 바꿉니다. 결과 문자열을 출력하는 pre에는 textContent를 사용해 HTML 자체가 실행되지 않게 했습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>텍스트와 HTML 비교</title>
</head>
<body>
<h1>텍스트와 HTML 비교</h1>
<div id="source">Hello <strong>HTML</strong><span hidden>숨긴 문장</span><br>두 번째 줄</div>
<pre id="read-result"></pre>
<p>아래 버튼은 같은 문자열을 서로 다른 방식으로 넣습니다.</p>
<button id="as-text" type="button">텍스트로 넣기</button>
<button id="as-html" type="button">HTML로 넣기</button>
<button id="restore" type="button">초기 상태로 복원</button>
<div id="target">초기 내용</div>
<script>
const source = document.getElementById("source");
const output = document.getElementById("read-result");
const target = document.getElementById("target");
output.textContent = [
  `innerText: ${JSON.stringify(source.innerText)}`,
  `textContent: ${JSON.stringify(source.textContent)}`,
  `innerHTML: ${source.innerHTML}`
].join("\n");

// 개발자가 직접 정한 고정 문자열입니다. 외부 입력을 받지 않습니다.
const markup = "<em>강조</em>\n두 번째 줄";
document.getElementById("as-text").addEventListener("click", () => {
  target.innerText = markup;
});
document.getElementById("as-html").addEventListener("click", () => {
  target.innerHTML = markup;
});
document.getElementById("restore").addEventListener("click", () => {
  target.textContent = "초기 내용";
});
</script>
</body>
</html>

처음 출력에서 innerText에는 Hello HTML과 두 번째 줄이 줄바꿈으로 구분됩니다. textContent에는 숨긴 문장도 포함되지만 br 자체는 텍스트가 아니므로 줄바꿈 문자를 만들지 않습니다. innerHTML에는 strong, span, br 마크업이 포함됩니다. JSON.stringify()는 줄바꿈을 \n으로 보여 주어 차이를 확인하기 위한 것입니다.

‘텍스트로 넣기’를 누르면 <em>강조</em>라는 태그 표현이 그대로 보이고 다음 문장은 새 줄에 표시됩니다. ‘HTML로 넣기’를 누르면 em이 요소로 만들어져 ‘강조’에 의미와 기본 스타일이 적용됩니다. 이때 문자열의 단순 줄바꿈이 반드시 시각적인 새 줄이 되는 것은 아닙니다. HTML의 기본 공백 처리와 innerText 대입의 줄바꿈 처리가 다르기 때문입니다.

비교할 때는 문자열을 바꾸지 말고 버튼만 바꾸세요. ‘초기 상태로 복원’을 눌러 원래 내용으로 돌릴 수 있으며, 파일을 수정했다면 저장 후 새로고침하세요. 결과가 안 바뀌면 개발자 도구의 Console에서 오류를 확인하고, 파일이 .html.txt로 저장되지 않았는지 점검합니다.

외부 입력을 innerHTML에 그대로 넣지 않기

폼 입력값, URL의 매개변수, 서버 응답 등 신뢰할 수 없는 문자열을 innerHTML에 바로 넣으면 교차 사이트 스크립팅(XSS, Cross-Site Scripting) 취약점이 생길 수 있습니다. 단순히 script 태그만 제거한다고 안전해지지 않으며, 다른 HTML 기능이나 이벤트 처리기 등을 통해 코드가 실행될 수도 있습니다. 위 예제는 개발자가 작성한 고정 문자열만 HTML로 넣습니다.

const name = "<em>사용자 이름</em>";
const message = document.getElementById("message");

if (message !== null) {
  message.textContent = name; // 태그로 해석하지 않고 글자로 표시
}

텍스트를 강조하면서 표시하려면 document.createElement("strong")으로 요소를 만들고 그 요소의 textContent에 문자열을 대입하는 방법도 있습니다. 사용자가 작성한 HTML을 허용해야 하는 서비스라면 허용할 마크업, 검증된 정화 도구, 콘텐츠 보안 정책과 Trusted Types 적용 여부 등을 별도로 설계해야 합니다. MDN의 innerHTML 보안 고려 사항은 이 속성을 HTML 삽입 지점으로 설명합니다.

내용을 교체하면 기존 자식과 이벤트도 달라집니다

element.innerHTML += ...는 기존 DOM 뒤에 노드를 하나만 덧붙이는 동작으로 생각하기 쉽지만, 속성 값을 읽어 문자열을 만든 뒤 다시 대입하는 방식입니다. 그 결과 기존 자식 요소가 새 요소로 교체될 수 있고, 이전 자식에 addEventListener()로 등록한 처리기는 새 요소에 이어지지 않습니다. 기존 입력 상태나 자식 객체를 보존해야 한다면 필요한 노드를 직접 추가·수정하세요.

innerText와 textContent도 전체 값을 대입하면 기존 자식 요소를 제거합니다. 예를 들어 아이콘과 span이 함께 있는 버튼의 텍스트만 바꾸려면 버튼 전체 대신 해당 span을 선택해 바꾸는 편이 적합합니다. MDN의 textContent 설명에서 읽기와 쓰기 범위를 확인할 수 있습니다.