이벤트 & 이벤트 핸들링
Last updated
Was this helpful?
Last updated
Was this helpful?
이벤트
브라우저에서의 이벤트란 예를 들어 사용자가 버튼을 클릭했을 때, 웹페이지가 로드되었을 때와 같은 것인데 이것은 DOM 요소와 관련이 있다.
이벤트가 발생하는 시점이나 순서를 사전에 인지할 수 없으므로 일반적인 제어 흐름과는 다른 접근 방식이 필요하다.
즉, 이벤트가 발생하면 누군가 이를 감지할 수 있어야 하며 그에 대응하는 처리를 호출해 주어야 한다.
브라우저는 이벤트를 감지할 수 있으며 이벤트 발생 시에는 통지해 준다. 이 과정을 통해 사용자와 웹페이지는 상호작용(Interaction)이 가능하게 된다.
이벤트 핸들러
특정 이벤트가 발생 시 실행되는 함수를 총칭한다.
이벤트 루프(Event Loop) 와 동시성(Concurrency)
브라우저는 single-thread 에서 event-driven 방식으로 동작한다.
하나의 작업은 하나의 스레드 안에서 이루어 지지만 자바스크립트는 Concurrency 를 지원하기 때문에 많은 작업이 동시에 처리되는 것처럼 느껴진다.
자바스크립트의 엔진
여러 종류의 자바스크립트 엔진이 존재하나 크게 다음 2가지 영역으로 나뉜다.
Call Stack (호출스택)
작업이 요청되면 순차적으로 이곳에 쌓이게 되며 순차적으로 수행된다.
오직 한개만 존재하고 사용하며, 처리하는 작업이 종료되기 전까지 다른 작업을 수행하지 않는다.
Heap
동적으로 생성된 객체 인스턴스가 할당되는 영역이다.
단순히 엔진은 Call Stack 을 활용하여 작업을 순차적으로 실행할 뿐이다.
동시성을 가지고 실행하기 위해서는 구동되는 환경, 브라우저, Node.js 가 처리한다.
Event Queue(혹은 Task Queue)
비동기 처리 함수의 콜백, 이벤트 핸들러, Timer 함수 등이 위치하는 영역이다.
Event Loop 에 의해서 Call Stack 이 비어졌을 때 순차적으로 할당되어 실행된다.
Event Loop(이벤트 루프)
Call Stack 내에서 작업이 있는지, Event Queue 에 작업이 있는지를 반복하며 실행하는 것을 이야기한다.
코드를 통해 어떻게 작동할 지 예측해보자
별도의 특별한 함수가 없다면 순서대로 실행이 되겠지만 그렇지가 않다.
비동기에 관련된 이벤트가 설명될때 자주 언급되는 setTimer() 이벤트는 다음과 같은 방식으로 실행된다.
평범한 함수의 경우에는 순서대로 Call Stack 에 쌓인다.
즉시 실행되지 않고, 타이머 객체의 실행 이벤트(tick)가 발생하면 Event Queue 에 적재된다.
(핵심!!) 그 이후에 Call Stack 이 전부 비워진 이후에 실행이 된다.
이벤트 핸들러 등록 방식
인라인 이벤트 핸들러 방식
DOM 요소에는 이벤트 핸들러에 관한 속성이 존재하지만 쓰지 않는 점이 좋다.
관심사가 다르고 자바스크립트의 이벤트가 DOM 요소에 종속적으로 두면 각 관심사 별로 집중이 안되어 분리해서 사용하곤 있다.
스크립트 코드 내에서 특정 DOM 의 프로퍼티에 해당 이벤트 핸들러 기술
이벤트 핸들러 프로퍼티 방식은 이벤트에 하나의 이벤트 핸들러만을 바인딩할 수 있다
여러 이벤트를 등록하기 위해서는 addEventListener(IE 8 이하 : attachEvent) 를 통해 등록해야한다.
eventTarget.addEventListener('eventType', functionName [, useCapture]
useCapture 는 기본이 false 이며 버블링을 허용하게된다.
이벤트의 흐름
버블링 (Bubbling)
엘리먼트에서 이벤트가 감지 되었을 때, 해당 엘리먼트를 포함하고 있는 부모 엘리먼트를 통하여 최상위 까지 이벤트가 전달되는 것을 버블링이라고 한다.
캡쳐링 (Capturing)
캡처링은 window 부터 최초 이벤트가 발생한 자식 요소로 내려가는 과정을 말한다.
주의할 것은 버블링과 캡처링은 둘 중에 하나만 발생하는 것이 아니라 캡처링부터 시작하여 버블링으로 종료한다는 것이다.
즉, 이벤트가 발생했을 때 캡처링과 버블링은 순차적으로 발생한다. IE8 버전에서 지원되지 않는다.
버튼을 클릭했다고 했을 때 다음 코드의 결과를 예상해보자.
body, button 요소는 버블링 이벤트 흐름만을 캐치하고 p 요소는 캡처링 이벤트 흐름만을 캐치한다. 따라서 button 에서 이벤트가 발생하면 먼저 캡처링이 발생하므로 p 요소의 이벤트 핸들러가 동작하고 그후 버블링이 발생하여 button, body 요소의 이벤트 핸들러가 동작한다.
누구나 코딩할때 위와같은 문제를 겪었을 것이고 신경쓰지 않고 해당 DOM 에만 집중하고 싶었을 것이다.
Jquery 를 사용하고 있다면 해당 이벤트 핸들러에 return 값으로 false 쥐어준다.
바닐라 자바스크립트의 경우는 다음의 절차를 따른다.
요소가 가지고 있는 기본 동작을 중단시키기 위한 메소드가 preventDefault()를 이용한다.
어느 한 요소를 이용하여 이벤트를 처리한 후 이벤트가 부모 요소로 이벤트가 전파되는 것을 중단시키기 위한 메소드 stopPropagation()를 이용한다.
비동기 처리와 콜백 함수
비동기 처리 예시 코드
ajax 통신
setTimeout
위 코드의 결과는 undefined 가 된다. 그 이유는 특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행이 됬기 때문이다.
해당 코드들을 해결하기위해 위와 같은 비동기 작업의 실행 이후 다음 실행해야 할 함수를 아규먼트로 쥐어주는데 이것이 바로 콜백 함수이다.
허나 위와같은 구조를 남발하여 "콜백의 콜백의 콜백" 을 남발하는 것을 콜백 지옥이라 불른다. 위와같은 구조가 되버리면 가독성과 유지보수성이 떨어진다.
하여 보통 분리를 하여 개선한다고 한다.
ES6 에서는 Promise, Async 등으로 이용하여 더 편하게 구현한다곤하나, 이 마저도 결국 좋은 방법은 아니라는 말이 있긴하다. 결국 이해하기 좋은 코드는 동기식 코드라는 말이 있었다. 무슨 말이었나 기억이 안났지만 개발자의 도움을 얻었다.