Compit
소개문의요금제개인정보처리방침이용약관환불규정

온드(ONDE) | 대표: 노승현 | 사업자등록번호: 778-22-02215

서울특별시 광진구 긴고랑로 133-1, 401호(중곡동, 백림주택) | 전화: 010-7794-3962

© 2026 Compit. All rights reserved.
COMPIT
챌린지연습장쇼케이스학습블로그랭킹가이드보안기초
챌린지연습장쇼케이스학습블로그랭킹가이드보안기초
홈/블로그/JavaScript DOM 조작 기초 — 웹 페이지를 동적으로 만들기
JavaScript

JavaScript DOM 조작 기초 — 웹 페이지를 동적으로 만들기

요소 선택, 생성, 수정, 삭제와 이벤트 처리까지, 바닐라 JS로 인터랙티브한 웹을 만드는 방법.

2026-03-01·⏱ 12분
JavaScriptDOM이벤트

DOM이란?

DOM(Document Object Model)은 브라우저가 HTML을 읽고 만드는 트리 구조 객체입니다. JavaScript는 이 DOM을 통해 웹 페이지의 내용, 구조, 스타일을 동적으로 변경할 수 있습니다.

HTML 문서의 모든 것 — 태그, 텍스트, 속성 — 이 DOM 노드가 됩니다.

document
└── html
    ├── head
    │   └── title
    └── body
        ├── h1
        └── p

요소 선택하기

querySelector / querySelectorAll

CSS 선택자로 요소를 찾습니다. 가장 많이 쓰는 방법입니다.

// 하나만 선택 (첫 번째 일치)
const title = document.querySelector('h1');
const btn = document.querySelector('.btn-primary');
const nav = document.querySelector('#main-nav');

// 여러 개 선택 (NodeList 반환)
const cards = document.querySelectorAll('.card');
const links = document.querySelectorAll('nav a');

// NodeList 순회
cards.forEach(card => {
  console.log(card.textContent);
});

getElementById / getElementsByClassName

const header = document.getElementById('header');           // 하나
const items = document.getElementsByClassName('item');       // HTMLCollection
const paragraphs = document.getElementsByTagName('p');       // HTMLCollection

팁: querySelector를 쓰면 통일성 있게 코드를 작성할 수 있어서 추천합니다.

내용 변경하기

textContent vs innerHTML

const title = document.querySelector('.title');

// 텍스트만 변경 (안전 — XSS 방지)
title.textContent = '새로운 제목';

// HTML 포함 변경 (주의 — 사용자 입력 넣지 말 것!)
title.innerHTML = '새로운 <strong>제목</strong>';

보안 규칙: 사용자 입력을 innerHTML에 직접 넣으면 XSS(크로스 사이트 스크립팅) 공격에 취약해집니다. 사용자 입력에는 항상 textContent를 사용하세요.

스타일 변경하기

const box = document.querySelector('.box');

// 직접 스타일 변경
box.style.backgroundColor = '#2dd4bf';
box.style.padding = '16px';
box.style.borderRadius = '8px';

// 클래스 토글 (추천)
box.classList.add('active');
box.classList.remove('hidden');
box.classList.toggle('open');     // 있으면 제거, 없으면 추가
box.classList.contains('active'); // true/false

베스트 프랙티스: style 속성보다 classList로 클래스를 추가/제거하는 것이 좋습니다. CSS와 JS의 역할이 분리되어 유지보수가 쉬워집니다.

속성 변경하기

const img = document.querySelector('img');
const link = document.querySelector('a');

// 속성 읽기/쓰기
img.src = 'new-image.png';
img.alt = '새 이미지 설명';
link.href = 'https://compit.kr';

// data 속성
const card = document.querySelector('.card');
card.dataset.id;          // data-id 값 읽기
card.dataset.status = 'complete';  // data-status 설정

// 임의 속성
card.setAttribute('aria-label', '챌린지 카드');
card.getAttribute('aria-label');
card.removeAttribute('disabled');

요소 생성하기

// 새 요소 만들기
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `
  <h3>새 카드</h3>
  <p>설명 텍스트</p>
`;

// DOM에 추가
document.querySelector('.card-grid').appendChild(card);

// 특정 위치에 삽입
const container = document.querySelector('.container');
container.insertBefore(card, container.firstChild);  // 맨 앞에

// insertAdjacentHTML (편리한 방법)
container.insertAdjacentHTML('beforeend', `
  <div class="card">
    <h3>또 다른 카드</h3>
  </div>
`);

insertAdjacentHTML 위치 옵션:

  • 'beforebegin' — 요소 앞
  • 'afterbegin' — 요소 내부 맨 앞
  • 'beforeend' — 요소 내부 맨 뒤
  • 'afterend' — 요소 뒤

요소 삭제하기

const card = document.querySelector('.card');

// 방법 1: remove()
card.remove();

// 방법 2: 부모에서 제거
card.parentElement.removeChild(card);

이벤트 처리

addEventListener

const btn = document.querySelector('.btn');

btn.addEventListener('click', function(event) {
  console.log('클릭됨!');
  console.log(event.target);  // 클릭된 요소
});

// 화살표 함수
btn.addEventListener('click', (e) => {
  e.preventDefault();  // 기본 동작 방지
  // ...
});

자주 쓰는 이벤트

이벤트설명
click클릭
dblclick더블클릭
mouseenter / mouseleave마우스 진입/이탈
keydown / keyup키보드 누름/뗌
input입력 값 변경
submit폼 제출
scroll스크롤
DOMContentLoadedDOM 로딩 완료

이벤트 위임 (Event Delegation)

자식 요소가 많을 때 각각에 이벤트를 붙이지 않고, 부모에 하나만 붙이는 패턴입니다.

// 비효율: 각 카드마다 이벤트 추가
document.querySelectorAll('.card').forEach(card => {
  card.addEventListener('click', handleClick);
});

// 효율: 부모에 하나만
document.querySelector('.card-grid').addEventListener('click', (e) => {
  const card = e.target.closest('.card');
  if (!card) return;

  console.log('카드 클릭:', card.dataset.id);
});

closest()는 자기 자신부터 부모를 거슬러 올라가며 선택자와 일치하는 요소를 찾습니다.

실전 예제: 할 일 목록

<div class="todo-app">
  <form class="todo-form">
    <input type="text" class="todo-input" placeholder="할 일 입력">
    <button type="submit">추가</button>
  </form>
  <ul class="todo-list"></ul>
</div>
const form = document.querySelector('.todo-form');
const input = document.querySelector('.todo-input');
const list = document.querySelector('.todo-list');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const text = input.value.trim();
  if (!text) return;

  const li = document.createElement('li');
  li.className = 'todo-item';
  li.innerHTML = `
    <span>${text}</span>
    <button class="delete-btn">삭제</button>
  `;
  list.appendChild(li);
  input.value = '';
  input.focus();
});

// 이벤트 위임으로 삭제 처리
list.addEventListener('click', (e) => {
  if (e.target.classList.contains('delete-btn')) {
    e.target.closest('.todo-item').remove();
  }
});

정리

DOM 조작 핵심:

  1. 선택: querySelector / querySelectorAll
  2. 내용 변경: textContent (안전) / innerHTML (주의)
  3. 스타일: classList로 클래스 제어
  4. 속성: dataset, setAttribute
  5. 생성/삭제: createElement, remove()
  6. 이벤트: addEventListener + 이벤트 위임

React, Vue 같은 프레임워크도 내부적으로 DOM을 조작합니다. 기초를 이해하면 프레임워크 학습도 빨라집니다.

관련 챌린지로 연습하기

기본 카드
Lv.1 · 카드
프로필 카드
Lv.2 · 카드
상품 카드
Lv.2 · 카드
← 블로그 목록으로