React와 Vue의 이벤트 바인딩 방식은 왜 다를까?
목차
· React의 이벤트 바인딩 방식
· React에서는 왜 () => handleClick(id) 형태로 선언해야해요?
· Vue의 이벤트 처리 방식
· Vue에서는 왜 handleClick(id) 형태로 선언해야해요?
· Vue는 HTML에 이벤트 바인딩이랑 상당히 유사한데, 어떤 차이가 있어요?
· JavaScript 코드에서의 element.onclick = handleClick()
· 글을 마치며
회사에서 업무를 진행하다가 문득 React에서 이벤트 바인딩 방식과 Vue의 템플릿에서의 이벤트 바인딩 방식이 다르다는 점에서, 어떤 방식으로 이벤트가 적용이 되고 왜 바인딩 방식이 다른지 궁금해졌어요. 우리가 흔히 아는 React와 Vue의 차이점은 React의 경우 JSX를 이용해 프로그래밍적으로 명확하고 제어 가능한 로직을 제공하려고 하며, Vue는 템플릿 문법의 간결함과 직관성을 제공하려고 한다는 점이에요. 두 프레임워크를 사용해봤다면 문법 상의 명확한 차이는 알 수 있겠지만, 당연히 사용하고 있던 바인딩 방식에 대해 궁금하신 적 없으셨나요?
React의 이벤트 바인딩 방식
React는 JSX를 JavaScript 함수로 해석해서 처리하기 때문에 이벤트 핸들러에 인자를 넘기고 싶을 때 직접 새로운 함수를 만들어서 전달해요.
<button onClick={() => handleClick(id)}> Click ! </button>
만약 onClick={handleClik(id)}와 같이 작성을 하게 되면 렌더링 시에 handleClick(id)가 즉시 실행이 되어버려요. 다른 프레임워크를 다뤄보지 않고 React 만을 사용해봤다면 이 예제가 당연하게 느껴지실 수 있겠지만 Vue와 혼동해서 사용을 하게 되면 "왜 안될까?" 라는 생각이 들 수 있답니다. 우리가 예제로 정의한 handleClick(id) 함수가 렌더링 시점에서 즉시 실행을 하지 않도록 해야하기 때문에 함수의 참조만을 넘겨야하며 () => handleClick(id)와 같은 익명 함수를 만들어서 전달하는 방식으로 사용을 해야해요.
<button onClick={handleClick}> Click ! </button> // OK
<button onClick={handleClick(id)}> Click ! </button> // NOT OK (함수가 즉시 실행되어버림)
<button onClick={() => handleClick(id)}> Click ! </button> // OK
React에서는 왜 () => handleClick(id) 형태로 선언해야해요?
JSX는 아래와 같은 과정을 통해 우리가 보고있는 자바스크립트 코드를 내부적으로 변환하는 과정을 진행하게 되어요.
// JSX
<button onClick={() => handleClick(id)} />
// 변환된 JavaScript
React.createElement("button", { onClick: () => handleClick(id) }, "Click"};
앞서 설명한 것처럼 Vue와 달리 React에서는 onClick={handleClick(id)}와 같이 선언을 하게 되면 handleClick(id)는 컴포넌트 렌더링 시점에서 바로 실행이 되어요. 반면 onClick={() => handleClick(id)}는 클릭 시점에서 함수가 실행되는 구조이기 때문에 비교적 안전하게 사용을 할 수 있습니다. 이 부분들은 JavaScript 함수 실행과 참조의 차이 때문에 발생하게 되며 이렇게 정리할 수 있을 것 같아요.
· handleClick(id) 형태만으로 바인딩을 하게 되면 함수 실행 결과를 의미해요
· () => handleClick(id) 형태로 바인딩을 하게 되면 함수 자체를 의미하며, 함수의 참조 값을 의미해요.
React는 모든 UI를 함수 기반으로 선언적으로 표현하는 특징을 가지고 있어요. 렌더링 또한 모두 JavaScript 함수이기 때문에 컴포넌트에서 사용하는 모든 로직은 명확한 자바스크립트 구문이여야해요. 따라 이벤트 핸들러에서 추가 로직이 있다면 명시적으로 함수를 래핑하는 과정을 진행해야합니다.
Vue의 이벤트 처리 방식
Vue의 경우 템플릿을 먼저 파싱한 뒤에 내부적으로 with 문과 Proxy 등을 이용해 템플릿 내부 표현식을 하나의 "문자열 코드 블록" 처럼 해석해요.
<template>
<button @click="handleClick(id)"> Click ! </button>
</template>
여기서 handleClick(id)는 문자열이 아니라, 템플릿 컴파일러가 아래와 같이 내부 코드로 변환하는 과정을 진행해요. 아래 코드상에 노출되는 $event는 네이티트 이벤트 객체를 의미하는 값이에요. 또한 Vue가 대신 () => handleClick(id)를 만들어주는 셈이라고 볼 수 있어요.
function ($event) {
return handleClick(id);
}
Vue에서는 왜 handleClick(id) 형태로 선언해야해요?
Vue 템플릿 문법에서는 이벤트를 다음과 같이 바인딩을 해요.
<button @click="handleClick"> Click ! </button> // OK
<button @click="handleClick(id)" Click ! </button> // OK
Vue는 위 코드들을 아래와 같이 컴파일을 진행하여 코드를 내부적으로 변환하는 과정을 진행해요.
// 가상의 컴파일 결과
render() {
return h('button', {
onClick: ($event) => this.handleClick(id)
}, 'Click' ),
}
Vue 템플릿은 단순한 HTML처럼 보이지만, 내부적으로 JavaScript 표현식을 자동으로 함수로 래핑해요. @click="handleClick(id)" 라고만 작성을 해도 Vue는 내부적으로 위에서 설명을 했던 것처럼 $event 라는 네이티브 이벤트 객체를 가지고 익명함수를 만들어 처리해요. Vue는 템플릿 문법 기반 접근을 취하며, 템플릿 문법이 간결하고 직관적이어야한다는 철학을 가지고 있어요. 그래서 HTML, CSS, JS 만을 가지고 바닐라스럽게 코딩을 하는 느낌을 주고, 선언적이지만 HTML-like 문법으로 로직을 간결하게 표현하고자 하며 로직을 명시적으로 작성하지 않아도 Vue가 알아서 처리해주는 편의성을 제공해요.
Vue는 HTML에 이벤트 바인딩이랑 상당히 유사한데, 어떤 차이가 있어요?
Vue의 템플릿 안 구조는 HTML의 이벤트를 바인딩하는 방식이나 상당히 유사한 부분들이 많아요. Vue에서 handleClick(id) 형태로 이벤트 바인딩을 진행했는데, HTML도 동일하게 이벤트 바인딩을 했던 기억이 있어 이 두 가지의 차이점도 조금 알아보고자 했어요.
<button onclick="handleClick()"> Click ! </button>
<script>
function handleCLick() {
alert("clicked");
}
</script>
위 코드는 바인딩한 이벤트 함수가 즉시실행되는 구조가 아니라 클릭 시점에서 정상적으로 작동을 하는 코드예요. onclick 안의 "handleClick()"은 문자열이지만 즉시 실행을 하지 않는 이유는 HTML 파서가 해당 문자열을 클릭 실행할 JavaScript 코드라고 인식하기 때문이에요. 즉 new Function("handleClick()") 처럼 동적으로 함수처럼 실행되기 때문에 렌더링 시점에서 실행되지 않고, 클릭시에 정상적으로 실행이 되는 과정을 거쳐요. 위의 함수는 내부적으로 아래와 같이 변환을 해줘 정상적으로 실행되어요.
element.onclick = function(event) {
handleClick();
}
JavaScript 코드에서의 element.onclick = handleClick()
자바스크립트 안에서의 onclick을 등록할 때 당연한 이야기지만, 혹여나 포스팅을 보시는 분들을 위해 해당 섹션도 같이 추가를 했어요.
const btn = document.getElemenyById("btn");
btn.onclick = handleClick() // 즉시 실행 되어버림
여기서 JavaScript 코드에서 보이는 handleClick()은 말그대로 함수를 호출하는 부분이기에 즉시 실행되어 그 리턴 값이 이벤트 핸들러로 등록이 되어요. 보통은 관련 값이 undefined 이기 때문에 클릭 시 아무 일도 생기지 않거나, 오류를 발생시켜요. 정상적으로 동작을 위해서는 위 코드를 아래와 같이 함수의 참조 값을 전달하는 방식으로 변경을 해야해요.
btn.onclick = handleClick // 함수의 참조 값을 전달
글을 마치며
위에서 본 내용을 기반으로 "이벤트 핸들러에 어떻게 접근을 해야하는가"와 같은 다양한 예제를 통해 함수의 참조를 어떻게 할 수 있는가에 대한 판단을 할 수 있어요. React는 HTML · JS 등을 JSX 문법으로 관리하고 있고, 자바스크립트 베이스에서 이 모든게 이루어져있기 때문에 JavaScript의 규칙을 따라야하며, Vue의 경우에는 HTML 템플릿 문법을 사용하고 있지만 내부적으로 자바스크립트 표현식을 자동으로 함수로 래핑을 진행하기 때문에 HTML 자체의 편의성을 제공해요. 사실 회사에서는 Vue를 많이 사용하다보니 React로 사이드 프로젝트를 진행하곤 하면 이벤트 바인딩을 혼동하곤했는데, 이번 기회에 다시 한번 이 개념을 복습해 볼 수 있어 너무 좋은 기회였습니다 :-)