본문 바로가기
개념 정리/모던 자바스크립트 딥다이브

모던 자바스크립트 : 이벤트

by 매진2 2023. 12. 14.
728x90

1. 이벤트 드리븐 프로그래밍

  • 브라우저는 처리해야 할 특정 사건이 발생하면 이를 감지해 이벤트 발생
  • 만약 애플리케이션이 특정 타입의 이벤트에 대해 반응해 어떤 일을 하고 싶다면 해당하는 타입의 이벤트가 발생했을 때 호출될 함수를 브라우저에게 알려 호출 위임

 

  • 이벤트 핸들러 : 이때 이벤트가 발생했을 때 호출될 함수
  • 이벤트 핸들러 등록 : 이벤트 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것

 

  • 브라우저는 사용자의 버튼 클릭을 감지해 클릭 이벤트 발생 가능
  • 특정 버튼 요소에서 클릭 이벤트가 발생하면 특정 함수(이벤트 핸들러) 호출하도록 브라우저에게 위임(이벤트 핸들러 등록) 가능
  • 이벤트 핸들러 프로퍼티에 함수를 할당하면 해당 이벤트가 발생했을 때 할당한 함수가 브라우저에 의해 호출
  • 이벤트와 그에 대응하는 함수를 통해 사용자와 애플리케이션은 상호작용 가능
  • 이벤트 드리븐 프로그래밍 : 프로그램 흐름을 이벤트 중심으로 제어하는 프로그래밍 방식 

 

2. 이벤트 타입

  • 이벤트 타입 : 200여 가지의 이벤트의 종류를 나타내는 문자열

a. 마우스 이벤트

b. 키보드 이벤트

c. 포커스 이벤트

  • focusin, focusout 이벤트 핸들러를 이벤트 핸들러 프로퍼티 방식으로 등록하면 크롬, 사파리에서 정상 동작하지 않음
  • => addEventListner 메서드 방식 사용해 등록

d. 폼 이벤트

e. 값 변경 이벤트

f. DOM 뮤테이션 이벤트

g. 뷰 이벤트

h. 리소스 이벤트

 

3. 이벤트 핸들러 등록

  • 이벤트 핸들러 : 이벤트가 발생했을 때 브라우저에 호출을 위임한 함수 = 브라우저에 의해 호출될 함수
  • 이벤트 핸들러 등록하는 3가지 방법
    • 이벤트 핸들러 어트리뷰트 방식
    • 이벤트 핸들러 프로퍼티 방식
    • addEventListener 메서드 방식

a. DOM Level 0 : 이벤트 핸들러 어트리뷰트 방식

HTML 요소의 어트리뷰트 중 이벤트에 대응하는 이벤트 핸들러 어트리뷰트가 있음

이벤트 핸들러 어트리뷰트의 이름은 on 접두사와 이벤트의 종류를 나타내는 이벤트 타입으로 이루어짐

이벤트 핸들러 어트리뷰트 값으로 함수 호출문 등의 문을 할당하면 이벤트 핸들러 등록

 

이벤트 핸들러 어트리뷰트 값으로 함수 참조가 아닌 함수 호출문 등의 문 할당

이벤트 핸들러를 등록할 때 함수 참조를 등록해야 브라우저가 이벤트 핸들러 호출

함수 참조가 아니라 함수 호출문을 등록하면 함수 호출문의 평가 결과가 이벤트 핸들러로 등록

함수를 반환하는 고차 함수 호출문을 이벤트 핸들러로 등록한다면 문제가 없겠지만 함수가 아닌 값을 반환하는 함수 호출문을 이벤트 핸들러로 등록하면 브라우저가 이벤트 핸들러를 호출 할 수 없음

 

이벤트 핸들러 어트리뷰트 값으로 함수 호출문 할당하면

이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미

onclick="sayHi('Lee')" 어트리뷰트는 파싱되어 다음과 같은 함수를 암묵적으로 생성하고 이벤트 핸들러 어트리뷰트 이름과 동일한 키 onclick 이벤트 핸들러 프로퍼티에 할당

이렇게 동작하는 이유는 이벤트 핸들러에 인수 전달하기 위함

만약 이벤트 핸들러 어트리뷰트 값으로 함수 참조를 할당해야한다면 이벤트 핸들러에 인수를 전달하기 곤란함

결국 이벤트 핸들러 어트리뷰트 값으로 할당한 문자열은 암묵적으로 생성되는 이벤트 핸들러의 함수 몸체

따라서 이벤트 핸들러 어트리뷰트 값으로 여러개의 문 할당 가능

이벤트 핸들러 어트리뷰트 방식은 오래된 코드에서 간혹 사용하지만 지금은 지양 => HTML과 자바스크립트는 관심사가 다르기때문에 분리하는 것이 좋아서

CBD(Component Based Development) 방식의 Angular, React, Svelte, Veu 같은 프레임워크/라이브러리에서는 이벤트 핸들러 어트리뷰트 방식으로 이벤트 처리

HTML, CSS, 자바스크립트를 관심사가 다른 개별적인 요소가 아닌 뷰를 구성하기 위한 구성요소로 보기때문

 

 

 

 

b. DOM Level 0 : 이벤트 핸들러 프로퍼티 방식

window 객체, Document, HTMLElement 타입의 DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티 가짐

이벤트 핸들러 프로퍼티 키는 on 접두사와 이벤트의 종류를 나타내는 이벤트 타입으로 일어짐

이벤트 핸들러 프로퍼티에 함수 바인딩 하면 이벤트 핸들러 등록

DOM 노드의 이벤트 핸들러 프로퍼티에 함수 참조 할당

 

이벤트 핸들러 등록 위해서는 이벤트 타깃, 이벤트 타입, 이벤트 핸들러 지정

이벤트 타깃 : 이벤트를 발생시킬 객체

이벤트 타입 : 이벤트 종류 나타내는 문자열

이벤트 핸들러는 대부분 이벤트를 발생시킬 이벤트 타깃에 바인딩

하지만 반드시 이벤트 타깃에 이벤트 핸들러를 바인딩해야하는 것은 아님

 

DOM 노드 객체의 이벤트 핸들러 프로퍼티로 변환되므로 결과적으로 이벤트 핸들러 프로퍼티 방식과 동일

이벤트 핸들러 프로퍼티 방식은 이벤트 핸들러 어트리뷰트 방식의 HTML과 자바스크립트가 뒤섞이는 문제 해결

하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩 가능

 

 

 

 

c. DOM Level 2 : addEventListener 메서드 방식

  • EventTarget.prototype.addEventListener 메서드 사용해 이벤트 핸들러 등록 가능
    • 첫번째 매개변수 : 이벤트의 종류를 나타내는 문자열인 이벤트 타입 전달
      • 이때 on 접두사 붙이지 않음
  • 두번째 매개변수 : 이벤트 핸들러 전달
  • 마지막 매개변수 : 이벤트를 캐치할 이벤트 전파 단계 캡처링 또는 버블링 지정
    • 생략하거나 false 지정 : 버블링 단계에서 이벤트 캐치
    • true 지정 : 캡처링 단계에서 이벤트 캐치

 

addEventListener 메서드에는 이벤트 핸들러를 인수로 전달

만약 동일한 HTML 요소에서 발생한 동일한 이벤트에 대해 이벤트 핸들러 프로퍼티 방식와 addEventListener 메서드 방식 모두 사용해 이벤트 핸들러 등록하면? => 2개의 이벤트 핸들러 모두 호출

 

동일한 HTML 요소에 동일한 이벤트에 대해 하나 이상의 이벤트 핸들러 등록 가능

등록된 순서대로 호출됨

만약 addEventListener 메서드를 통해 참조가 동일한 이벤트 핸들러를 중복 등록하면 하나의 이벤트 핸들러만 등록

4. 이벤트 핸들러 제거

EventTarget.prototype.removeEventListener 메서드 사용해 등록한 이벤트 핸들러 제거

 

removeEventListener 메서드에 전달할 인수는 addEventListener와 동일

단, addEventListener 메서드에 전달한 인수와 removeEventListener 메서드에 전달한 인수와 일치해야함

 

무명함수를 이벤트 핸들러로 등록한 경우 제거 불가능

이벤트 핸들러를 제거하려면 이벤트 핸들러의 참조를 변수나 자료구조에 저장하고 있어야함

기명 이벤트 핸들러 내부에서 removeEventListener 메서드를 호출하여 이벤트 핸들러를 제거하는건 가능

단, 이벤트 핸들러는 단 한번만 호출됨

기명함수를 이벤트 핸들러로 등록할 수 없다면 호출된 함수, 즉 자신을 가리키는 arguments.callee 사용 가능

하지만 arguments.callee는 코드 최적화를 방해하므로 strict mode에서 사용 금지 => 참조를 변수나 자료구조에 저장해 제거 지향

 

이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러는 removeEventListener 메서드로 제거 불가능

이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸드러를 제거하려면 이벤트 핸들러 프로퍼티에 null 할당

 

5. 이벤트 객체

이벤트가 발생하면 이벤트에 관련한 다양한 정보를 담고 있는 이벤트 객체가 동적 생성됨

생성된 이벤트 객체는 이벤트 핸들러의 첫번째 인수로 전달됨

 

 

클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫번째 인수로 전달되어 매개변수 e에 암묵적 할당

브라우저가 이벤트 핸들러를 호출할 때 이벤트 객체를 인수로 전달하기 때문

이벤트 객체를 전달받으려면 이벤트 핸들러를 정의할 때 이벤트 객체를 전달받을 매개변수를 명시적으로 선언해야함

 

이벤트 핸들러 어트리뷰트 방식의 경우 첫번째 매개변수 이름이 event가 아니면 이벤트 객체를 전달받지 못함

이벤트 핸들러 어트리뷰트 값은 암묵적으로 생성되는 이벤트 핸들러의 함수 몸체를 의미하기 때문

암묵적으로 생성된 onclick 이벤트 핸들러의 첫번째 매개변수의 이름이 event로 암묵적으로 명명되기 때문에 event가 아닌 다른 이름으로는 이벤트 객체 받지 못함

 

a. 이벤트 객체의 상속 구조

이벤트가 발생하면 이벤트 타입에 따라 다양한 타입의 이벤트 객체 생성

Event, UIEvent, MouseEvent 등은 모두 생성자 함수 -> 생성자 함수를 호출해 이벤트 객체 생성

 

이벤트가 발생하면 암묵적으로 생성되는 이벤트 객체도 생성자 함수에 의해 생성

생성된 이벤트 객체는 생성자 함수와 더불어 생성되는 프로토타입으로 구성된 프로토타입 체인의 일원이 됨

이벤트 객체 중 일부는 사용자의 행위에 의해 생성된 것이고 일부는 자바스크립트 코드에 의해 인위적으로 생성된 것

예를 들어

MouseEvent 타입의 이벤트 객체 : 사용자가 마우스 클릭하거나 이동했을 때 생성되는 이벤트 객체

CustomEvent 타입의 이벤트 객체 : 자바스크립트 코드에 의해 인위적으로 생성한 이벤트 객체

 

Event 인터페이스 : DOM 내에서 발생한 이벤트에 의해 생성되는 이벤트 객체

Event 인터페이스에는 모든 이벤트 객체의 공통 프로퍼티 정의되어 있고

FocuseEvent, MouseEvent,KeyBoardEvent, WheelEvent 같은 하위 인터페이스에는 이벤트 타입에 따라 고유한 프로퍼티 정의

 

 

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

Event 인터페이스, 즉 Event.prototype에 정의되어 있는 이벤트 관련 프로퍼티는 UIEvent, CustomEvent, MouseEvent 등 모든 파생 이벤트 객체에 상속됨

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

일반적으로 이벤트 객체의 target 프로퍼티와 currentTarget 프로퍼티는 동일한 DOM 요소 가리키지만 아닐 수도 있음

 

 

c. 마우스 정보 취득

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

드래그 : mousedown 이벤트 발생 + mousemove 이벤트 발생한 시점에 시작 -> mouseup 이벤트 발생 시점에 종료

mousemove 이벤트가 발생할 때마다의 마우스 포인터 좌표 비교해 드래그 대상의 이동거리 계산

 

마우스 포인터 좌표는 MouseEvent 타입의 이벤트 객체에서 제공

mousedown, mousemove, mouseup 이벤트가 발생하면 생성되는 MouseEvent 타입의 이벤트 객체는 마우스 포인터의 좌표 정보를 나타내는 screenX/screenY, clientX/clientY, pageX/pageY, offsetX/offsetY 프로퍼티 제공

이 중 clientX/clientY 는 뷰포트를 기준으로 마우스 포인터 좌표 나타냄

d. 키보드 정보 취득

keyboardEvent 타입의 이벤트 객체는 altKey, ctrlKey, shiftKey, metakey, key, keyCode 같은 고유의 프로퍼티 가짐

입력한 키와 key 프로퍼티 값의 대응 관계가 있음

 

 

6. 이벤트 전파

이벤트 전파 : DOM 트리 상에 존재하는 DOM 요소 노드에서 발생한 이벤트가 DOM 트리 통해 전파되는 것

생성된 이벤트 객체는 이벤트를 발생시킨 DOM 요소인 이벤트 타깃을 중심으로 DOM 트리 통해 전파

이벤트 객체가 전파되는 방향에 따라 3단계로 구분

캡처링 단계 : 이벤트가 상위 요소에서 하위 요소 방향으로 전파

타깃 단계 : 이벤트가 이벤트 타깃에 도달

버블링 단계 : 이벤트가 하위요소에서 상위 요소 방향으로 전파

 

이벤트 핸들러 어트리뷰트/프로퍼티 방식으로 등록한 이벤트 핸들러는 타깃단계와 버블링 단계의 이벤트만 캐치 가능

addEventListener 메서드 방식으로 등록한 이벤트 핸들러는 타깃 단계와 버블링 단계뿐만 아니라 캡처링 단꼐의 이벤트도 선별적으로 캐치 가능

캡처링 단계의 이벤트 캐치하려면 addEventListener 메서드의 3번째 인수로 true를 전달해야함

 

이벤트는 이벤트를 발생시킨 이벤트 타킷, 상위 DOM 요소에서도 캐치 가능

DOM 트리를 통해 전파되는 이벤트는 이벤트 패스(이벤트가 통과하는 DOM 트리 상의 경로, event.prototype.composedPath 메서드로 확인 가능)에 위치한 모든 DOM 요소에서 캐치 가능

 

대부분의 이벤트는 캡처링과 버블링을 통해 전파되는데 몇몇 이벤트는 버블링을 통해 전파되지 않음

이벤트 객체의 공통 프로퍼티 event.bubbles 값이 모두 false

이벤트 타깃의 상위 요소에서 이벤트를 캐치라면 캡처링 단계의 이벤트 캐치 => 이런 경우 거의 없어서 대체제 사용

포커스 이벤트 : focus, blur => focusein, focuseout

리소스 이벤트 : load, unload, abort, error 

마우스 이벤트 : mouseenter, mouseleave => mouseover, mouserout

7. 이벤트 위임

이벤트 위임은 여러개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 방법

이벤트는 이벤트 위임을 통해 상위 DOM 요소에 이벤트 핸들러 등ㄹㄱ하면 여러개의 하위 DOM 요소에 이벤트 핸들러 등록할 필요 없음

동적으로 하위 DOM 요소를 추가하더라도 일일이 추가된 DOM 요소에 이벤트 핸들러를 등록할 필요 없음

이벤트 위임으로 통해 하위 DOM 요소에서 발생한 이벤트를 처리할 때 주의점

상위 요소에 이벤트 핸들러를 등록하기 때문에 이벤트 타깃, 즉 이벤트를 실제로 발생시킨 DOM 요소가 개발자가 기대한 DOM 요소가 아닐 수 있음

이벤트에 반응이 필요한 DOM 요소에 한정해 이벤트 핸들러가 실행되도록 이벤트 타깃을 검사해야함

 

이벤트 위임을 통해 상위 DOM 요소에 이벤트를 바인딩한 경우 이벤트 객체의 target 프로퍼티와 currentTarget 프로퍼티가 다른 DOM 요소를 가리킬 수 있음

타깃요소의 하위 요소에서 이벤트 발생한 경우

currentTarget 프로퍼티는 언제나 타깃 요소를 가리키지만 

target 프로퍼티는 실제로 이벤트를 발생시킨 DOM 요소 가리킴

 

 

8. DOM 요소의 기본 동작 조작

a. DOM 요소의 기본 동작 중단

DOM 요소는 저마다 기본 동작이 있음

이벤트 객체의 preventDefault 메서드는 DOM 요소의 기본 동작 중단 시킴

 

 

 

 

b. 이벤트 전파 방지

이벤트 객체의 stopPropagation 메서드는 이벤트 전파 중지 시킴

하위 DOM 요소의 이벤트를 개별적으로 처리하기 위해 이벤트 전파 중지 시킴

 

 

9.이벤트 핸들러 내부의 this

a. 이벤트 핸들러 어트리뷰트 방식

이벤트 핸들러 어트리뷰트의 값으로 지정한 문자열은 사실 암묵적으로 생성되는 이벤트 핸들러의 문

handleClick 함수는 이벤트 핸들러에 의해 일반 함수로 호출되어 함수 내부의 this는 전역 객체를 가리킴

단, 이벤트 핸들러를 호출할 때 인수로 전달한 this는 이벤트를 바인딩한 DOM 요소 가리킴

handleClick 함수에 전달한 this는 암묵적으로 생성된 이벤트 핸들러 내부의 this

이벤트 핸들러 어트리뷰트 방식에 의해 암묵적으로 생성된 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소 가리킴

 

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

이벤트 핸들러 프로퍼티 방식와 addEventListener 메서드 방식 모두 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소 가리킴

이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같음

 

화살표 함수로 정의한 이벤트 핸들러 내부의 this는 상위 스코프의 this 가리킴

화살표 함수는 함수 자체의 this 바인딩을 갖지 않기 때문

 

클래스에서 이벤트 핸들러를 바인딩하는 경우 this 주의해야함

increase 메서드 내부의 this는 클래스가 생성할 인스턴스를 가리키지 않음

이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리키기 댸문에 increase 메서드 내부의 this는 this.$button 가리킴

따라서 increase 메서드를 이벤트 랜들러로 바인딩할 때 bind 메서드를 사용해 this를 전달해 increase 메서드 내부의 this가 클래스가 생성할 인스턴스를 가리키도록 해야함

또는 클래스 필드에 할당한 화살표 함수를 이벤트 핸들러로 등록해 이벤트 핸들러 내부의 thisrk 인스턴스 가리키도록 할 수도 잇음

다만 이때 이벤트 핸들러 increase는 프로토타입 메서드가 아닌 인스턴스 메서드가 됨

 

 

 

 

 

 

 

10. 이벤트 핸들러에 인수 전달

함수에 인수를 전달하려면 함수를 호출할 때 전달

이벤트 핸들러 어트리뷰트 방식은 함수 호출문을 사용할 수 있기때문에 인수 전달 가능

이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식의 경우 이벤트 핸들러를 브라우저가 호출하기 때문에 함수 호출문이 아닌 함수 자체를 등록해야해서 인수 전달 불가

 

이벤트 핸들러 내부에서 함수 호출하면서 인수 전달

 

이벤트 핸들러를 반환하는 함수를 호출하면서 인수 전달

 

 

11. 커스텀 이벤트

a. 커스텀 이벤트 생성

이벤트 객체는 이벤트 생성자 함수로 생성

이벤트가 발생하면 암묵적으로 생성되는 이벤트 객체는 발생한 이벤트 종류에 따라 이벤트 타입 결정

이벤트 생성자 함수를 호출하여 명시적으로 생성한 이벤트 객체는 임의의 이벤트 타입 지정

 

커스텀 이벤트 : 개발자의 의도로 생성된 이벤트

  • 이벤트 생성자 함수의 첫번째 인수 : 이벤트 타입을 나타내는 문자열 전달 받음
    • 이벤트 타입을 나타내는 문자열은 기존 이벤트 타입 사용할 수도 있고
    • 기존 이벤트 타입이 아닌 임의의 문자열 사용하여 새로운 이벤트 타입 지정 가능 =>일반적으로 CustomEvent 이벤트 생성자 함수 사용

생성된 커스텀 이벤트 객체는 버블링되지 않고 preventDefault 메서드로 취소할 수도 없음

커스텀 이벤트 객체는 bubbles와 cancelable 프로퍼티의 값이 false로 기본 설정

커스텀 이벤트 객체의 bubbles와 cancelable 프로퍼티를 true로 설정하려면 이벤트 생성자 함수의 두번째 인수로 bubbles와 cancelable 프로퍼티를 갖는 객체 전달 

 

커스텀 이벤트 객체에는 bubbles와 cancelable 프로퍼티뿐만 아니라 이벤트 타입에 따라 가지는 이벤트 고유의 프로퍼티 값 지정 가능

이벤트 객체 고유의 프로퍼티 값 지정하려면 두번째 인수로 프로퍼티 전달

이벤트 생성자 함수로 생성한 커스텀 이벤트는 isTrusted 프로퍼티 값이 언제나 false

사용자 행위에 의해 발생한 이벤트에 의해 생성된 이벤트 객체의 isTrusted 프로퍼티 값은 언제나 true

b. 커스텀 이벤트 디스패치

생성된 커스텀 이벤트는 dispatchEvent 메서드로 디스패치 가능

디스패치 : 이벤트를 발생시키는 행위

dispatchEvent 메서드에 이벤트 객체를 인수로 전달하면서 호출하면 인수로 전달한 이벤트 타입의 이벤트 발생

 

이벤트 핸들러는 비동기 처리방식으로 동작하지만 dispatchEvent 메서드는 이벤트 핸들러를 동기 처리 방식으로 호출

dispatchEvent 메서드를 호출하면 커스텀 이벤트에 바인딩된 이벤트 핸들러를 직접 호출하는것과 같음

dispatchEvent 메서드로 이벤트를 디스패치 하기 이전에 커스텀 이벤트를 처리할 이벤트 핸들러 등록해야함

 

CustomEvent 이벤트 생성자 함수에는 두번째 인수로 이벤트와 함께 전달하고 싶은 정보를 담은 detail 프로퍼티를 포함하는 객체 전달 가능

이 정보는 이벤트 객체의 detail 프로퍼티(e.detail)에 담겨 전달

커스텀 이벤트 객체 생성한 경우 반드시 addEventListener 메서드 방식으로 이벤트 핸들러 등록해야함

on이벤트 타입으로 이루어진 이벤트 핸들러 어트리뷰트/프로퍼티가 요소 노드에 존재하지 않기 때문

 

728x90