이벤트 드리븐 프로그래밍은 “프로그램의 흐름을 이벤트 중심으로 제어하는 프로그래밍 방식”이다.
브라우저에서 돌아가는 프로그램의 구성 요소(EventTarget
)는 “특정 타입” 에 대한 이벤트에 대해 “이벤트 핸들러”를 브라우저에 등록하고, 그 이벤트가 발생하면 브라우저로 하여금 이벤트 핸들러를 호출하도록 한다.
이러한 맥락에서 이벤트는 발생한 사건(occurance)의 성격을 가진다. 하지만 이벤트 드리븐 프로그래밍에서의 이벤트는 단순히 발생한 사건만을 의미하지는 않는다. 때로 이벤트는 무언가를 하도록 한다는 행위(action)으로서의 성격을 가질 수 있다. 자바스크립트 딥다이브 40장을 공부하면서 이벤트의 두 가지 성격에 대해 구분해서 생각하면 좋을 것 같아 이 글을 작성하게 되었다.
사건으로서의 이벤트 - occurrence
DOM 스펙에서는 이벤트를 다음과 같이 정의하고 있다.
- Interface Event: Event 객체를 이벤트라고 합니다. 이벤트 객체는 무언가가 발생했음을 알려줄 수 있습니다.
- Action versus occurrence: 이벤트는 행동이 아닌 사건을 의미합니다. 다르게 표현하자면, 이벤트는 어떤 알고리즘으로부터 받은 알림이며, 다음 알고리즘 과정에 영향을 주는데 사용할 수 있습니다.
사건으로서의 이벤트는 프로그램의 실행 과정에서 발생한 특정 순간, 또는 그 순간에 나타났던 상태의 변화를 나타낸다. 이벤트는 단순히 ‘무언가가 일어났다’는 사실을 알리는 통지의 역할을 한다. DOM 스펙을 좀 더 이어나가보면 다소 강경한 문장이 있다.
- 이벤트는 어떤 액션이나 액션을 유발해서는 안됩니다. 이벤트는 오로지 진행 중인 행위에 영향을 미치는 데에만 사용될 수 있습니다.
- 이벤트는 어떤 알고리즘을 실행하게 만드는 행위나 시발점으로 사용되어서는 안됩니다. 그것은 이벤트의 본질적인 목적이 아닙니다.
첫 번째 문장은 이벤트의 수동적인 본질을 강조한다. 여기서 중요한 점은 “진행 중인” 이라는 워딩인데, 이는 이벤트가 이미 실행되고 있는 알고리즘의 흐름 속에서만 의미를 가진다는 걸 암시한다. 예를 들어 submit
이벤트가 발생할 때 이미 진행 중인 제출 프로세스를 중단(preventDefault
)할 수 있다는 것이다.
두 번째 문장은 이벤트가 만들어질 때 뭔가가 발생하면 안된다는 것이다. 무언가를 발생한 것이 완료가 되면 그 신호로 이벤트를 보내는 것이다. 이벤트를 보내는 통지 행위는 실제로 ‘완료된 상태’나 ‘이미 일어난 변화’를 알리는 신호다. 인풋의 change
이벤트는 값이 이미 변경된 후에 발생한다.
스펙대로 생각한다면 DOM에서 발생하는 모든 이벤트들(Custom Event 포함)은 시점이 과거형, 또는 현재 완료형이다. 이렇게 만든 이유는 무엇일까? 나의 생각이지만 이벤트 핸들러를 실제로 호출하는 주체가 브라우저여서 이런 제한을 두지 않았을까 싶다.
사실 프론트엔드 개발자는 HTML 상태에 직접적으로 접근할 수 없다. 브라우저가 DOM이라는 인터페이스를 만들어놓고 간접적으로 만나도록 할 뿐이다. 우리는 실제로 커서가 어디에 위치해야 하는지 조작할 수도 없고, Input에 값을 직접적으로 추가하지 못한다. 브라우저가 먼저 뭔가를 해야한다는 이러한 특성상 이벤트는 그 후의 결과를 통지하는 용도로만 쓰이지 않을까 생각한다.
사건에 대한 이벤트 핸들러는 이미 일어난 일에 대한 후속 조치를 담당한다. 이벤트 핸들러는 이 사건에 반응하는 동작을 정의하는 것이다. 이미 일어난 사건에 대해 우리는 다양한 대응을 할 수 있다. 그래서 이벤트 핸들러는 여러 개 등록할 수 있다는 맥락이 태어났다.
‘무언가가 일어났음’을 표현하는 이벤트는 리액트의 세계에서도 매우 친숙하다. 먼저 아주 먼 옛날(리액트가 0.14버전 대) 페이스북에서 소개한 Flux 패턴의 주요 컴포넌트인 action도 “무언가가 발생했음”을 나타낸다. 사실을 바탕으로 새로운 상태를 업데이트하고 그걸 보여주는 패러다임은 리덕스의 탄생으로까지 이어진다. 리덕스 공식 홈페이지에서는 action 객체를 무언가가 발생했다는 사실을 나타내는 객체로 표현한다. 어디 리덕스 뿐일까? 최신 버전의 리액트 공식 홈페이지에서 useReducer()
의 usage를 보면 “유저가 무언가를 했음” 의 사실을 action으로 취급한다.
행위로서의 이벤트 - action
이벤트는 사용자나 시스템의 의도적인 행동을 중심으로 생각해볼 수도 있다. 이는 앞서 살펴본 사건으로서의 이벤트와는 다른 관점이다. 행위로서의 이벤트는 “무언가를 하라”는 명령적 성격을 띠며, 특정한 목적을 달성하기 위한 의도적인 동작을 표현한다.
가장 대표적인 행위로서의 이벤트는 HTTP 요청이다. HTTP 메서드들을 보면 행위적 특성이 명백히 드러난다. 각 요청은 의도적인 행위를 나타내며, 행위를 수행하는 데 필요한 모든 정보를 포함한다. 그밖의 예시로 웹소켓이나 postMessage를 통한 통신을 들 수 있다. 이들은 단순히 상태 변화를 통지하는 것이 아니라, 수신자가 특정 작업을 수행하도록 하는 명확한 목적을 가진다.
행위로서의 이벤트는 사건으로서의 이벤트와는 다른 특징을 보인다.
1. 먼저 행위로서의 이벤트는 통작 수행에 필요한 파라미터를 같이 보낸다. 예를 들어 주문하기 이벤트는 주문할 상품 목록, 배송지 정보, 결제 정보 등 주문 실행에 필요한 데이터를 모두 가지고 있어야 한다. 이는 이벤트가 단순한 통지가 아닌, 실제로 수행되어야 할 작업을 명시하는 역할을 한다는 것을 보여준다.
2. 다음으로 행위로서의 이벤트는 주로 하나의 핸들러가 그 행위의 실행을 담당하게 된다. 행위로서의 **이벤트 핸들러는 이벤트가 요청한 작업을 ‘수행’**하는 역할을 하며, 이는 사건에 ‘반응’하는 것과는 다른 성격을 가진다.
3. 마지막으로 행위로서의 이벤트는 수행되어야 할 미래의 시점에 대한 맥락을 가지며, 사건으로서의 이벤트는 이미 발생한 과거의 시점을 가리킨다.
두 가지 관점은 각각의 맥락에서 의미있는 역할을 하며, 현대의 프론트엔드 아키텍처는 이 둘을 적절히 조화시켜 사용하고 있다.