17. 이벤트

17-1. 이벤트 기본 개념

17-1-1. 이벤트의 의미와 중요성

사용자의 클릭, 입력값을 입력하는 등 특정 동작에서 변화가 발생하는 (브라우저에 표시되고, 값이 변경되는 등) 것을 프로그래밍에서 ‘이벤트’라고 한다. 이벤트 발생을 감지하고, 코드를 실행하는 역할을 하는 함수를 ‘이벤트 핸들러’라고 한다. 이는 동적 상호작용을 제공하는 데 중요하고, 에러와 예외 처리도 수행할 수 있다.
이벤트는 브라우저에서 발생하는 다양한 동작을 다루는데, 클릭 이벤트 외에도 마우스 이벤트, 키보드 이벤트, 포커스 관련 이벤트, 뷰포트 및 창 이벤트, 네트워크 상태 변경 이벤트 등 많은 유형이 있다. 각 이벤트 유형은 특징과 사용 사례가 다르며, 프로젝트 요구사항에 따라 적절하게 선택해야 한다. 예를 들어 웹 애플리케이션에서 실시간 업데이트, 폼 유효성 검사, 모달 팝업, 게임 상호작용 및 데이터 검색과 같은 다양한 사용 사례를 위해 활용된다.
또한 이벤트 위임 패턴을 사용하여 동적으로 생성된 여러 요소에 대한 이벤트 처리를 최적화하는 방법도 중요하다. 크로스 브라우저 호환성과 보안 측면에서 이벤트 처리에 대한 안전한 접근 방법을 고려하여 이벤트에 대해 심층적인 이해를 하고, 적절하게 활용하는 방법을 항상 염두에 두어야 한다. 다양한 이벤트 타입, 이벤트 발생 조건 및 처리, 패턴 등에 대해서 살펴볼 예정이다.
 

17-2. 이벤트 핸들링

17-2-1. 이벤트 리스너 등록 및 삭제

  • 이벤트리스너 등록하는 메서드 element.addEventListener(eventType, eventListener)
<button id='btn'>Click</button> <script> const btn = document.getElementById('btn'); btn.addEventListener('click',() => { alert('버튼이 클릭 되었습니다.'); }) </script>
addEventListener() 메서드를 사용하여 ‘click’ 이벤트 타입과 실행함수를 파라미터로 받는다. 클릭 이벤트가 발생하면 alert 메시지가 표시된다. 여러 개 이벤트 사용 시 같은 이벤트 리스너를 여러 번 추가할 수 있다. 가장 많이 쓰이는 기본적인 ‘click’ 이벤트에 대한 예제이다. 마우스, 키보드 등 다양한 이벤트 타입이 존재하고, 이벤트 등록은 모두 addEventListener() 메서드를 사용한다.
 
  • 이벤트리스너 제거하는 메서드 element.removeEventListener(eventType, eventListener)
const btn = document.getElementById('btn'); function btnClick() { alert('버튼이 클릭 되었습니다.'); }; btn.removeEventListener('click', btnClick);
addEventListener() 메서드로 이벤트 리스너를 등록할 때, 같은 이벤트 타입과 콜백 함수를 사용하여 removeEventListener() 메서드를 호출하여 해당 이벤트 리스너를 제거할 수 있다. 이벤트 리스너를 제거할 때 사용한 콜백 함수를 다시 사용해야 하므로 익명 함수로 이벤트리스너를 등록하면 제거할 수 없으니 주의해야 한다.
 

17-2-2. 이벤트 타입과 발생 조건

  • 클릭 이벤트 (click Event)
const btn = document.querySelector('button'); // 클릭 이벤트 핸들러 등록 btn.addEventListener('click', () => { alert('버튼이 클릭 되었습니다.'); });
위에서 설명했듯이 가장 대표적으로 많이 쓰이는 이벤트 타입은 ‘click’ 이벤트로 사용자가 마우스 왼쪽 버튼을 클릭할 때 발생하는 이벤트이다. 특정 요소를 불러오고, 클릭할 때 alert 메시지를 표시하거나, 스타일을 변경하는 등 사용된다.
 
  • 마우스 이벤트 (mouse Event)
const myDiv = document.getElementById('myDiv'); // 마우스 오버 이벤트 myDiv.addEventListener('mouseenter', () => { myDiv.textContent = '마우스가 위에 있어요!'; }); // 마우스 아웃 이벤트 myDiv.addEventListener('mouseleave', () => { myDiv.textContent = '마우스를 올려보세요'; });
마우스 이벤트 타입은 마우스 동작 시 발생하는 이벤트로 자주 사용되는 두 가지는 첫 번째, mouseenter 로 마우스 포인터가 요소 위로 갔을 때 이벤트가 발생한다. 주로 요소의 호버시 스타일 변경이나, 이벤트 감지로 효과를 적용할 때 활용한다. 두 번째는 mouseleave 로 마우스 포인터가 요소를 빠져나갈 때 발생한다. 호버시 효과를 제거할 때 활용한다. 이외에도 아래 표와같이 다양한 마우스 이벤트 타입들이 존재한다. 이벤트를 상황에 맞게 효과적으로 활용하면 된다.
이벤트 타입
mousedown
mouseup
mousemove
wheel
contextmenu
발생 이벤트
마우스 버튼 클릭하는 순간 발생한다.
마우스 버튼을 떼는 순간 발생한다.
마우스 포인터가 요소 위에서 움직일때만 발생한다.
마우스 휠을 위아래로 움직일때 발생한다.
요소에서 마우스 오른쪽 버튼을 클릭할 때 발생한다.
 
  • 키보드 이벤트 (keyboard Event)
const input = document.querySelector('input'); const result = document.querySelector('div'); // 키보드 입력 이벤트 핸들러 등록 input.addEventListener('keyup', (event) => { result.textContent = '입력한 내용' + event.target.value; });
notion imagenotion image
키보드 이벤트 타입은 사용자가 키보드 입력 시 발생하는 이벤트로 사용자가 다양한 키보드 상호작용을 처리하는 데 사용된다. 위 예제는 사용자가 내용을 입력란(input)에 입력하면 동시에 그 내용이 결과란(div)에 표시된다. keyup 이벤트는 사용자가 키를 누르고 뗄 때 발생하며, 입력이 최신 정보로 업데이트된다. 주로 키 입력 처리, 키보드 단축키 해제 등에 쓰인다.
타입은 keydown, keypress 이벤트도 있다. 입력값에 사용할 수 있고, 사용 목적에 따라 선택할 수 있다.
차이점을 설명하면, keydown이벤트는 키를 누를 때마다 발생하기 때문에 입력 내용이 연속적으로 업데이트된다. keypress 이벤트는 키를 누르고, 문자를 입력할 때 발생하는데, 특정 키(shift, ctrl 등)에는 이벤트가 발생하지 않는다. 주로 일반적인 문자 입력을 감지하는 데 사용된다.
따라서 위 코드처럼 입력한 내용을 실시간으로 표시하는 것은 ‘keyup’ 이벤트를 활용하여 사용자가 키를 입력하고 떼는 순간의 입력 내용을 감지하고, 업데이트하기 위해 적절하게 사용된다.
 
  • 폼 이벤트 (form Event)
이벤트 타입
submit
reset
change
input
발생 이벤트
폼에서 제출 버튼 클릭할 때 발생하는 이벤트 폼 데이터의 유효성 검사 및 서버에 데이터를 전송할 수 있다.
리셋 버튼 클릭할 때 발생하는 이벤트로, 사용자가 데이터를 초기 상태로 되돌릴 때 사용된다.
폼 요소값이 변경될 때 발생하는 이벤트로, 주로 체크박스, 라디오 버튼, 드롭다운 메뉴 등 입력 요소에서 사용된다.
폼 요소값이 변경될 때 발생하는 이벤트로, 주로 텍스트 입력 필드에서 사용된다. 사용자가 글자 입력하거나 삭제할 때, 실시간으로 입력 내용을 처리하거나 유효성 검사하는데 사용된다.
 
  • 포커스 이벤트 (focus Event)
element.focus(): 요소에 포커스를 설정할 때 사용하는 이벤트
주로 키보드 이벤트, 다른 사용자 입력 이벤트 등을 수신할 수 있게 한다. 입력 필드나 버튼 등의 포커스를 변경해야 할 때 사용한다.
element.blur(): 요소로부터 포커스를 제거할 때 사용하는 이벤트
모달창을 닫거나, 다른 ui 요소에 포커스 이동할 때 발생한다. 키보드 입력 수신하지 않게 하고, 입력 필드의 유효성을 검사할 때 주로 사용된다.
 

17-2-3. HTML 속성과 JavaScript를 활용한 이벤트 핸들링

  • onclick 이벤트
<button class='btn' onclick='btnClick()'>click !</button> <script> function btnClick() { alert('Hello !'); }; </script>
해당 요소에 onclick 이벤트 핸들러 함수를 직접 정의하고, button 요소 클릭할 때 btnClick() 함수가 실행되어 alert 메시지가 표시된다. html 속성과 JavaScript를 활용한 이벤트 핸들링으로 가장 흔하게 사용되는 onclick 속성이다.
 
  • onchange 이벤트
입력 필드의 값이 변경되었을 때 발생하는 이벤트. 주로 input 요소와 함께 사용되며, 값의 변경을 감지하고 처리할 때 사용된다.
<input type='text' onchange='textChange()'> <script> const textChange = () => { const value = document.querySelector('input').value; console.log(value); }; </script>
해당 요소의 onchange 이벤트 함수를 정의하고, onchange 이벤트가 발생할 때마다 textChange 함수가 실행되어 input 요소의 현재 value(값)를 가져와서 콘솔에 출력한다.
이런 방식으로 입력 필드의 값을 모니터링하고, 변경될 때마다 원하는 작업을 수행할 수 있어 용이하게 쓰인다.
 
  • onsubmit 이벤트
폼(form)을 제출할 때 발생하는 이벤트. 주로 폼 유효성 검사 및 서버로 데이터 전송과 관련된 동작을 수행할 때 사용된다.
<form onsubmit='check()'> <input type='text'> <input type='submit'> </form>
form 요소에서 <input type='submit'> 클릭할 때 check() 함수를 호출한다. 필수 입력값, 올바르지 않은 데이터 형식 등을 검증할 수 있다.
 

17-3. 이벤트 객체와 정보 활용

17-3-1. 이벤트 타겟과 현재 요소 파악

  • 이벤트 객체의 ‘type’ 속성 event.type
이벤트 발생한 종류 또는 유형을 나타낸다.
const div = document.querySelector('div'); div.addEventListener('click',(event)=>{ console.log(event.type); // click });
 
  • 이벤트 객체의 ‘target’ 속성 event.target
이벤트가 실제로 발생한 요소를 가리킨다.
const div = document.querySelector('div'); div.addEventListener('click',(event)=>{ console.log(event.target); // <div></div> });
 
  • 이벤트 객체의 ‘currentTarget’ 속성 event.currentTarget
이벤트를 처리하는 요소를 가리킨다.
<div class='parent'> <p class='child'>안녕하세요.</p> </div> <script> const parent = document.querySelector('.parent') parent.addEventListener('click',(event)=>{ console.log(event.target); // ul요소 console.log(event.currentTarget); // 클릭한 li 요소 }); </script>
이벤트 핸들러에서 처리 중인 요소를 나타내고, 항상 현재 요소를 참조한다. 이 패턴은 부모 요소에서 이벤트를 한 번만 처리할 수 있는 장점이 있다.
‘target’과 차이점은 위 코드에서 클릭 이벤트가 발생하면, ‘currentTarget’은 항상 ul 요소를 출력하고, ‘target’은 ul 안에 li만 클릭하면, 실제로 클릭 된 li 요소를 출력한다.
사용자가 누른 키보드의 키에 해당하는 유니코드 값을 나타내는 이벤트 객체의 프로퍼티 keyCode 속성도 있는데, 더는 권장되진 않는다.
 

17-3-2. 기본 동작 제어와 이벤트 전파 이해

  • 해당 이벤트에 대한 기본동작을 실행하지 않는 방법 preventDefault() 메서드
<form action=''> <button type='submit' class='submit'>제출</button> </form> <script> const myForm = document.querySelector('form'); myForm.addEventListener('submit', (e)=>{ e.preventDefault(); }); </script>
form 태그에 기본동작은 submit(전송)으로 브라우저는 새로 고침이 된다. event에 preventDefault() 함수를 추가하여 새로 고침 동작을 막을 수 있다. 주로 해당 이벤트가 가진 기본 동작을 실행시키고 싶지 않을 때 preventDefault() 함수를 사용하여 기본동작을 제어할 수 있다.
 
  • 해당 이벤트가 상위 요소의 이벤트 전파를 막는 방법 stopPropagation() 메서드
리스트 항목을 클릭했을 때 하위 요소인 리스트 항목의 클릭 이벤트와 함께 상위 요소인 리스트 컨테이너의 클릭 이벤트가 모두 발생할 때, stopPropagation()을 사용하여 상위 요소의 클릭 이벤트가 발생하지 않도록 사용할 때 유용하다.
<div id='container'> <button id='myButto'>버튼 클릭</button> </div> <script> const button = document.getElementById('myButton'); const container = document.getElementById('container'); button.addEventListener('click', function(event) { alert('버튼이 클릭 되었습니다.'); event.stopPropagation(); // 이벤트 전파 중단 }); container.addEventListener('click', function() { alert('컨테이너가 클릭 되었습니다.'); }); </script>
stopPropagation()을 사용하면 버튼 클릭 이벤트만 처리되고, 컨테이너 클릭 이벤트는 발생하지 않는다. 이렇게 stopPropagation()을 사용하면 이벤트 버블링을 중단하고, 부모 요소로의 이벤트 전파를 막는 데 사용된다.
 

17-4. 이벤트 위임 패턴

17-4-1. 이벤트 위임 패턴 개념 및 장점

이벤트 위임(Event Delegation)은 효율적인 이벤트 관리 디자인 패턴으로, 다수의 하위 요소에 개별 이벤트 핸들러를 등록하는 대신 상위 요소에 하나만 등록하고, 이벤트가 상위 요소에서 버블링되어 발생한 요소를 처리한다.
이벤트 위임 패턴은 다음과 같은 장점이 있다.
  1. 메모리 효율성: 각 하위 요소에 이벤트 핸들러를 등록하는 대신 상위 요소에 하나의 이벤트 핸들러만 등록하므로 메모리 사용량을 줄일 수 있다.
  1. 유지보수 용이: 동적으로 추가되거나 제거되는 요소에 대해서도 이벤트를 처리할 수 있다. 이벤트 위임을 사용하면 새로운 요소가 나타날 때마다 핸들러를 다시 등록할 필요가 없이 자동으로 적용되어 유지보수가 편리하다.
  1. 단순한 코드 구조: 함수를 사용하여 많은 하위 요소에 대한 복잡한 이벤트 핸들러 관리 코드를 간결하고, 단순화할 수 있다.
  1. 브라우저 호환성: 다양한 브라우저(Chrome, Firefox, Safari, Internet Explorer, Edge 등)는 각각 고유한 이벤트 모델을 가지고 있어 이 때문에 이벤트 처리 코드가 각 브라우저에 따라 다를 수 있다. 이벤트 위임을 사용하면 같은 코드로 다양한 브라우저에서 일관된 동작을 보장할 수 있다.
 

17-4-2. 이벤트 버블링과 캡처링

notion imagenotion image
  • 이벤트 버블링
이벤트 버블링은 브라우저에서 기본 동작으로, 이벤트가 발생하면 하위요소에서 상위요소로 전파되는 흐름을 이벤트 버블링이라고 한다.
<ul id='itemList'> <li>리스트 1</li> <li>리스트 2</li> <li>리스트 3</li> <li>리스트 4</li> <li>리스트 5</li> </ul>
const itemList = document.getElementById('itemList'); // 목록 리스트 클릭 이벤트를 처리하는 함수 function handleItemClick(event) { const clickedItemText = event.target.textContent; console.log('클릭 된 항목' + clickedItemText); }; // 목록 컨테이너에 클릭 이벤트 핸들러를 등록 itemList.addEventListener('click', handleItemClick);
클릭 된 항목의 텍스트를 가져와서 콘솔에 출력하는 함수를 만들고, 클릭 이벤트 핸들러를 등록한다.
사용자가 itemList 요소에서 클릭하면 이벤트는 가장 하위의 요소부터 시작하여 상위로 버블링되며, 이 과정에서 각 요소에 등록된 클릭 이벤트 핸들러가 실행된다. 따라서 클릭 된 항목의 텍스트를 가져와서 콘솔에 출력하는 handleItemClick 함수가 실행된다.
이렇게 하면 버블링을 효과적으로 활용하여 여러 하위 요소에서 발생하는 이벤트를 효율적으로 관리할 수 있다. 리스트가 계속 추가되더라도 각각의 이벤트 핸들러를 개별적으로 등록할 필요가 없으므로 코드 관리가 간편해진다. 하나의 이벤트 리스너를 사용하기 때문에 초기 로딩 시간도 단축되고, 메모리 사용량이 줄어들어 실무에서 자주 사용되는 자바스크립트 패턴 중 하나로 볼 수 있다.
 
  • 이벤트 캡처링
이벤트 캡처링은 버블링과는 반대 동작으로, 이벤트가 발생하면 상위요소에서 하위요소로 전파되는 이벤트 처리이다. 이벤트 캡처링은 일반적으로 세 단계 프로세스로 진행된다.
  • 캡처 단계 (Capture Phase): 이벤트가 최상위(루트) 요소에서 시작하여 타겟 요소까지 이벤트 캡처 단계 동안 이벤트 핸들러를 호출한다.
  • 타겟 단계 (Target Phase): 이벤트가 실제 타겟 요소에서 발생하고 이벤트 핸들러를 호출한다.
  • 버블링 단계 (Bubbling Phase): 이벤트가 타겟 요소에서 시작하여 최상위(루트) 요소까지 버블링 단계 동안 이벤트 핸들러를 호출한다.
이벤트 캡처링을 사용하려면 addEventListener() 메서드의 세 번째 매개변수를 true로 설정해야 한다. 이렇게 하면 이벤트 핸들러가 캡처 단계에서 호출된다.
element.addEventListener('click', myFunction, true); // true는 캡처링을 활성화
특히 이벤트가 하위 요소로 전파되기 전에 상위 요소에서 미리 처리해야 하는 상황에 유용하다. 예를 들어, 폼 요소에서 발생한 이벤트를 폼 요소가 아닌 상위 요소에서 먼저 처리하여 유효성 검사 등을 수행할 수 있다.
요약하면, 이벤트 캡처링은 이벤트가 상위 요소에서 하위 요소로 전파되는 방향으로 이벤트를 처리하고, 캡처링 단계에서 이벤트를 가로채기 위해 사용된다. 자주 쓰이지는 않는다.