Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Archives
Today
Total
관리 메뉴

nhyunzi

[ 모던 자바스크립트 Deep Dive ] 40 이벤트 본문

카테고리 없음

[ 모던 자바스크립트 Deep Dive ] 40 이벤트

nhyunzi 2025. 1. 11. 10:32

40.1 이벤트 드리븐 프로그래밍

  • 브라우저는 처리해야 할 특정 사건(ex. 클릭, 키보드 입력, 마우스 이동)이 발생하면 이를 감지하여 이벤트를 발생시킨다.
  • 만약 애플리케이션이 특정 타입의 이벤트에 대해 반응하여 어떤 일을 하고 싶다면 해당하는 타입의 이벤트가 발생했을 때 호출될 함수를 브라우저에게 알려 호출을 위임한다.
  • 이때 이벤트가 발생했을 때 호출될 함수를 이벤트 핸들러라 하고, 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것을 이벤트 핸들러 등록이라 한다.
  • 이처럼 이벤트와 그에 대응하는 함수(이벤트 핸들러)를 통해 사용자와 애플리케이션은 상호작용을 할 수 있다.
  • 이와 같이 프로그램의 흐름을 이벤트 중심으로 제어하는 프로그래밍을 이벤트 드리븐 프로그래밍이라 한다.

40.2 이벤트 타입

  • 이벤트 타입은 이벤트의 종류를 나타내는 문자열이다.
  • 이벤트 타입은 약 200여 가지가 있다.
  • 이벤트 타입 상세 목록은 MDN의 Event reference에서 확인할 수 있다.

40.2.1 마우스 이벤트

40.2.2 키보드 이벤트

40.2.3 포커스 이벤트

focusin, focusout 이벤트를 이벤트 핸들러 프로퍼티 방식으로 등록하면 크롬, 사파리에서 정상 동작하지 않으므로 addEventListener 메서드 방식을 사용해 등록해야 한다.

40.2.4 폼 이벤트

40.2.5 값 변경 이벤트

40.2.6 DOM 뮤테이션 이벤트

40.2.7 뷰 이벤트

40.2.8 리소스 이벤트

40.3 이벤트 핸들러 등록

  • 이벤트가 발생하면 브라우저에 의해 호출될 함수가 이벤트 핸들러다.
  • 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것을 이벤트 핸들러 등록이라 한다.
  • 이벤트 핸들러를 등록하는 방법은 3가지다.

 

40.3.1 이벤트 핸들러 어트리뷰트 방식

  • HTML 요소의 어트리뷰트 중에는 이벤트에 대응하는 이벤트 핸들러 어트리뷰트가 있다.
  • 이벤트 핸들러 어트리뷰트의 이름은 onclick과 같이 on 접두사와 이벤트의 종류를 나타내는 이벤트 타입으로 이루어져 있다.
  • 이벤트 핸들러 어트리뷰트 값으로 함수 호출문 등의 문을 할당하면 이벤트 핸들러가 등록된다.
  • 주의 : 이벤트 핸들러 어트리뷰트 값으로 함수가 아닌 값을 반환하는 함수 호출문을 이벤트 핸들러로 등록하면 브라우저가 이벤트 핸들러를 호출할 수 없다. 함수 참조를 등록해야 브라우저가 이벤트 핸들러를 호출할 수 있다.
<!DOCTYPE html>
<html>
<body>
  	<button onclick="sayHi('Lee')">Click me!</button>
	<script>
      function sayHi(name){
    	console.log(`Hi! ${name}.`);	
      }
    </script>
</body>
</html>

 

40.3.2 이벤트 핸들러 프로퍼티 방식

  • window 객체와 Document, HTMLElement 타입의 DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가지고 있다.
  • 이벤트 핸들러 프로퍼티의 키 또한 on 접두사와 이벤트 타입으로 이루어져 있다.
  • 이벤트 핸들러 프로퍼티에 함수를 바인딩하면 이벤트 핸들러가 등록된다.
<!DOCTYPE html>
<html>
<body>
  	<button>Click me!</button>
	<script>
      const $button = document.querySelector('button');
      
      // 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩
      $button.onclick = function (){
    	console.log('button click');	
      }
    </script>
</body>
</html>
  • 이벤트 핸들러를 등록하기 위해서는 이벤트를 발생시킬 객체인 이벤트 타깃과 이벤트의 종류를 나타내는 문자열인 이벤트 타입, 그리고 이벤트 핸들러를 지정할 필요가 있다.
  • 이벤트 핸들러는 대부분 이벤트를 발생시킬 이벤트 타깃에 바인딩한다.
  • 하지만 반드시 이벤트 타깃에 이벤트 핸들러를 바인딩해야 하는 것은 아니다.
  • 이벤트 핸들러는 이벤트 타깃 또는 전파된 이벤트를 캐치할 DOM 노드 객체에 바인딩한다.
  • 이벤트 핸들러 프로퍼티 방식은 이벤트 핸들러 어트리뷰트 방식의 HTML과 자바스크립트가 뒤섞이는 문제를 해결할 수 있다.
  • 하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩할 수 있다는 단점이 있다.

 

40.3.3 addEventListener 메서드 방식

  • DOM Level 2에서 도입된 EventTarget.prototype.addEventListener 메서드를 사용하여 이벤트 핸들러를 등록할 수 있다.
  • 앞서 살펴본 이벤트 핸들러 어트리뷰트 방식과 이벤트 핸들러 프로퍼티 방식은 DOM Level 0부터 제공되던 방식이다.
<!DOCTYPE html>
<html>
<body>
  	<button>Click me!</button>
	<script>
      const $button = document.querySelector('button');
      
      // 이벤트 핸들러 프로퍼티 방식
      // $button.onclick = function (){
      //	console.log('button click');	
      // }
      
      // addEventListener 메서드 방식
      $button.addEventListener('click', function (){
    	console.log('button click');	
      });
    </script>
</body>
</html>
  • 이벤트 핸들러 프로퍼티 방식은 하나 이상의 이벤트 핸들러를 등록할 수 없지만 addEventListener 메서드는 하나 이상의 이벤트 핸들러를 등록할 수 있다.
  • 이때 이벤트 핸들러는 등록된 순서대로 호출된다.
<!DOCTYPE html>
<html>
<body>
  	<button>Click me!</button>
	<script>
      const $button = document.querySelector('button');
      
      // addEventListener 메서드 방식
      $button.addEventListener('click', function (){
    	console.log('button click');	
      });
      
      $button.addEventListener('click', function (){
    	console.log('button click 2');	
      });
    </script>
</body>
</html>

 

40.4 이벤트 핸들러 제거

addEventListener 메서드 방식

  • addEventListener 메서드로 등록한 이벤트 핸들러를 제거하려면 EventTarget.prototype.removeEventListener 메서드를 사용한다. 단, addEventListener 메서드에 전달한 인수와 removeEventListener 메서드에 전달한 인수가 일치하지 않으면 이벤트 핸들러가 제거되지 않는다.
<!DOCTYPE html>
<html>
<body>
  	<button>Click me!</button>
	<script>
      const $button = document.querySelector('button');
      
      const handleClick = () => console.log('button click');	
      
      // 다음과 같이 무명 함수를 이벤트 핸들러로 등록한 경우 제거할 수 없다. 
      // 이벤트 핸들러를 제거하려면 이벤트 핸들러의 참조를 변수나 자료구조에 저장하고 있어야 한다.
      // $button.addEventListener('click', function (){
      //	console.log('button click');	
      // });
      
      // 이벤트 핸들러 등록
      $button.addEventListener('click', handleClick);
      
      // 이벤트 핸들러 제거 
      $button.removeEventListener('click', handleClick, true); // 인수가 일치하지 않으므로 실패
      $button.removeEventListener('click', handleClick); // 성공
    </script>
</body>
</html>

이벤트 핸들러 프로퍼티 방식

  • 이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러는 removeEventListener 메서드로 제거할 수 없다. 이를 제거하려면 이벤트 핸들러 프로퍼티에 null을 할당한다.
<!DOCTYPE html>
<html>
<body>
  	<button>Click me!</button>
	<script>
      const $button = document.querySelector('button');
      
      const handleClick = () => console.log('button click');	
      
      // 이벤트 핸들러 프로퍼티 방식으로 이벤트 핸들러 등록
      $button.onclick = handleClick;
      
      // 이벤트 핸들러 제거 
      $button.onclick = null;
    </script>
</body>
</html>

 

 

40.5 이벤트 객체

  • 이벤트가 발생하면 이벤트에 관련한 다양한 정보를 담고 있는 이벤트 객체가 동적으로 생성된다.
  • 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다.
  • 클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달되어 매개변수 e에 암묵적으로 할당된다.
  • 이는 브라우저가 이벤트 핸들러를 호출할 때 이벤트 객체를 인수로 전달하기 때문이다.
  • 따라서 이벤트 객체를 전달받으려면 이벤트 핸들러를 정의할 때 이벤트 객체를 전달받은 매개변수를 명시적으로 선언해야 한다.
<!DOCTYPE html>
<html>
<body>
  	<p>클릭하세요. 클릭한 곳의 좌표가 표시됩니다.</p>
    <em class="message"></em>
	<script>
      const $msg = document.querySelector('.message');
      
      function showCoords(e){
      	$msg.textContent = `clientX: ${e.clientX}, clientY: ${e.clientY}`;
      }
      
      document.onclick = showCoords;
    </script>
</body>
</html>

40.5.1 이벤트 객체의 상속 구조

  • 이벤트가 발생하면 이벤트 타입에 따라 다양한 타입의 이벤트 객체가 생성된다.
  • 이벤트 객체는 다음과 같은 상속 구조를 갖는다.

40.5.2 이벤트 객체의 공통 프로퍼티

  • Event 인터페이스의 이벤트 관련 프로퍼티는 모든 이벤트 객체가 상속받는 공통 프로퍼티다.
  • 이벤트 객체의 공통 프로퍼티는 다음과 같다.

40.5.3 마우스 정보 취득

  • MouseEvent 타입의 이벤트 객체는 다음과 같은 고유의 프로퍼티를 갖는다.
  • 마우스 포인터의 좌표 정보를 나타내는 프로퍼티 : screenX/screenY, clientX/clientY, pageX/pageY, offsetX/offsetY\
  • 버튼 정보를 나타내는 프로퍼티 : altKey, ctrlKey, shiftKey, button

40.5.4 키보드 정보 취득

  • keyboardEvent 타입의 이벤트 객체는 altKey, ctrlKey, shiftKey, metaKey, key, keyCode 같은 고유의 프로퍼티를 갖는다.

 

40.6 이벤트 전파

  • DOM 트리 상에 존재하는 DOM 요소 노드에서 발생한 이벤트는 DOM 트리를 통해 전파된다.
  • 위 예제에서 ul 요소의 두 번째 자식 요소인 li 요소를 클릭하면 클릭 이벤트가 발생한다.
  • 이때 생성된 이벤트 객체는 이벤트를 발생시킨 DOM 요소인 이벤트 타깃을 중심으로 DOM 트리를 통해 전파된다.
  • 이벤트 전파는 이벤트 객체가 전파되는 방향에 따라 다음과 같이 3단계로 구분할 수 있다.
  • 이벤트 전파 단계
  • 캡처링 단계 : 이벤트가 상위 요소에서 하위 요소 방향으로 전파
  • 타깃 단계 : 이벤트가 이벤트 타깃에 도달
  • 버블링 단계: 이벤트가 하위 요소에서 상위 요소 방향으로 전파
  • 이벤트 핸들러 어트리뷰트/프로퍼티 방식으로 등록한 이벤트 핸들러는 타깃 단계와 버블링 단계의 이벤트만 캐치할 수 있다.
  • addEventListener 메서드 방식으로 등록한 이벤트 핸들러는 캡처링 단계의 이벤트도 선별적으로 캐치할 수 있다.
  • 이처럼 이벤트는 이벤트를 발생시킨 이벤트 타깃은 물론 상위 DOM 요소에서도 캐치할 수 있다.

 

40.7 이벤트 위임

  • 이벤트 위임은 여러 개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 방법을 말한다.
  • 이벤트 전파에서 살펴본 바와 같이 이벤트는 이벤트 타깃은 물론 상위 DOM 요소에서도 캐치할 수 있다.

 

  • 이벤트 위임의 장점
  • 이벤트 위임을 통해 상위 DOM 요소에 이벤트 핸들러를 등록하면 여러 개의 하위 DOM 요소에 이벤트 핸들러를 등록할 필요가 없다.
  • 동적으로 하위 DOM 요소를 추가하더라도 일일이 추가된 DOM 요소에 이벤트 핸들러를 등록할 필요가 없다.

 

40.8 DOM 요소의 기본 동작 조작

 

40.8.1 DOM 요소의 기본 동작 중단

  • DOM 요소는 저마다 기본 동작이 있다.
  • 이벤트 객체의 preventDefault 메서드는 이러한 DOM 요소의 기본 동작을 중단시킨다.

40.8.2 이벤트 전파 방지

  • 이벤트 객체의 stopPropagation 메서드는 이벤트 전파를 중지시킨다.

 

40.9 이벤트 핸들러 내부의 this

 

40.9.1 이벤트 핸들러 어트리뷰트 방식

  • 다음 예제의 handleClick 함수 내부의 this는 전역 객체 window를 가리킨다.
  • 일반 함수로서 호출되는 함수 내부의 this는 전역 객체를 가리키기 때문
  • 단, 이벤트 핸들러를 호출할 때 인수로 전달한 this는 이벤트를 바인딩한 DOM 요소를 가리킨다.

40.9.2 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식

  • 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식 모두 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
  • 즉, 이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같다.
  • 화살표 함수로 정의한 이벤트 핸들러 내부의 this는 상위 스코프의 this를 가리킨다.
  • 화살표 함수는 함수 자체의 this 바인딩을 갖지 않는다.

 

40.10 이벤트 핸들러에 인수 전달

  • 이벤트 핸들러 어트리뷰트 방식은 함수 호출문을 사용할 수 있기 때문에 인수를 전달할 수 있다.
  • 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식의 경우 이벤트 핸들러를 브라우저가 호출하기 때문에 함수 호출문이 아닌 함수 자체를 등록하여 인수를 전달할 수 없다.
  • 그러나 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달할 수 있다.

 

40.11 커스텀 이벤트

 

40.11.1 커스텀 이벤트 생성

  • 개발자의 의도로 생성된 이벤트를 커스텀 이벤트라 한다.
  • 이벤트 생성자 함수는 첫 번째 인수로 이벤트 타입을 나타내는 문자열을 전달받는다.
  • 이때 이벤트 타입을 나타내는 문자열은 기존 이벤트 타입을 사용할 수도 있고, 임의의 문자열을 사용하여 새로운 이벤트 타입을 지정할 수도 있다.
  • 이 경우 일반적으로 CustomEvent 이벤트 생성자 함수를 사용한다.
// KeyboardEvent 생성자 함수로 keyup 이벤트 타입의 커스텀 이벤트 객체를 생성
const keyboardEvent = new KeyboardEvent('keyup');
console.log(keyboardEvent.type); // keyup

// CustomEvent 생성자 함수로 foo 이벤트 타입의 커스텀 이벤트 객체를 생성
const customEvent = new CustomEvent('foo');
console.log(customEvent.type); // foo

40.11.2 커스텀 이벤트 디스패치

  • 생성된 커스텀 이벤트는 dispatchEvent 메서드로 디스패치(이벤트를 발생시키는 행위)할 수 있다.
  • dispatchEvent 메서드에 이벤트 객체를 인수로 전달하면서 호출하면 인수로 전달한 이벤트 타입의 이벤트가 발생한다.
  • 기존 타입이 아닌 임의의 이벤트 타입을 지정하여 커스텀 이벤트 객체를 생성한 경우 반드시addEventListener 메서드 방식으로 이벤트 핸들러를 등록해야 한다.
  • 이벤트 핸들러 어트리뷰트/프로퍼티 방식을 사용할 수 없는 이유는 on + 이벤트 타입으로 이루어진 이벤트 핸들러 어트리뷰트/프로퍼티가 요소 노드에 존재하지 않기 때문이다.