이벤트 버블링과 캡처링 완전 이해하기

2025. 10. 14. 23:09·Frontend/Javascript Essentials

브라우저에서 이벤트는 단순히 한 요소에서만 끝나지 않는다.

사용자가 어떤 버튼을 클릭하면, 그 클릭 이벤트는 DOM 트리 전체를 따라 전파(propagation) 되며 여러 단계의 흐름을 거친다.

이벤트 전파는 크게 두 가지 흐름으로 나뉜다 — 버블링(Bubbling) 과 캡처링(Capturing).

1. 버블링 (Bubbling)

이벤트 버블링은 가장 안쪽의 요소에서 시작해 바깥쪽 부모 요소로 전파되는 현상이다.

즉, 특정 요소에 이벤트가 발생하면 해당 요소의 핸들러가 실행된 후,

그 이벤트는 부모 → 조상 순으로 계속 전파되며 각각의 핸들러가 차례로 실행된다.

 

이때 이벤트가 시작된 가장 안쪽의 요소를 타깃(target) 이라 하며, event.target으로 접근할 수 있다.

<style>
  body * {
    margin: 10px;
    border: 1px solid blue;
  }
</style>

<form onclick="alert('form')">
  FORM
  <div onclick="alert('div')">
    DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

위 코드에서 <p> 요소를 클릭하면 아래 순서로 이벤트가 전파된다.

  1. <p> 요소의 핸들러 실행
  2. 부모 <div> 핸들러 실행
  3. 그 위의 <form> 핸들러 실행
  4. 마지막으로 document까지 도달

 

이처럼 “아래에서 위로” 올라가는 방식이 버블링 단계다.

이름 그대로, 물속의 거품이 위로 올라가는 모습과 닮았다.

버블링 중단하기

이벤트가 부모로 전파되는 걸 막고 싶다면 다음 메서드를 사용할 수 있다.

  • event.stopPropagation() : 이벤트가 상위 요소로 전파되지 않음
  • event.stopImmediatePropagation() : 같은 요소 내의 다른 이벤트 핸들러도 실행되지 않음
<body onclick="alert('버블링은 여기까지 도달하지 못합니다.')">
  <button onclick="event.stopPropagation()">클릭해 주세요</button>
</body>

하지만 이 방법은 남용을 피하는 것이 좋다.

지금은 필요 없어 보여도, 나중에 상위 요소에서 이벤트를 처리해야 할 상황이 생길 수 있기 때문이다.

2. 캡처링 (Capturing)

이벤트는 사실 버블링 전에 ‘캡처링 단계’ 를 거친다.

표준 DOM 이벤트 모델에서는 이벤트 전파가 아래 순서로 진행된다.

  1. 캡처링 단계 – 이벤트가 상위 요소에서 타깃 요소로 전달되는 단계
  2. 타깃 단계 – 이벤트가 실제 타깃 요소에 도달
  3. 버블링 단계 – 타깃에서 다시 상위 요소로 전파되는 단계

즉, 캡처링은 “위에서 아래로”, 버블링은 “아래에서 위로” 향한다.

1. 캡처링 이벤트 핸들러 등록하기

기본적으로 addEventListener()는 버블링 단계에서만 작동한다.

하지만 아래처럼 옵션을 { capture: true }로 설정하면 캡처링 단계에서 실행할 수 있다.

document.body.addEventListener(
  'click',
  () => console.log('캡처링: body 클릭 감지'),
  { capture: true }
);

이렇게 하면 이벤트가 타깃 요소에 도달하기 전에, 상위 요소(body)가 먼저 이벤트를 “가로채” 처리한다.

 

2. 캡처링이 유용한 상황

1. 이벤트 우선 제어

특정 요소보다 상위 요소가 먼저 이벤트를 감지해야 할 때

 

예: 하위 버튼 클릭 전에 사용자 권한을 확인하거나 로깅할 때

function PermissionExample() {
  const handleClickCapture = (e: React.MouseEvent) => {
    console.log("👮 권한 확인 중...");
    const hasPermission = false;

    if (!hasPermission) {
      e.stopPropagation(); // 하위 버튼 이벤트 실행 방지
      alert("권한이 없습니다!");
    }
  };

  const handleClick = () => {
    console.log("✅ 버튼 동작 수행");
  };

  return (
    <div
      onClickCapture={handleClickCapture}
      className="p-6 border rounded-md bg-gray-50"
    >
      <button onClick={handleClick} className="bg-blue-500 text-white p-2 rounded">
        민감한 동작 실행
      </button>
    </div>
  );
}

 

사용자가 버튼을 클릭하면, 상위 요소의 onClickCapture가 먼저 실행되는데, 

권한이 없으면 이벤트 버블링을 막고, 하위 버튼 로직(handleClick)은 실행되지 않는다.

2. 보안 / 무결성 제어

하위 요소의 동작을 사전에 차단하거나 허용 여부를 판단할 때

function IntegrityExample() {
  const handleCapture = (e: React.MouseEvent) => {
    const isSafe = Math.random() > 0.5; // 임의의 조건
    console.log("🧩 데이터 무결성 검사 중...");

    if (!isSafe) {
      e.stopPropagation();
      alert("데이터 무결성 검증 실패! 실행이 중단되었습니다.");
    }
  };

  const handleClick = () => {
    console.log("데이터 업데이트 실행 중...");
  };

  return (
    <div
      onClickCapture={handleCapture}
      className="p-6 border rounded-md bg-white"
    >
      <button onClick={handleClick} className="bg-green-500 text-white p-2 rounded">
        데이터 업데이트
      </button>
    </div>
  );
}

 

캡처링 단계에서 데이터를 미리 검사하여, 문제가 감지되면 이벤트 전파를 막아 하위 로직이 실행되지 않도록 한다.

3. 이벤트 흐름 분석

이벤트가 어떤 경로로 전파되는지 디버깅할 때 유용(캡처링 단계부터 추적 가능)

function EventFlowExample() {
  const handleOuterCapture = () => console.log("🔵 Outer(Capture)");
  const handleOuter = () => console.log("🔵 Outer(Bubble)");

  const handleInnerCapture = () => console.log("🟢 Inner(Capture)");
  const handleInner = () => console.log("🟢 Inner(Bubble)");

  return (
    <div
      onClickCapture={handleOuterCapture}
      onClick={handleOuter}
      className="p-6 border rounded-md bg-gray-100"
    >
      <button
        onClickCapture={handleInnerCapture}
        onClick={handleInner}
        className="bg-purple-500 text-white p-2 rounded"
      >
        클릭!
      </button>
    </div>
  );
}

 

콘솔 출력 순서 → Outer(Capture) → Inner(Capture) → Inner(Bubble) → Outer(Bubble)

이를 통해 캡처링 → 버블링의 흐름을 한눈에 확인할 수 있다.

4. 복잡한 UI 제어

여러 이벤트가 동시에 발생할 때, 캡처링으로 우선순위 조정이 가능하다.

 

예를 들어, 카드 안의 버튼 클릭 시 카드 클릭 이벤트와 버튼 클릭 이벤트가 중복 실행되는 걸 방지해야 할 때 유용합니다.

function ComplexUIExample() {
  const handleCardCapture = () => {
    console.log("🟩 카드의 캡처링 단계에서 우선 제어");
  };

  const handleCardClick = () => {
    console.log("🟩 카드 클릭 이벤트 (버블 단계)");
  };

  const handleButtonClick = (e: React.MouseEvent) => {
    e.stopPropagation(); // 카드 클릭 방지
    console.log("🟦 버튼 클릭");
  };

  return (
    <div
      onClickCapture={handleCardCapture}
      onClick={handleCardClick}
      className="p-6 border rounded-md bg-gray-50 cursor-pointer"
    >
      <h2 className="font-semibold mb-2">카드 타이틀</h2>
      <button
        onClick={handleButtonClick}
        className="bg-pink-500 text-white p-2 rounded"
      >
        세부 동작
      </button>
    </div>
  );
}

 

 

캡처링 단계에서 상위 카드의 제어 로직이 먼저 작동하여, 하위 버튼이 클릭되더라도 카드의 버블 이벤트(onClick)은 실행되지 않도록 제어할 수 있다.

 

3. event 객체 핵심 정리

프로퍼티 설명
event.target 이벤트가 실제 발생한 요소
event.currentTarget 현재 이벤트 핸들러가 바인딩된 요소
event.eventPhase 이벤트 흐름 단계 (1=캡처링, 2=타깃, 3=버블링)

 

4. 마무리

이벤트 버블링과 캡처링은 단순한 개념 같지만, 이벤트 위임(Event Delegation) 이나 성능 최적화, 보안 제어 등에서도 매우 중요한 개념이다. 버블링은 대부분의 상황에서 자연스럽게 동작하지만, 특정 흐름을 제어해야 할 때는 캡처링이나 stopPropagation() 같은 메서드를 적절히 활용해야 한다.

 

핵심 포인트:

  • 기본은 버블링 중심
  • 캡처링은 특별한 상황에서만 사용
  • 이벤트 흐름을 이해하면, DOM 이벤트를 훨씬 유연하게 제어할 수 있다.

'Frontend > Javascript Essentials' 카테고리의 다른 글

JavaScript 진화사: ES1부터 최신 ES14, CommonJS에서 ESM까지  (0) 2025.11.03
this — 호출 방식에 따라 달라지는 자바스크립트의 핵심 키워드  (0) 2025.10.14
호이스팅(Hoisting)  (0) 2025.10.14
비동기 처리: CallBack → Promise → async/await  (0) 2025.10.13
클로저 — 함수가 변수를 기억하는 방식  (0) 2025.10.12
'Frontend/Javascript Essentials' 카테고리의 다른 글
  • JavaScript 진화사: ES1부터 최신 ES14, CommonJS에서 ESM까지
  • this — 호출 방식에 따라 달라지는 자바스크립트의 핵심 키워드
  • 호이스팅(Hoisting)
  • 비동기 처리: CallBack → Promise → async/await
JTB
JTB
웹/앱 개발 정보를 공유하고 있습니다.
  • JTB
    JTechBlog
    JTB
  • 전체
    오늘
    어제
    • All About Programming;)
      • Other than Tech
        • 잡생각
      • Computer Science
        • Terminology and Concepts
        • Network
        • Operating System
        • Database
        • Data Structure
        • Web Development
        • Interview Prep
      • Frontend
        • Javascript Essentials
        • Perfomance Optimization
        • JS Patterns
        • React
        • Next.js
        • Flutter
        • Testing
      • Backend
        • Node.js
      • DevOps
        • Docker & Kubernetes
      • Coding Test
        • LeetCode
        • Programmers
      • Tech Books & Lectures
        • Javascript_Modern JS Deep d..
        • Network_IT 엔지니어를 위한 네트워크 입문
      • Projects
        • PolyLingo_2025
        • Build Your Body_2024
        • JStargram_2021
        • Covid19 Tracker_2021
        • JPortfolio_2021
        • Nativ_2026
      • BootCamp_Codestates
        • TIL
        • TILookCloser
        • Pre Tech Blog
        • IM Tech Blog
        • Daily Issues and DeBugging
        • First Project
        • Final Project
        • Sprint Review
        • Good to Know
        • Socrative Review
        • HTML &amp; CSS
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 글쓰기
    • 관리
  • 공지사항

  • 인기 글

  • 태그

    VoiceJournal
    스코프
    structure of os
    testing
    프론트엔드 성능 최적화 가이드
    Shared resources
    자바스크립트 딥다이브
    DOM
    자바스크립트
    이벤트
    딥다이브
    How memory manage data
    indie hacker
    leetcode
    Data Structure
    mobile app
    Javascript Essentials
    개인의무한한능력이요구되는세상
    Time complexity and Space complexity
    polylingo
    Network
    모던 자바스크립트 Deep Dive
    database
    TCP/IP
    커리어
    CPU scheduling algorithm
    Operating System
    Threads and Multithreading
    Binary Tree BFS
    js pattern
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
JTB
이벤트 버블링과 캡처링 완전 이해하기
상단으로

티스토리툴바