반응형

textContent의 근본 개념

textContent노드와 그 자손들의 텍스트 콘텐츠를 순수 텍스트로 표현한 것입니다. HTML 태그는 모두 제거하고 오직 텍스트만 추출하거나 설정하는 속성입니다.

const div = document.createElement('div');
div.innerHTML = '<p>Hello <strong>World</strong>!</p>';

// textContent는 모든 텍스트만 추출
console.log(div.textContent);  // "Hello World!"

// innerHTML은 HTML 구조 포함
console.log(div.innerHTML);  // "<p>Hello <strong>World</strong>!</p>"

주요 사용 케이스

1. 안전한 텍스트 삽입 (XSS 방지)

사용자 입력을 받을 때 가장 중요한 용도입니다:

// ❌ 위험: 사용자가 스크립트를 삽입할 수 있음
userComment.innerHTML = userInput;  // "<script>alert('해킹!')</script>" 실행됨

// ✅ 안전: HTML로 해석되지 않고 순수 텍스트로 표시
userComment.textContent = userInput;  // 스크립트가 문자 그대로 표시됨

2. 텍스트만 추출하기

HTML 구조에서 순수 텍스트만 필요할 때:

// 복잡한 HTML 구조에서 텍스트만 추출
const article = document.querySelector('article');
const plainText = article.textContent;  // 모든 태그 제거, 텍스트만

// 예: 글자 수 세기
const charCount = article.textContent.length;

// 예: 검색 기능
if (article.textContent.includes('JavaScript')) {
    // 해당 article에 'JavaScript' 단어가 포함됨
}

3. 간단한 텍스트 업데이트

HTML 구조가 필요 없이 텍스트만 변경할 때:

// 버튼 텍스트 변경
button.textContent = '로딩중...';

// 에러 메시지 표시
errorDiv.textContent = '입력값이 올바르지 않습니다.';

// 카운터 업데이트
counterSpan.textContent = count.toString();

4. 텍스트 노드 직접 조작

// 텍스트 노드의 내용 변경
const textNode = element.firstChild;  // 첫 번째 텍스트 노드
if (textNode.nodeType === Node.TEXT_NODE) {
    textNode.textContent = '새로운 텍스트';
}

textContent vs innerHTML vs innerText

const div = document.createElement('div');
div.innerHTML = `
    <p>보이는 텍스트</p>
    <p style="display:none">숨겨진 텍스트</p>
`;

// innerHTML: HTML 태그 포함
console.log(div.innerHTML);  // 전체 HTML 구조

// textContent: 모든 텍스트 (숨겨진 것 포함)
console.log(div.textContent);  // "보이는 텍스트숨겨진 텍스트"

// innerText: 실제로 보이는 텍스트만
console.log(div.innerText);  // "보이는 텍스트"

실전 예시: 댓글 시스템

function addComment(userInput) {
    const comment = document.createElement('div');

    // 사용자 입력을 위한 별도 요소 생성
    const textContainer = document.createElement('p');
    textContainer.textContent = userInput;  // XSS 방지

    // 시간 정보 요소 생성
    const time = document.createElement('span');
    time.className = 'comment-time';

    // small 요소도 DOM으로 생성 (더 안전)
    const small = document.createElement('small');
    small.textContent = new Date().toLocaleString();
    time.appendChild(small);

    // 조립
    comment.appendChild(textContainer);
    comment.appendChild(time);
    commentList.appendChild(comment);
}

// 결과 HTML:
// <div>
//     <p>사용자의 댓글 내용</p>
//     <span class="comment-time">
//         <small>2024. 12. 27. 오후 3:30:00</small>
//     </span>
// </div>

핵심은 "HTML 태그 해석이 필요한가?"입니다:

  • 필요 없음 → textContent (더 안전하고 빠름)
  • 필요함 → innerHTML (XSS 위험 주의)

코드 상세 해석

실무에서 자주 쓰는 패턴

function addComment(userInput) {
    const comment = document.createElement('div');
    comment.className = 'comment-item';

    // HTML 템플릿 사용 (신뢰할 수 있는 데이터만)
    comment.innerHTML = `
        <div class="comment-content"></div>
        <span class="comment-meta">
            <small class="comment-time">${new Date().toLocaleString()}</small>
        </span>
    `;

    // 사용자 입력은 textContent로 안전하게 삽입
    comment.querySelector('.comment-content').textContent = userInput;

    commentList.appendChild(comment);
}

XSS 공격 시뮬레이션

// 악의적인 사용자 입력 예시
const maliciousInput = '<img src=x onerror="alert(\'해킹!\')">'; 

// ❌ innerHTML 사용 시 (위험!)
comment.innerHTML = maliciousInput;  
// 결과: 스크립트가 실행되어 alert 창이 뜸!

// ✅ textContent 사용 시 (안전!)
comment.textContent = maliciousInput;  
// 결과: "<img src=x onerror="alert('해킹!')">" 텍스트가 그대로 표시됨

완전한 실전 예시

class CommentSystem {
    constructor(container) {
        this.container = container;
    }

    addComment(userInput, userId) {
        // 입력값 검증
        if (!userInput.trim()) {
            alert('댓글을 입력해주세요');
            return;
        }

        // 댓글 요소 생성
        const comment = document.createElement('article');
        comment.className = 'comment';
        comment.dataset.timestamp = Date.now();

        // 안전한 템플릿 구조
        comment.innerHTML = `
            <div class="comment-header">
                <span class="comment-author"></span>
                <time class="comment-time">${this.formatTime(new Date())}</time>
            </div>
            <div class="comment-body"></div>
            <div class="comment-actions">
                <button class="btn-like">좋아요</button>
                <button class="btn-reply">답글</button>
            </div>
        `;

        // 사용자 입력은 textContent로 안전하게
        comment.querySelector('.comment-author').textContent = userId;
        comment.querySelector('.comment-body').textContent = userInput;

        // 이벤트 리스너 추가
        comment.querySelector('.btn-like').addEventListener('click', () => {
            this.likeComment(comment);
        });

        this.container.appendChild(comment);
    }

    formatTime(date) {
        const now = new Date();
        const diff = now - date;

        if (diff < 60000) return '방금 전';
        if (diff < 3600000) return `${Math.floor(diff/60000)}분 전`;
        if (diff < 86400000) return `${Math.floor(diff/3600000)}시간 전`;
        return date.toLocaleDateString();
    }

    likeComment(comment) {
        comment.classList.toggle('liked');
    }
}

// 사용
const commentSystem = new CommentSystem(document.getElementById('comments'));
commentSystem.addComment('좋은 글 감사합니다!', 'user123');

핵심 원칙:

  • 사용자 입력 → textContent (항상!)
  • 신뢰할 수 있는 구조 → innerHTML (조심스럽게)
  • 두 가지를 섞을 때는 구조를 먼저 만들고, 사용자 데이터를 나중에 삽입
반응형

'JS_개념' 카테고리의 다른 글

childNodes vs children  (0) 2025.10.14
childNodes vs children  (0) 2025.09.19
2-1-5. Node와 Element의 차이에 대해 설명해주세요  (0) 2025.09.19

+ Recent posts