Event 객체 & 고급 이벤트 기법

seungjun.dev·2025년 8월 27일

JavaScript

목록 보기
8/8

🎪 Event 객체

Event

웹 페이지에서 마우스 클릭, 키 입력, 포커스 이동 등 어떤 사건을 발생시키는 것

이벤트 객체는 특정 이벤트가 발생했을 때, 해당 이벤트에 대한 상세 정보가 담긴 정보 꾸러미와 같다.

브라우저는 이벤트가 발생하면 이 정보 꾸러미(객체)를 자동으로 생성해서, 우리가 지정한 이벤트 리스너의 첫 번째 인자로 전달해 준다.

const myButton = document.getElementById("myButton");

myButton.addEventListener("click", (event) => {
  console.log(event);
});

event 객체 안에는 "어떤 종류의 이벤트가", "어디서", "언제", "어떻게" 발생했는지 등 온갖 유용한 정보가 들어있다.

이벤트 객체의 핵심 속성과 메서드

객체 안에는 수많은 정보가 있지만, 실제 개발에서 자주 쓰이는 것들은 아래와 같다.

이벤트 발생 위치 정보

  • event.target: 실제로 이벤트가 시작된 요소
  • event.currentTarget: 이벤트 리스너가 현재 연결되어 있는 요소

이벤트 종류 및 사용자 입력 정보

  • event.type: 발생한 이벤트의 종류 (String)
  • event.key / event.code: 사용자가 누른 키에 대한 정보
  • event.clientX / event.clientY: 브라우저 화면(Viewport) 기준으로 마우스 커서의 X, Y 좌표

이벤트 흐름 제어 메서드

  • event.preventDefault(): 브라우저의 기본 동작을 막는 매우 중요한 메서드
    • 예시 1: <a> 태그를 클릭했을 때 페이지 이동을 막고 다른 동작(e.g. 팝업 띄우기)을 시키고 싶을 때
    • 예시 2: <form> 안에서 submit 버튼을 눌렀을 때, 페이지가 새로고침되는 것을 막고 HTTP 요청을 보내고 싶을 때
const myLink = document.getElementById("myLink");

myLink.addEventListener("click", (event) => {
  event.preventDefault();
  alert("링크의 기본 동작을 막았습니다.");
});
  • event.stopPropagation(): 이벤트가 상위 요소로 전파(버블링)되는 것을 막음
    • 예시: 카드 UI 컴포넌트가 있고, 카드 전체에 클릭 이벤트가, 카드 안의 '삭제' 버튼에도 별도의 클릭 이벤트가 있다고 가정
    • '삭제' 버튼을 눌렀을 때, 카드 전체의 클릭 이벤트까지 실행되면 안되므로 stopPropagation()을 사용해 이벤트 전파를 차단

🚦 고급 이벤트 기법

이벤트 전파 (Propagation)

DOM Tree의 특정 요소에서 이벤트가 발생했을 때, 그 이벤트가 단순히 해당 요소에서 끝나지 않고 DOM 트리를 따라 다른 요소들에게 퍼져나가는 현상

이벤트 전파는 아래 두 가지 단계로 나뉜다.

비유하자면, 연못에 돌을 던졌을 때 돌이 수면까지 내려가는 과정이 캡처링, 돌이 닿은 지점부터 물결이 바깥으로 퍼져나가는 과정이 버블링과 같다.

캡처링

  • 이벤트가 최상위 요소(window)부터 시작하여 이벤트가 발생한 실제 요소(target)까지 내려가는 과정

버블링

  • 이벤트가 실제 발생한 요소(target)부터 시작하여 다시 최상위 요소(window)까지 올라가는 과정
  • addEventListener는 기본적으로 버블링 단계의 이벤트만 감지한다.
    • 세 번째 인자로 { capture: true } 옵션을 주면 캡처링 단계의 이벤트를 감지할 수 있다.

이벤트 위임 (Delegation)

  • 여러 자식 요소의 이벤트를 부모 요소 하나에서 처리하는 기법

  • 버블링과 event.target 속성을 활용하는 것이 핵심이다.

언제 사용할까?

  • 목록(ul, ol)처럼 동적으로 아이템이 추가되거나 삭제될 때
  • 테이블의 각 셀(td)이나 버튼이 많은 UI처럼 비슷한 요소에 동일한 이벤트를 적용해야 할 때

핵심 원리

이벤트는 버블링이라는 특성을 가진다.

특정 요소에서 이벤트가 발생하면, 그 이벤트를 처리한 후 부모 요소로, 또 그 부모 요소로 올라가며 이벤트를 전파한다.

이벤트 위임은 바로 이 버블링을 활용하는 것이다.
부모 요소에 "너의 자식 중 누군가에게 이벤트가 생기면 나에게 알려줘"라고 말해두는 것과 같다.

예시

<ul id="itemList">
  <li>항목 1</li>
  <li>항목 2</li>
  <li>항목 3</li>
  <li>항목 4</li>
</ul>
const itemList = document.getElementById("itemList");

// 부모 요소에 이벤트 리스너를 하나만 답니다.
itemList.addEventListener("click", (event) => {
  // 클릭된 요소가 LI 태그가 맞는지 확인합니다.
  if (event.target && event.target.nodeName === "LI") {
    console.log("클릭된 항목:", event.target.textContent);
    // event.target은 실제 클릭된 <li> 요소를 가리킵니다!
  }
});

lia 태그 각각에 이벤트 리스너를 추가하는 대신, 부모인 ul에 단 하나만 추가한다.
event.target을 통해 어떤 메뉴가 클릭되었는지 정확히 알 수 있다.

여기서 버블링과 연결고리를 지어본다면 아래와 같다.

  1. 사용자가 '항목 2'(<li>)를 클릭한다. '항목 2'가 이벤트의 target이 된다.
  2. '항목 2'에서 click 이벤트는 처리가 끝난 후, 이벤트 버블링 때문에 부모인 <ul>로 전파됨
  3. <ul>에 붙어 있던 이벤트 리스너가 자신의 자식 중 하나에서 클릭 이벤트가 버블링되어 올라왔다며 동작하기 시작
  4. 리스너 함수 안에서 event.target을 확인하면, 비록 리스너는 <ul>에 있지만 target은 최초의 이벤트 발생지인 '항목 2'(<li>)를 정확히 가리키고 있음

결론적으로, 이벤트 버블링이라는 전파 현상이 없다면 자식 요소에서 발생한 이벤트가 부모 요소까지 전달될 수 없다.
그러면 이벤트 위임 패턴은 성립할 수 없게 된다.

디바운싱 (Debouncing)

연속적으로 발생하는 이벤트를 그룹화하여 특정 시간이 지난 후에 마지막 이벤트에 대해서만 콜백 함수를 실행하는 기법

언제 사용할까?

  • 검색창 자동완성 (타이핑이 끝난 후 API 요청)
  • 창 크기 조절(resize) 이벤트 처리 (크기 조절이 끝난 후 레이아웃 계산)
  • 버튼 중복 클릭 방지

예시

<input type="text" id="searchBox" placeholder="검색어를 입력하세요..." />
const searchBox = document.getElementById("searchBox");

let debounceTimer; // 타이머 ID를 저장할 변수

function search(keyword) {
  console.log(`API 요청 실행: ${keyword}`);
}

searchBox.addEventListener("input", function (event) {
  // 1. 기존에 설정된 타이머가 있다면 취소합니다.
  clearTimeout(debounceTimer);

  const keyword = event.target.value;

  // 2. 500ms(0.5초) 후에 search 함수를 실행하도록 새로운 타이머를 설정합니다.
  // 만약 0.5초 안에 새로운 입력이 들어오면, 위에서 타이머가 취소되고 새 타이머가 설정됩니다.
  debounceTimer = setTimeout(() => {
    search(keyword);
  }, 500);
});

쓰로틀링 (Throttling)

이벤트를 일정 시간 간격으로 딱 한 번만 실행하도록 제한하는 기법

언제 사용할까?

  • 스크롤 이벤트에 기반한 애니메이션 (일정 간격으로만 위치 계산)
  • 마우스 이동(mousemove) 이벤트 처리 (일정 간격으로만 커서 위치 추적)

예시

let throttleTimer; // 쓰로틀링 상태를 체크할 변수

function onScroll() {
  console.log("스크롤 이벤트 발생! 하지만 쓰로틀링으로 조절됩니다.");
}

window.addEventListener("scroll", function () {
  // 1. 현재 타이머가 설정되어 있다면(쓰로틀링 중이라면) 아무것도 하지 않고 함수를 종료합니다.
  if (throttleTimer) {
    return;
  }

  // 2. 타이머를 설정하고, 그 안에 실제 실행할 함수를 넣습니다.
  // 이 함수는 즉시 실행됩니다.
  throttleTimer = setTimeout(() => {
    onScroll();
    // 3. 200ms 후에 타이머를 null로 만들어 다음 이벤트가 실행될 수 있게 합니다.
    throttleTimer = null;
  }, 200);
});
profile
Frontend Engineer | Microsoft Student Ambassadors Alumni

0개의 댓글