Event
이벤트는 말 그대로 어떤 사건을 의미합니다. 브라우저에서의 이벤트란 예를 들어 사용자가 버튼을 클릭했을 때, 웹페이지가 로드되었을 때와 같은 것인데 이것은 DOM요소와 관련이 있습니다.
이벤트가 발생하면 누군가 이를 감지할 수 있어야 하며 그에 대응하는 처리를 호출해 주어야 합니다. 이를 위해 이벤트는 일반적으로 함수에 연결되며 그 함수는 이벤트가 발생하기전에는 실행되지 않다가 발생되면 실행됩니다. 이 함수를 이벤트 핸들러라고 하며 이벤트에 대응하는 처리를 기술합니다.
이벤트 루프와 동시성
브라우저는 단일 쓰레드에서 event-driven 방식으로 동작합니다.
단일 쓰레드는 쓰레드가 하나뿐이라는 의미이고 이말은 곧 하나의 작업만을 처리할 수 있다는 것입니다. 하지만 실제로 웹 애플리케이션은 많은 task가 동시에 처리되는 것 처럼 느껴지는데 이러한 동시성을 지원하는 것이 바로 이벤트 루프입니다.
자바스크립트 엔진은 크게 2개의 영역으로 나뉩니다.
- Call Stack(호출 스택) : 작업이 요청되면 요청된 작업은 순차적으로 스택에 쌓이게 되고 순차적으로 실행됩니다. 단 하나의 스택을 사용하기 때문에 해당 태스크가 종료되기 전까지 다른 태스크는 수행될 수 없습니다.
- Heap : 동적으로 생성된 객체 인스턴스가 할당되는 영역입니다.
자바스크립트 엔진은 작업이 요청되면 요청된 작업을 순차적으로 실행하는 것입니다. 앞에서 언급한 동시성을 위한 비동기 요청 처리는 자바스크립트 엔진을 구동하는 환경 즉 브라우저(or Node.js)가 담당합니다.
- Event Queue(Task Queue) : 비동기 처리 함수의 콜백 함수, 비동기 이벤트 핸들러, Timer 함수(setTimeout(), setInterval())의 콜백 함수가 보관되는 영역으로 이벤트 루프에 의해 특정 시점에 순차적으로 콜 스텍으로 이동되어 실행됩니다.
- Event loop : 콜 스택내에서 현재 실행중인 task가 있는지 그리고 Event Queue에 task가 있는지 반복하여 확인합니다. 만약 비어있다면 task를 이동하여 실행합니다.
아래의 예제로 한번 살펴보겠습니다.
function func1() {
console.log('func1');
func2();
}
function func2() {
setTimeout(function () {
console.log('func2');
}, 0);
func3();
}
function func3() {
console.log('func3');
}
func1();
함수 func1이 호출되면 func1은 call stack에 쌓입니다. 그리고 func2 함수를 호출하므로 func2가 스택에 쌓이고 setTimeout가 호출됩니다. setTimeout의 콜백함수는 즉시 실행되지 않고 지정 대기 시간만큼 기다리다가 "tick" 이벤트가 발생하면 테스트 큐로 이동한 후 call stack이 비어졌을 때 이동되어 실행됩니다.
DOM 이벤트 핸들러도 이와 같이 동작합니다.
function func1() {
console.log('func1');
func2();
}
function func2() {
// <button class="foo">foo</button>
const elem = document.querySelector('.foo');
elem.addEventListener('click', function () {
this.style.backgroundColor = 'indigo';
console.log('func2');
});
func3();
}
function func3() {
console.log('func3');
}
func1();
func1이 호출되면 똑같이 call stack에 쌓입니다. 그리고 func1이 func2를 호출하므로 func2도 똑같이 call stack에 쌓이고 addEventListener가 호출됩니다. addEventListener의 콜백함수는 foo 버튼이 클릭이 되어 click 이벤트가 발생하면 테스트 큐로 이동한 후 call stack이 비어졌을 때 call stack으로 이동되어 실행됩니다.
이벤트 종류
UI Event
Event | Description |
---|---|
load | 웹페이지의 로드가 완료되었을 때 |
unload | 웹페이지가 언로드될 때(주로 새로운 페이지를 요청한 경우) |
error | 브라우저가 자바스크립트 오류를 만났거나 요청한 자원이 존재하지 않는 경우 |
resize | 브라우저 창의 크기를 조절했을 때 |
scroll | 사용자가 페이지를 위아래로 스크롤할 때 |
select | 텍스트를 선택했을 때 |
Keyboard Event
Event | Description |
---|---|
keydown | 키를 누르고 있을 때 |
keyup | 누르고 있던 키를 뗄 때 |
keypress | 키를 누르고 뗏을 때 |
Mouse Event
Event | Description |
---|---|
click | 마우스 버튼을 클릭했을 때 |
dbclick | 마우스 버튼을 더블 클릭했을 때 |
mousedown | 마우스 버튼을 누르고 있을 때 |
mouseup | 누르고 있던 마우스 버튼을 뗄 때 |
mousemove | 마우스를 움직일 때 (터치스크린에서 동작하지 않는다) |
mouseover | 마우스를 요소 위로 움직였을 때 (터치스크린에서 동작하지 않는다) |
mouseout | 마우스를 요소 밖으로 움직였을 때 (터치스크린에서 동작하지 않는다) |
Focus Event
Event | Description |
---|---|
focus/focusin | 요소가 포커스를 얻었을 때 |
blur/foucusout | 요소가 포커스를 잃었을 때 |
Form Event
Event | Description |
---|---|
input | input 또는 textarea 요소의 값이 변경되었을 때 |
contenteditable 어트리뷰트를 가진 요소의 값이 변경되었을 때 | |
change | select box, checkbox, radio button의 상태가 변경되었을 때 |
submit | form을 submit할 때 (버튼 또는 키) |
reset | reset 버튼을 클릭할 때 (최근에는 사용 안함) |
Clipboard Event
Event | Description |
---|---|
cut | 콘텐츠를 잘라내기할 때 |
copy | 콘텐츠를 복사할 때 |
paste | 콘텐츠를 붙여넣기할 때 |
이벤트 핸들러 등록
이벤트 핸들러를 이벤트에 등록하는 방법은 3가지가 있습니다.
인라인 이벤트 핸들러 방식
<!DOCTYPE html>
<html>
<body>
<button onclick="myHandler()">Click me</button>
<script>
function myHandler() {
alert('Button clicked!');
}
</script>
</body>
</html>
인라인 이벤트 핸들러는 현재는 잘 쓰지 않는다고 합니다. 이유를 찾아보니 HTML과 자바스크립트가 분리되지 않는 문제가 생겨서 코드가 길어질수록 가독성이 떨어지고 문제가 생길 확률이 높아지기 때문이라고합니다.
하지만 Angular/React/Vue.js와 같은 프레임워크/라이브러리에서는 인라인 이벤트 핸들러 방식으로 이벤트를 처리합니다.
주의할 것은 onclick과 같이 on으로 시작하는 이벤트 어트리뷰트의 값으로 함수 호출을 전달한다는 것입니다.
위 예제의 경우, button 요소의 onclick 프로퍼티에 함수 function onclick(event) { foo(); }
가 할당됩니다.
이벤트 핸들러 프로퍼티 방식
인라인 이벤트 핸들러 방식처럼 HTML과 Javascript가 뒤섞이는 문제는 해결할 수 있는 방식입니다. 하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만을 바인딩할 수 있다는 단점이 있습니다.
<!DOCTYPE html>
<html>
<body>
<button class="btn">Click me</button>
<script>
const btn = document.querySelector('.btn');
// 이벤트 핸들러 프로퍼티 방식은 이벤트에 하나의 이벤트 핸들러만을 바인딩할 수 있다
// 첫번째 바인딩된 이벤트 핸들러 => 실행되지 않는다.
btn.onclick = function () {
alert('① Button clicked 1');
};
// 두번째 바인딩된 이벤트 핸들러
btn.onclick = function () {
alert('① Button clicked 2');
};
// addEventListener 메소드 방식
// 첫번째 바인딩된 이벤트 핸들러
btn.addEventListener('click', function () {
alert('② Button clicked 1');
});
// 두번째 바인딩된 이벤트 핸들러
btn.addEventListener('click', function () {
alert('② Button clicked 2');
});
</script>
</body>
</html>
addEventListener 메소드 방식
addEventListener
메소드를 이용하여 대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 지정합니다.
addEventListener 방식은 이전 방식에 비해 아래와 같은 장점이 있습니다.
- 하나의 이벤트에 대해 하나 이상의 이벤트 핸들러를 추가할 수 있습니다.
- 캡처링과 버블링을 지원합니다
- HTML 요소뿐만아니라 모든 DOM 요소(HTML, XML, SVG)에 대해 동작합니다. 브라우저는 웹 문서를 로드한 후 파싱하여 DOM을 생성합니다.
addEventListener 메소드는 IE 9이상에서 동작하고 IE 8 이하에서는 attachEvent 이벤트를 사용합니다.
이벤트 핸들러 함수 내부의 this
인라인 이벤트 핸들러 방식
인라인 이벤트 핸들러 방식의 경우, 일반 함수로서 호출되므로 전역 객체 window를 가리킵니다.
이벤트 핸들러 프로퍼티 방식
이벤트 핸들러는 메소드이므로 이벤트 핸들러 내부의 this는 이벤트에 바인딩된 요소를 가리킵니다.
addEventListener 메소드 방식
이벤트 핸들러는 콜백 함수이지만 이벤트 핸들러 내부의 this는 이벤트 리스너에 바인딩된 요소를 가리킵니다.
마치며
이번 포스팅은 여기까지 하고 다음 포스팅은 이벤트 흐름부터 다시 해보겠습니다.
이벤트가 중요해서 생각보다 정리해야할 양이 많네요.
'프론트엔드 스터디 > Javascript' 카테고리의 다른 글
RxJS 정리 (0) | 2021.07.27 |
---|---|
프로미스(Promise)란? (0) | 2021.07.16 |
자바스크립트에서 객체지향 프로그래밍이란? (0) | 2021.07.14 |
자바스크립트 core 개념 간단 정리 (0) | 2021.07.13 |
클로저(closure)란??? (0) | 2021.07.13 |