[자바스크립트] 이벤트의 동작 원리
자바스크립트로 개발을 좀 해본사람이라면 누구나 이벤트를 다뤄봤을 것이다. 소위 말하는 프론트엔드, 즉 화면을 만든다고 치자. 그러면 단지 눈에 보이는 화면만을 만들어내는게 아니라 그 위에서 일어날 '동작'을 구현해야한다. 어떤 버튼을 눌렀을때 화면에 alert 메세지가 뜨게끔 한다던지 말이다. 화면에서 버튼을 누를 때 발생하는 click 이벤트를 비롯하여 자바스크립트 개발 과정은 이벤트 핸들링의 연속이다. 따라서 확실히 정리하고 넘어가야할 내용인 것이다.
이벤트란
이벤트란 사건이다. 사용자가 특정 버튼을 클릭을 할때, 키보드로 키를 입력할때, 마우스를 화면의 특정 요소에 끌어다 놓을 때를 생각해보자. 이는 특정한 동작이 벌어지는 '사건'이 일어난 것이다. 이 사건을 일으키는 주체는 반드시 사용자일 필요는 없다. 관점에 따라서 브라우저가 될 수도 있다. 예컨대 브라우저의 크기가 줄어들었을 때, 페이지의 로딩이 끝났을 때도 특정한 사건이 벌어진 것이다.
웹페이지에서 일어나는 이러한 사건을 이벤트라고 한다. 고로 이벤트를 처리한다는 것은 이러한 의미이다. 'A사건이 일어났을 때는 B를 수행해!'
자바스크립트 개발자는 각각의 이벤트를 처리하는 함수를 구현하고 이 함수를 해당 이벤트에 연결(바인딩)하는 작업을 해야하는 것이다. 다음과 같이 말이다.
HTML 문서 내의 버튼에 event listener를 연결했다. addEventListener라는 함수에 전달되는 파라미터들만 보더라도 의미를 쉽게 파악할 수 있다. 이런 뜻이다. '자 Listener야, 이제부터 내가 너에게 역할을 줄게. 너는 exam이라는 버튼에서 클릭 이벤트가 발생하는지 잘 관찰하고 있어. 만약 클릭 이벤트가 발생하면 당황하지 말고 showMessage 함수를 호출하면 돼! 쉽지?'
여기까지는 직관적이다. 그런데 맨 마지막에 true로 전달된 세번째 파라미터의 의미는 무엇일까.
이벤트 캡처링과 버블링
addEventListener에 넘긴 세번째 파라미터의 의미는 이벤트 캡처링과 버블링이라는 개념과 연결된다. 결론부터 말하자면 이 값이 true냐 false냐에 따라서 이벤트의 전파 방향이 결정된다.
HTML 문서에 javascript같은 프로그래밍 언어를 통해 접근할 수 있는 인터페이스를 제공해주는 것이 바로 Document Object Model(DOM) 이라는 개념이다. 그런데 이 DOM은 트리 구조로 되어있다. 즉, button을 포함하는 div가 있고, 그 위에는 또 BODY가 있고 끝에는 결국 HTML 문서서 자체가 있다. 우리가 버튼을 클릭해서 이벤트가 발생하면, 이 이벤트는 버튼을 감싸고 있는 상위 객체로 혹은 버튼 내에 속해있는 하위 객체로 전파된다.
❙ 이벤트 캡처링(Event Capturing)
여기서, 그림과 같이 하위 레벨로 이벤트가 전파되는 것을 이벤트 캡처링이라고 한다. 즉, DOM 트리를 따라서 부모 -> 자식 방향으로 이벤트가 전파된다.
addEventListener 함수의 세번째 파라미터로 true를 넘겨주었다. 이는 event caturing 방식으로 이벤트를 전파시키겠다는 의미이다. 이제 가장 안쪽 요소인 span을 클릭하여 실행 결과가 어떻게 되는지 보자.
초록색 영역(span)을 클릭했을 뿐인데 div나 p 요소에 연결된 이벤트 함수도 함께 동작해버렸다. 그런데 여기서 우리는 그 순서에 더 주목할 필요가 있다. 초록색 영역을 클릭했는데 가장 바깥 영역의 이벤트가 먼저 동작했다. 이것이 이벤트 캡처링의 동작 방식이다. 클릭한 영역을 감싸는 가장 바깥쪽 요소부터 안쪽 요소의 방향으로 이벤트가 전파된다. 즉 이런 식이다. '나 div인데! 지금 클릭 이벤트 발생했어! p야 너도 이벤트 함수 수행하고 자식들에게 쭉 알려!'
❙ 이벤트 버블링(Event Bubbling)
이벤트 버블링은 캡처링과 반대의 개념이다. 특정 요소에서 발생한 이벤트가 DOM 트리를 타고 쭉 위로 전파되는 것이다. 마찬가지로 예제를 통해 살펴보자.
바뀐 것은 하나다. addEventListener 함수의 세번째 파라미터를 false로 바꿔주었다. 이는 이벤트 전파 방식을 버블링 방식으로 바꾼다는 의미이다. 역시 가장 안쪽의 span 요소를 클릭해보자.
바뀐게 없는 것 같다. 하지만 자세히 보면 있다. 출력 순서가 바뀌었다. span 클릭에 바인딩된 함수가 먼저, div 클릭이 가장 나중에 동작한다. 이는 이벤트가 발생한 요소로부터 부모 요소의 방향으로 이벤트의 발생을 전파하는 것이다.
❙ 이벤트 전파의 취소
그런데 굳이 이벤트가 전파되어야 하는 이유는 무엇일까. 브라우저가 이벤트의 발생 의도를 알지 못하기 때문이다. 버블링 예제의 박스 그림을 보자. 그리고 주황색 영역이 사용자가 클릭하고 싶었던 버튼이라고 생각해보자. 그러면 사용자가 주황색 영역 내의 어디를 클릭하든 브라우저는 사용자가 의도한, 주황색 버튼에 연결된 이벤트 함수를 수행해야 하는 것이다. 때문에 브라우저는 사용자가 초록색 영역을 클릭했을 때도 이를 감싸고 있는 영역에 이벤트를 전파시킨다. 결국 '사용자가 클릭하려고 의도한게 뭐지? 이건가? 이건가?' 하면서 전부 다 수행해보는 것이다.
물론 이러한 전파를 명시적으로 취소하는 방법 또한 있다. 다음과 같은 함수들을 사용하면 된다.
마무리하며
하지만 그동안 자바스크립트 개발을 해오면서도 근본적인 원리에 대해 궁금해하지 않았다. 그러던 중, 누군가가 질문을 해왔다. "자바스크립트에서 이벤트가 처리되는 원리가 어떻게 되나요?" 그리고 이 질문에 쉽사리 답하지 못하는 나 자신을 발견했다. 이래놓고선 내가 자바스크립트 개발을 한다고 말할 수 있겠는가. 하지만 역시 같은 처지인 동지들이 많을 것이라 생각하여 이번 기회에 관련 내용을 한번 정리해보았다. 늘 원리를 탐구하고 기본에 충실한 개발자가 되도록 하자.
참고
http://tcpschool.com/javascript/js_event_eventListenerCall
https://opentutorials.org/course/1375/6768
http://webclub.tistory.com/186