글 목록

event.target과 event.currentTarget의 차이

2025년 2월 12일 3분 읽기
JavaScriptDOM

최근 면접에서 event.target과 event.currentTarget의 차이를 묻는 질문을 받았습니다. 알고 있다고 생각했습니다. 그런데 막상 답할 때는 둘의 의미를 정반대로 설명하고 말았습니다. 그래서 저와 같은 일을 겪는 분이 없도록 이 글을 남깁니다.

부모에게 이벤트 처리를 맡길 수 있다

둘의 차이는 이벤트 위임(Event Delegation) 예시로 보면 이해하기 쉽습니다.

이벤트 위임은 이벤트 리스너를 부모 요소에 등록해서, 하위 요소에서 발생하는 이벤트를 부모가 감지하고 처리하는 방법입니다. 이게 가능한 이유는 JavaScript의 버블링(Bubbling) 덕분입니다. 자식 요소에서 발생한 이벤트는 DOM 트리를 타고 부모 요소 방향으로 전파되는데, 이 성질을 활용하면 리스너를 일일이 자식 요소마다 등록하지 않아도 됩니다. 반대 방향인 부모에서 자식으로의 전파는 캡처링입니다.

이벤트 위임은 주로 동적으로 생성되는 요소를 관리할 때 유용합니다. 아직 DOM에 없는 요소에는 리스너를 직접 달 수 없으니까요.

html
<ul id="parent">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
<button id="addItem">Add Item</button>

<script>
  const parent = document.getElementById("parent");
  const addItem = document.getElementById("addItem");

  // 이벤트 위임: 부모 요소에서 클릭 이벤트 감지
  parent.addEventListener("click", (event) => {
    if (event.target.tagName === "LI") {
      console.log(`Clicked: ${event.target.textContent}`);
    }
  });

  // 동적으로 리스트 추가
  addItem.addEventListener("click", () => {
    const newItem = document.createElement("li");
    newItem.textContent = `Item ${parent.children.length + 1}`;
    parent.appendChild(newItem);
  });
</script>

하나는 클릭된 요소, 하나는 리스너가 달린 요소

속성의미값
target이벤트가 실제로 발생한 요소클릭된 <li>
currentTarget이벤트 리스너가 등록된 요소이벤트가 등록된 <ul>

위 코드에서 <li>를 클릭하면 event.target은 클릭된 <li> 요소이고, event.currentTarget은 이벤트 리스너가 등록된 <ul> 요소입니다. 이벤트가 버블링되어 올라오는 동안 target은 변하지 않지만, currentTarget은 이벤트가 현재 처리되고 있는 요소를 가리키니까 전파 경로에 따라 달라집니다.

React에서 왜 currentTarget을 사용할까

React와 TypeScript를 쓸 때는 주로 currentTarget을 씁니다. 이유가 두 가지 있는데, 하나는 타입 안정성이고 다른 하나는 비동기 컨텍스트에서의 동작 방식입니다.

currentTarget은 리스너를 등록한 요소의 타입을 따른다

TypeScript에서 event.target과 event.currentTarget은 타입이 다릅니다.

tsx
<button onClick={handleClick}>
  <span>Click Me</span>
</button>;

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
  console.log(event.currentTarget); // HTMLButtonElement
  console.log(event.target); // EventTarget - 타입이 불명확
};

currentTarget은 이벤트가 발생한 위치와 상관없이 타입이 항상 HTMLButtonElement입니다. 반면 target은 실제로 클릭된 요소를 가리키니까, <span>을 클릭하면 HTMLSpanElement가 됩니다. TypeScript는 런타임에 어떤 자식 요소가 클릭될지 알 수 없으니 target의 타입을 EventTarget으로만 추론합니다. 그래서 .value나 .tagName 같은 속성에 접근하려면 타입 단언이 필요합니다.

비동기 코드에서는 currentTarget을 미리 저장한다

React 16까지는 SyntheticEvent 풀링(event pooling)이 있어서, 이벤트 핸들러 실행 후 event.target을 포함한 모든 속성이 null로 초기화됐습니다. 하지만 React 17에서 이벤트 풀링이 제거되면서 event.target은 비동기 컨텍스트에서도 안전하게 참조할 수 있게 됐죠.

다만 event.currentTarget은 다른 이유로 여전히 주의가 필요합니다.

tsx
const handleClick = (event: React.MouseEvent) => {
  setTimeout(() => {
    console.log(event.target);        // React 17+ 에서 안전
    console.log(event.currentTarget); // null — 핸들러 종료 후 React가 리셋
  }, 1000);
};

currentTarget만 null이 되는 데는 이유가 있습니다. React는 이벤트 핸들러가 끝나는 시점에 currentTarget을 null로 리셋합니다. currentTarget의 의미 자체가 "현재 이벤트를 처리하고 있는 리스너가 달린 요소"인데, 이벤트 전파가 완료되면 "현재 처리 중인 리스너"라는 개념 자체가 사라집니다. 이건 풀링과는 무관한 동작이고, React 17 이후에도 그대로 유지됩니다.

비동기 코드에서 currentTarget이 필요하다면 핸들러 실행 중에 변수에 저장해둬야 합니다.

tsx
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
  const currentTarget = event.currentTarget; // 핸들러 실행 중에 저장

  setTimeout(() => {
    console.log(currentTarget); // 안전
  }, 1000);
};

클릭된 요소와 처리 중인 요소를 구분한다

항목event.targetevent.currentTarget
의미실제로 이벤트가 발생한 요소이벤트 리스너가 등록된 요소
TypeScript 타입불명확 (EventTarget)명확 (지정한 타입)
비동기 사용React 17+ 에서 안전핸들러 종료 후 null — 미리 저장 필요
추천 사용처이벤트 위임 시 실제 클릭된 요소 확인이벤트 핸들러 등록 요소 접근

면접에서 이 둘을 헷갈리는 사람이 저 말고도 꽤 있는 것 같더라고요. target은 실제로 클릭된 요소, currentTarget은 리스너가 달린 요소. 이 차이를 예제 코드로 확인해두면 저처럼 반대로 설명하는 일은 줄어들 것 같습니다.