[2026] JavaScript DOM 조작 | 웹 페이지 동적으로 제어하기

[2026] JavaScript DOM 조작 | 웹 페이지 동적으로 제어하기

이 글의 핵심

JavaScript DOM 조작: 웹 페이지 동적으로 제어하기. 요소 선택·요소 조작.

들어가며

DOM이란?

DOM (Document Object Model)은 HTML 문서를 트리 구조의 객체로 옮겨 둔 것입니다. 브라우저가 문서를 그리기 위한 내부 표현이며, JavaScript는 이 트리의 노드를 읽고 바꿔 화면과 동작을 갱신합니다. 아래 코드는 html를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<!DOCTYPE html>
<html>
  <head>
    <title>제목</title>
  </head>
  <body>
    <h1 id="title">안녕하세요</h1>
    <p class="content">내용</p>
  </body>
</html>

DOM 트리: 아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

document
  └─ html
      ├─ head
      │   └─ title
      └─ body
          ├─ h1#title
          └─ p.content

DOM 트리 구조 조금 더 보기

  • document: 트리의 진입점입니다. document.documentElement<html>, document.body<body>입니다.
  • 노드(Node): 요소뿐 아니라 텍스트·주석도 노드입니다. 예: <p>안녕</p>에서 "안녕"텍스트 노드입니다.
  • 부모·자식·형제: 각 노드는 parentElement, children, nextElementSibling 등으로 트리 상의 위치를 따라갈 수 있습니다(아래 «기타 선택 메서드» 절 참고).
  • 렌더 트리와의 관계: 브라우저는 HTML을 파싱해 DOM을 만들고, CSS와 함께 화면에 그립니다. DOM을 바꾸면(텍스트·속성·자식 추가/삭제) 화면이 갱신됩니다. 실무에서는 개발자 도구 Elements 패널에서 트리를 펼치며 구조를 확인하는 습관이 중요합니다.

1. 요소 선택

getElementById

// ID로 선택 (가장 빠름)
const title = document.getElementById("title");
console.log(title.textContent);  // 안녕하세요

querySelector / querySelectorAll

다음은 javascript를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// CSS 선택자 사용 (첫 번째 요소)
const title = document.querySelector("#title");
const content = document.querySelector(".content");
const firstP = document.querySelector("p");
// 모든 요소 선택
const allPs = document.querySelectorAll("p");
console.log(allPs.length);  // p 태그 개수
// 복잡한 선택자
const link = document.querySelector("div.container > a.link");
const items = document.querySelectorAll("ul li:nth-child(odd)");
// NodeList 순회
allPs.forEach(p => {
    console.log(p.textContent);
});

기타 선택 메서드

다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 클래스로 선택
const elements = document.getElementsByClassName("content");
// 태그로 선택
const paragraphs = document.getElementsByTagName("p");
// 자식 요소
const parent = document.getElementById("parent");
const children = parent.children;  // HTMLCollection
const firstChild = parent.firstElementChild;
const lastChild = parent.lastElementChild;
// 형제 요소
const element = document.getElementById("myElement");
const next = element.nextElementSibling;
const prev = element.previousElementSibling;
// 부모 요소
const parent = element.parentElement;

2. 요소 조작

텍스트 변경

아래 코드는 javascript를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const title = document.getElementById("title");
// textContent: 순수 텍스트
title.textContent = "새로운 제목";
// innerHTML: HTML 포함 (XSS 주의!)
title.innerHTML = "새로운 <strong>제목</strong>";
// innerText: 화면에 보이는 텍스트 (스타일 고려)
title.innerText = "제목";

innerHTML vs textContent 실무 가이드

구분textContentinnerHTML
내용순수 텍스트만HTML 문자열을 파싱해 DOM에 반영
XSS사용자 입력을 넣어도 태그로 실행되지 않음신뢰할 수 없는 문자열을 넣으면 스크립트 삽입 위험
성능대체로 단순·안전HTML 파싱 비용 + 보안 이슈
규칙: 사용자 입력이나 API 응답을 화면에 넣을 때는 textContent를 기본으로 하고, 정말 HTML이 필요하면 먼저 이스케이프하거나 DOMPurify 같은 라이브러리를 검토합니다. 리스트를 동적으로 만들 때는 createElement + appendChildinnerHTML 문자열 조립보다 추적이 쉬운 경우가 많습니다.
아래 코드는 javascript를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ✅ 안전: 텍스트만 표시
el.textContent = userInput;
// ⚠️ 위험: userInput에 <script>가 들어갈 수 있음
el.innerHTML = userInput;

속성 조작

다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const link = document.querySelector("a");
// 속성 가져오기
console.log(link.getAttribute("href"));
// 속성 설정
link.setAttribute("href", "https://google.com");
link.setAttribute("target", "_blank");
// 속성 제거
link.removeAttribute("target");
// 직접 접근
link.href = "https://google.com";
link.id = "myLink";
link.className = "link active";
// classList (클래스 조작)
link.classList.add("highlight");
link.classList.remove("active");
link.classList.toggle("selected");  // 있으면 제거, 없으면 추가
console.log(link.classList.contains("highlight"));  // true

스타일 변경

다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const box = document.getElementById("box");
// 인라인 스타일
box.style.color = "red";
box.style.backgroundColor = "yellow";
box.style.fontSize = "20px";
// 여러 스타일 한 번에
Object.assign(box.style, {
    color: "blue",
    backgroundColor: "lightgray",
    padding: "10px",
    borderRadius: "5px"
});
// 계산된 스타일 가져오기
const styles = window.getComputedStyle(box);
console.log(styles.color);  // rgb(0, 0, 255)

3. 요소 생성과 삭제

요소 생성

다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 요소 생성
const div = document.createElement("div");
div.textContent = "새 요소";
div.className = "box";
div.id = "newBox";
// 속성 설정
div.setAttribute("data-id", "123");
// 추가
document.body.appendChild(div);  // body 끝에 추가
// 특정 위치에 추가
const container = document.getElementById("container");
const firstChild = container.firstElementChild;
container.insertBefore(div, firstChild);  // 첫 번째 자식 앞에
// insertAdjacentHTML
container.insertAdjacentHTML("beforeend", "<p>새 단락</p>");
// beforebegin: 요소 앞
// afterbegin: 첫 자식 앞
// beforeend: 마지막 자식 뒤
// afterend: 요소 뒤

요소 삭제

다음은 javascript를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const element = document.getElementById("myElement");
// 방법 1: remove()
element.remove();
// 방법 2: removeChild()
const parent = element.parentElement;
parent.removeChild(element);
// 모든 자식 제거
const container = document.getElementById("container");
container.innerHTML = "";  // 간단하지만 이벤트 리스너 제거 안 됨
// 또는
while (container.firstChild) {
    container.removeChild(container.firstChild);
}

요소 복제

아래 코드는 javascript를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

const original = document.getElementById("original");
// 얕은 복제 (자식 제외)
const shallowClone = original.cloneNode(false);
// 깊은 복제 (자식 포함)
const deepClone = original.cloneNode(true);
document.body.appendChild(deepClone);

4. 이벤트 처리

이벤트 리스너

다음은 javascript를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const button = document.getElementById("myButton");
// 이벤트 리스너 추가
button.addEventListener("click", function(event) {
    console.log("클릭됨!");
    console.log("이벤트 타입:", event.type);
    console.log("타겟:", event.target);
});
// 화살표 함수
button.addEventListener("click", (e) => {
    console.log("클릭됨!");
});
// 이벤트 리스너 제거
function handleClick(e) {
    console.log("클릭!");
}
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick);
// 한 번만 실행
button.addEventListener("click", () => {
    console.log("한 번만 실행");
}, { once: true });

이벤트 전파: 캡처링과 버블링

이벤트는 DOM 트리를 따라 두 단계로 전파됩니다.

  1. 캡처링(capturing): window → 대상 요소 방향(위에서 아래로).
  2. 타깃(target): 실제 이벤트가 발생한 요소.
  3. 버블링(bubbling): 대상 요소 → window 방향(아래에서 위로). 대부분의 이벤트는 버블링합니다. addEventListener세 번째 인자로 캡처 단계에서만 실행할지 정합니다. 아래 코드는 javascript를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
const outer = document.getElementById("outer");
const inner = document.getElementById("inner");
outer.addEventListener(
  "click",
  () => console.log("outer 캡처"),
  { capture: true }
);
inner.addEventListener("click", () => console.log("inner 타깃"));
outer.addEventListener("click", () => console.log("outer 버블"));
// inner 클릭 시 로그 순서(대표적): outer 캡처 → inner 타깃 → outer 버블
  • event.target: 실제로 이벤트가 발생한 가장 안쪽 요소.
  • event.currentTarget: 리스너가 붙은 요소(위임 시 부모일 수 있음). 버블링을 막으려면 event.stopPropagation()을 사용합니다(꼭 필요할 때만 — 상위 핸들러까지 막습니다).

주요 이벤트

다음은 javascript를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 마우스 이벤트
// 실행 예제
element.addEventListener("click", e => {});      // 클릭
element.addEventListener("dblclick", e => {});   // 더블클릭
element.addEventListener("mouseenter", e => {}); // 마우스 진입
element.addEventListener("mouseleave", e => {}); // 마우스 이탈
element.addEventListener("mousemove", e => {});  // 마우스 이동
// 키보드 이벤트
input.addEventListener("keydown", e => {
    console.log("키:", e.key);
    console.log("코드:", e.code);
    
    if (e.key === "Enter") {
        console.log("엔터 입력!");
    }
});
input.addEventListener("keyup", e => {});
input.addEventListener("keypress", e => {});  // deprecated
// 폼 이벤트
form.addEventListener("submit", e => {
    e.preventDefault();  // 기본 동작 막기
    console.log("폼 제출");
});
input.addEventListener("input", e => {
    console.log("입력값:", e.target.value);
});
input.addEventListener("change", e => {
    console.log("변경됨:", e.target.value);
});
// 윈도우 이벤트
window.addEventListener("load", () => {
    console.log("페이지 로드 완료");
});
window.addEventListener("resize", () => {
    console.log("창 크기:", window.innerWidth, window.innerHeight);
});
window.addEventListener("scroll", () => {
    console.log("스크롤 위치:", window.scrollY);
});

이벤트 객체

다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

button.addEventListener("click", (event) => {
    // 이벤트 타입
    console.log(event.type);  // click
    
    // 타겟 요소
    console.log(event.target);  // 클릭된 요소
    console.log(event.currentTarget);  // 이벤트 리스너가 등록된 요소
    
    // 마우스 위치
    console.log(event.clientX, event.clientY);  // 뷰포트 기준
    console.log(event.pageX, event.pageY);      // 문서 기준
    
    // 기본 동작 막기
    event.preventDefault();
    
    // 이벤트 전파 중단
    event.stopPropagation();
});

이벤트 위임 (Event Delegation)

다음은 javascript를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 각 요소에 이벤트 등록 (비효율)
const items = document.querySelectorAll(".item");
items.forEach(item => {
    item.addEventListener("click", () => {
        console.log("클릭:", item.textContent);
    });
});
// ✅ 부모에 이벤트 등록 (효율적)
const list = document.getElementById("list");
list.addEventListener("click", (e) => {
    if (e.target.classList.contains("item")) {
        console.log("클릭:", e.target.textContent);
    }
});
// 동적 요소에도 작동
const newItem = document.createElement("li");
newItem.className = "item";
newItem.textContent = "새 항목";
list.appendChild(newItem);  // 자동으로 이벤트 적용됨

5. 실전 예제

예제 1: To-Do 리스트

다음은 html를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<!DOCTYPE html>
<html>
<head>
    <title>To-Do 리스트</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 500px;
            margin: 50px auto;
        }
        .todo-item {
            padding: 10px;
            margin: 5px 0;
            border: 1px solid #ddd;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .completed {
            text-decoration: line-through;
            opacity: 0.6;
        }
        button {
            padding: 5px 10px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>To-Do 리스트</h1>
    
    <input type="text" id="todoInput" placeholder="할 일 입력">
    <button id="addBtn">추가</button>
    
    <div id="todoList"></div>
    
    <script>
        const input = document.getElementById("todoInput");
        const addBtn = document.getElementById("addBtn");
        const todoList = document.getElementById("todoList");
        
        // 할 일 추가
        function addTodo() {
            const text = input.value.trim();
            
            if (!text) {
                alert("할 일을 입력하세요!");
                return;
            }
            
            // 요소 생성
            const todoItem = document.createElement("div");
            todoItem.className = "todo-item";
            
            const span = document.createElement("span");
            span.textContent = text;
            
            const deleteBtn = document.createElement("button");
            deleteBtn.textContent = "삭제";
            
            todoItem.appendChild(span);
            todoItem.appendChild(deleteBtn);
            todoList.appendChild(todoItem);
            
            // 입력창 초기화
            input.value = "";
            input.focus();
        }
        
        // 이벤트 리스너
        addBtn.addEventListener("click", addTodo);
        
        input.addEventListener("keypress", (e) => {
            if (e.key === "Enter") {
                addTodo();
            }
        });
        
        // 이벤트 위임: 완료/삭제
        todoList.addEventListener("click", (e) => {
            const todoItem = e.target.closest(".todo-item");
            
            if (e.target.tagName === "SPAN") {
                // 완료 토글
                todoItem.classList.toggle("completed");
            } else if (e.target.tagName === "BUTTON") {
                // 삭제
                todoItem.remove();
            }
        });
    </script>
</body>
</html>

예제 2: 탭 UI

다음은 html를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<!DOCTYPE html>
<html>
<head>
    <title>탭 UI</title>
    <style>
        .tabs {
            display: flex;
            border-bottom: 2px solid #ddd;
        }
        .tab {
            padding: 10px 20px;
            cursor: pointer;
            border: none;
            background: none;
        }
        .tab.active {
            border-bottom: 3px solid #007bff;
            color: #007bff;
        }
        .tab-content {
            padding: 20px;
            display: none;
        }
        .tab-content.active {
            display: block;
        }
    </style>
</head>
<body>
    <div class="tabs">
        <button class="tab active" data-tab="tab1">탭 1</button>
        <button class="tab" data-tab="tab2">탭 2</button>
        <button class="tab" data-tab="tab3">탭 3</button>
    </div>
    
    <div id="tab1" class="tab-content active">
        <h2>탭 1 내용</h2>
        <p>첫 번째 탭입니다.</p>
    </div>
    
    <div id="tab2" class="tab-content">
        <h2>탭 2 내용</h2>
        <p>두 번째 탭입니다.</p>
    </div>
    
    <div id="tab3" class="tab-content">
        <h2>탭 3 내용</h2>
        <p>세 번째 탭입니다.</p>
    </div>
    
    <script>
        const tabs = document.querySelectorAll(".tab");
        const contents = document.querySelectorAll(".tab-content");
        
        tabs.forEach(tab => {
            tab.addEventListener("click", () => {
                // 모든 탭 비활성화
                tabs.forEach(t => t.classList.remove("active"));
                contents.forEach(c => c.classList.remove("active"));
                
                // 클릭된 탭 활성화
                tab.classList.add("active");
                const targetId = tab.getAttribute("data-tab");
                document.getElementById(targetId).classList.add("active");
            });
        });
    </script>
</body>
</html>

예제 3: 모달 (Modal)

다음은 html를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<!DOCTYPE html>
<html>
<head>
    <title>모달</title>
    <style>
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            justify-content: center;
            align-items: center;
        }
        .modal.active {
            display: flex;
        }
        .modal-content {
            background: white;
            padding: 30px;
            border-radius: 10px;
            max-width: 500px;
        }
        .close {
            float: right;
            cursor: pointer;
            font-size: 24px;
        }
    </style>
</head>
<body>
    <button id="openModal">모달 열기</button>
    
    <div id="modal" class="modal">
        <div class="modal-content">
            <span class="close">&times;</span>
            <h2>모달 제목</h2>
            <p>모달 내용입니다.</p>
        </div>
    </div>
    
    <script>
        const openBtn = document.getElementById("openModal");
        const modal = document.getElementById("modal");
        const closeBtn = document.querySelector(".close");
        
        // 모달 열기
        openBtn.addEventListener("click", () => {
            modal.classList.add("active");
        });
        
        // 모달 닫기
        closeBtn.addEventListener("click", () => {
            modal.classList.remove("active");
        });
        
        // 배경 클릭 시 닫기
        modal.addEventListener("click", (e) => {
            if (e.target === modal) {
                modal.classList.remove("active");
            }
        });
        
        // ESC 키로 닫기
        document.addEventListener("keydown", (e) => {
            if (e.key === "Escape" && modal.classList.contains("active")) {
                modal.classList.remove("active");
            }
        });
    </script>
</body>
</html>

예제 4: 동적 리스트 (추가·삭제 패턴)

입력값으로 <li>를 만들고, 삭제 버튼createElement로 붙이거나 이벤트 위임으로 처리합니다. 아래는 위임만 사용하는 짧은 패턴입니다. 다음은 html를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<ul id="itemList"></ul>
<input type="text" id="itemInput" placeholder="항목">
<button type="button" id="addBtn">추가</button>
<script>
  const list = document.getElementById("itemList");
  const input = document.getElementById("itemInput");
  const addBtn = document.getElementById("addBtn");
  function addItem() {
    const text = input.value.trim();
    if (!text) return;
    const li = document.createElement("li");
    li.textContent = text; // 텍스트만 (HTML 주입 방지)
    const del = document.createElement("button");
    del.type = "button";
    del.textContent = "삭제";
    del.dataset.action = "delete";
    li.appendChild(del);
    list.appendChild(li);
    input.value = "";
  }
  addBtn.addEventListener("click", addItem);
  list.addEventListener("click", (e) => {
    const btn = e.target.closest("button[data-action='delete']");
    if (!btn) return;
    btn.closest("li")?.remove();
  });
</script>

예제 5: 폼 검증 (클라이언트)

HTML5 required, pattern, type=“email” 로 1차 검증을 하고, JavaScript로 메시지 표시·포커스를 보강합니다. 최종 검증은 서버에서 다시 해야 합니다. 다음은 html를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 실행 예제
<form id="signup" novalidate>
  <label>
    이메일
    <input type="email" id="email" name="email" required>
  </label>
  <span id="emailError" class="error" aria-live="polite"></span>
  <label>
    비밀번호 (8자 이상)
    <input type="password" id="password" name="password" minlength="8" required>
  </label>
  <span id="pwError" class="error" aria-live="polite"></span>
  <button type="submit">가입</button>
</form>
<script>
  const form = document.getElementById("signup");
  const email = document.getElementById("email");
  const password = document.getElementById("password");
  const emailError = document.getElementById("emailError");
  const pwError = document.getElementById("pwError");
  function validateEmailField() {
    emailError.textContent = "";
    if (!email.validity.valid) {
      emailError.textContent = email.validationMessage || "이메일 형식을 확인하세요.";
      return false;
    }
    return true;
  }
  function validatePasswordField() {
    pwError.textContent = "";
    if (password.value.length < 8) {
      pwError.textContent = "비밀번호는 8자 이상이어야 합니다.";
      return false;
    }
    return true;
  }
  email.addEventListener("blur", validateEmailField);
  password.addEventListener("blur", validatePasswordField);
  form.addEventListener("submit", (e) => {
    const okEmail = validateEmailField();
    const okPw = validatePasswordField();
    if (!okEmail || !okPw) {
      e.preventDefault();
      return;
    }
    // e.preventDefault(); 후 fetch()로 전송 등
    console.log({ email: email.value });
  });
</script>

6. 폼 처리

폼 이벤트

다음은 html를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<form id="myForm">
    <input type="text" id="username" name="username" required>
    <input type="email" id="email" name="email" required>
    <input type="password" id="password" name="password" required>
    <button type="submit">제출</button>
</form>
<script>
    const form = document.getElementById("myForm");
    
    form.addEventListener("submit", (e) => {
        e.preventDefault();  // 페이지 새로고침 방지
        
        // FormData 사용
        const formData = new FormData(form);
        
        const data = {
            username: formData.get("username"),
            email: formData.get("email"),
            password: formData.get("password")
        };
        
        console.log(data);
        
        // 또는 직접 접근
        const username = document.getElementById("username").value;
        const email = document.getElementById("email").value;
        
        // 유효성 검사
        if (username.length < 3) {
            alert("사용자명은 3자 이상이어야 합니다");
            return;
        }
        
        // API 전송
        fetch("/api/register", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(data)
        })
        .then(response => response.json())
        .then(result => console.log(result))
        .catch(error => console.error(error));
    });
    
    // 실시간 유효성 검사
    const username = document.getElementById("username");
    
    username.addEventListener("input", (e) => {
        const value = e.target.value;
        
        if (value.length < 3) {
            username.style.borderColor = "red";
        } else {
            username.style.borderColor = "green";
        }
    });
</script>

7. 자주 하는 실수와 해결법

실수 1: DOMContentLoaded 전에 접근

아래 코드는 javascript를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ HTML 로드 전 실행
const button = document.getElementById("myButton");  // null!
button.addEventListener("click", () => {});  // TypeError
// ✅ DOMContentLoaded 대기
document.addEventListener("DOMContentLoaded", () => {
    const button = document.getElementById("myButton");
    button.addEventListener("click", () => {
        console.log("클릭!");
    });
});
// ✅ 또는 script를 body 끝에 배치

실수 2: innerHTML로 이벤트 리스너 제거

아래 코드는 javascript를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ innerHTML은 이벤트 리스너 제거
const container = document.getElementById("container");
const button = document.createElement("button");
button.textContent = "클릭";
button.addEventListener("click", () => console.log("클릭!"));
container.appendChild(button);
container.innerHTML = "";  // 이벤트 리스너도 제거됨
// ✅ removeChild 사용
while (container.firstChild) {
    container.removeChild(container.firstChild);
}

실수 3: 이벤트 전파

다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 이벤트 버블링
<div id="parent">
    <button id="child">버튼</button>
</div>
document.getElementById("parent").addEventListener("click", () => {
    console.log("부모 클릭");
});
document.getElementById("child").addEventListener("click", (e) => {
    console.log("자식 클릭");
    // e.stopPropagation();  // 전파 중단
});
// 버튼 클릭 시:
// 자식 클릭
// 부모 클릭 (버블링)

8. 연습 문제

문제 1: 카운터

증가/감소 버튼이 있는 카운터를 만드세요. 다음은 html를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<div id="counter">
    <button id="decrease">-</button>
    <span id="count">0</span>
    <button id="increase">+</button>
</div>
<script>
    let count = 0;
    const countSpan = document.getElementById("count");
    
    document.getElementById("increase").addEventListener("click", () => {
        count++;
        countSpan.textContent = count;
    });
    
    document.getElementById("decrease").addEventListener("click", () => {
        count--;
        countSpan.textContent = count;
    });
</script>

문제 2: 동적 리스트

입력한 항목을 리스트에 추가하고 삭제할 수 있게 하세요. 다음은 html를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

<input type="text" id="itemInput">
<button id="addItem">추가</button>
<ul id="itemList"></ul>
<script>
    const input = document.getElementById("itemInput");
    const addBtn = document.getElementById("addItem");
    const list = document.getElementById("itemList");
    
    function addItem() {
        const text = input.value.trim();
        if (!text) return;
        
        const li = document.createElement("li");
        li.innerHTML = `
            ${text}
            <button class="delete">삭제</button>
        `;
        
        list.appendChild(li);
        input.value = "";
    }
    
    addBtn.addEventListener("click", addItem);
    
    input.addEventListener("keypress", (e) => {
        if (e.key === "Enter") addItem();
    });
    
    // 이벤트 위임
    list.addEventListener("click", (e) => {
        if (e.target.classList.contains("delete")) {
            e.target.parentElement.remove();
        }
    });
</script>

정리

핵심 요약

  1. 요소 선택:
    • getElementById(): ID로 선택
    • querySelector(): CSS 선택자
    • querySelectorAll(): 모든 요소
  2. 요소 조작:
    • 텍스트: textContent(기본), innerHTML(신뢰할 수 있는 입력만)
    • 속성: getAttribute(), setAttribute()
    • 스타일: style, classList
  3. 요소 생성/삭제:
    • 생성: createElement()
    • 추가: appendChild(), insertBefore()
    • 삭제: remove(), removeChild()
  4. 이벤트:
    • 등록: addEventListener()
    • 제거: removeEventListener()
    • 위임: 부모에 이벤트 등록
    • 전파: 캡처링(capture: true)과 버블링 이해
  5. 이벤트 객체:
    • event.target: 이벤트 발생 요소
    • event.preventDefault(): 기본 동작 막기
    • event.stopPropagation(): 전파 중단

베스트 프랙티스

  1. querySelector 우선 사용
  2. ✅ 이벤트 위임 활용
  3. DOMContentLoaded 대기
  4. innerHTML 대신 createElement (보안)
  5. ✅ 이벤트 리스너 정리

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3