DOM/BOM, 이벤트 위임, 버블링 특성과 제어
1. 브라우저 객체 모델과 이벤트 (DOM, BOM, Event)
1) DOM (Document Object Model) 이란?
DOM은 HTML 문서의 구조를 객체로 표현한 것입니다. 브라우저가 HTML 코드를 파싱하여 트리 구조(Tree Structure)의 객체 모델로 만들며, 자바스크립트는 이 DOM을 통해 웹 페이지의 요소들을 추가, 삭제, 수정할 수 있습니다.
2) BOM (Browser Object Model) 이란?
BOM은 웹 브라우저 환경의 다양한 기능들을 객체로 다루는 모델입니다. DOM이 '문서 내부'를 다룬다면, BOM은 '브라우저 창 자체'의 제어를 담당합니다. 자바스크립트의 최상위 객체인 window가 BOM의 핵심이며, 하위에 location, navigator, history, screen 등이 포함됩니다. DOM(window.document) 역시 엄밀히 말하면 BOM의 한 부분입니다.
3) Event와 이벤트 버블링, 이벤트 위임
- Event: 웹 페이지에서 일어나는 모든 동적인 신호(클릭, 키보드 입력, 스크롤 등)를 의미합니다.
- 이벤트 버블링 (Event Bubbling): 특정 화면 요소에서 이벤트가 발생했을 때, 해당 이벤트가 더 상위의 부모 요소들로 전달되어 올라가는 특성입니다. 물속의 거품이 위로 올라가는 모양과 같아 버블링이라 부릅니다.
- 이벤트 위임 (Event Delegation): 하위 요소마다 리스너를 수십 개씩 달지 않고, 버블링을 활용해 상위 부모 요소 하나에만 이벤트 리스너를 장착하여 하위 요소들의 이벤트를 제어하는 설계 패턴입니다.
이벤트 위임 예시 코드
<ul id="parent-list">
<li data-id="1">아이템 1</li>
<li data-id="2">아이템 2</li>
<li data-id="3">아이템 3</li>
</ul>
<script>
const parent = document.getElementById('parent-list');
// 부모 요소에만 이벤트 리스너를 한 번만 등록합니다.
parent.addEventListener('click', (event) => {
// 실제로 클릭된 타겟이 li 요소인지 확인합니다.
if (event.target && event.target.nodeName === 'LI') {
const itemId = event.target.getAttribute('data-id');
console.log(`클릭된 아이템 ID: ${itemId}`);
}
});</script>2. 이벤트 위임 / 방지
1) 이벤트 위임 (Event Delegation)
이벤트 위임은 하위 요소 각각에 이벤트 리스너를 일일이 붙이지 않고, 부모 요소에만 리스너를 달아 하위 요소들의 이벤트를 한꺼번에 제어하는 기법입니다.
이것이 가능한 이유는 브라우저 엔진의 '이벤트 버블링(Event Bubbling)' 메커니즘 때문입니다. 자식 요소에서 발생한 이벤트는 자바스크립트 엔진의 순리에 따라 부모, 조상 요소를 거쳐 최상위 window 객체까지 마치 물방울이 솟구치듯 위로 전파됩니다.
예시
// 부모 요소(ul) 단 하나에만 이벤트 리스너를 바인딩
const menuList = document.querySelector('#menuList');
menuList.addEventListener('click', function(event){
// event.target은 실제 사용자가 클릭한 원시 타깃(li)을 가리킴
if (event.target.tagName === 'LI') {
console.log('클릭된 아이템:', event.target.innerText);
}
});2) 원치 않는 이벤트 위임 방지
부모에게 이벤트를 위임해 두었는데, 정작 특정 자식 요소를 클릭했을 때는 부모의 이벤트 로직이 실행되지 않아야 하거나 브라우저 고유의 기본 동작을 막아야 하는 상황이 있습니다. 이때 브라우저 엔진의 순리를 인위적으로 끊어버리는 제약 조건을 주입하는 것이 이벤트 위임 방지입니다.
e.stopPropagation(): 이벤트가 부모 요소로 더 이상 올라가지 않도록 버블링 체인을 물리적으로 끊어버립니다.e.preventDefault(): HTML 태그 고유의 동작(예:<a>링크 이동,<form>새로고침 제출)을 엔진 레벨에서 차단합니다.
예시
리스트 아이템(<li>)을 클릭하면 상세 페이지로 이동하는 위임 로직이 걸려 있는데, 그 <li> 내부에 있는 '삭제 버튼'을 누를 때는 상세 페이지로 이동하면 안 됩니다. 오직 삭제 로직만 돌고 버블링이 멈춰야 합니다.
// 리스트 내부의 '삭제 버튼' 클릭 이벤트 핸들러
deleteButton.addEventListener('click', function(event){
// 1. 삭제 로직 실행
deleteItem();
// 2. 버블링을 막아 부모인 <li>나 <ul>의 클릭 이벤트가 실행되지 않도록 방지
event.stopPropagation();
});3) 적절한 이벤트 위임과 방지란?
적절한 위임과 방지란, "메모리 효율성(위임)"과 "동작의 독립성(방지)" 사이의 균형을 맞추는 규칙을 세우는 것입니다.
무조건 모든 이벤트를 최상위 body에 위임하면 엔진이 모든 클릭을 검사해야 하므로 하이레벨 영역에서 분기문(조건문)이 지저분해집니다. 반대로 모든 자식에게 이벤트를 다 붙이면 메모리가 낭비됩니다. 구조적으로 연관된 '공통 관심사 그룹' 단위로 위임을 묶고, 그 안에서 독립적으로 튀어야 하는 액션만 정밀하게 타격하여 방지하는 것이 가장 이상적입니다.
예시
하나의 피드 카드 컴포넌트 내부에 '카드 전체 클릭(상세보기)', '좋아요 버튼', '공유 링크'가 섞여 있는 경우의 적절한 설계 예시입니다.
// 1. 피드 컨테이너(부모)에 큰 룰을 위임
const feedContainer = document.querySelector('.feed-container');
feedContainer.addEventListener('click', function(e){
// 공유 링크인 경우 브라우저 기본 이동을 막고 커스텀 팝업을 띄움
if (e.target.closest('.share-link')) {
e.preventDefault(); // 기본 이동 방지
openShareModal();
return;
}
// 좋아요 버튼인 경우 버블링을 방지하여 카드 전체 클릭이 안 되도록 차단
if (e.target.closest('.like-btn')) {
toggleLike();
// 부모의 '상세보기'로 버블링되는 것을 적절히 방지
e.stopPropagation();
return;
}
// 그 외 카드 영역을 클릭하면 상세보기 실행 (자연스러운 버블링 활용)
if (e.target.closest('.feed-card')) {
goToDetail();
}
});3. iframe의 위험성과 이벤트 위임 여부
<iframe>은 현재 웹 페이지 내에 다른 웹 페이지를 임베드하는 태그입니다.
- iframe의 위험성: 악의적인 사이트를 투명하게 겹쳐 클릭을 유도하는 클릭재킹(Clickjacking) 공격이나, 악성 스크립트가 실행되는 XSS(Cross-Site Scripting) 통로가 될 수 있습니다. 브라우저는 이를 막기 위해 크로스 도메인일 경우 서로의 내부에 접근하지 못하도록 동일 출처 정책(SOP)을 엄격히 적용합니다.
- iframe 내부 요소에 대한 이벤트 위임 여부: 불가능합니다.
iframe은 내부적으로 완전히 독립된 전역 객체(window)와 문서 객체(document)를 가집니다. 따라서 부모 페이지의document에 이벤트 위임을 걸어두어도,iframe내부에서 발생한 이벤트는 부모 페이지의 DOM 트리로 버블링되지 않습니다. > 블로그 보충 정보 (iframe 통신 방법): iframe 내부 이벤트를 부모에게 전달하려면, iframe 내부 스크립트에서 이벤트를 감지한 뒤window.parent.postMessage()API를 이용해 부모 창으로 명시적인 메시지를 던져주어야 합니다.
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.