Event Handler

김동현·2021년 12월 8일
1

React

목록 보기
5/27
post-thumbnail
post-custom-banner

이벤트 핸들러 등록

리액트에서 이벤트를 처리하는 방법은 자바스크립트와 유사합니다. "html 태그 이름을 문자열"로 사용한 JSX 문법on 접두사이벤트 타입을 카멜 케이스로하는 어트리뷰트에 이벤트 핸들러를 작성하는 방법으로 이벤트를 캐치할 수 있습니다.

주의할 점으로는 "내장 HTML 태그"를 사용하는 JSX 문법에서만 사용할 수 있으며 어트리뷰트로 on 접두사와 이벤트 이름"카멜 케이스"로 작성해야 합니다(onChange, onFocus, onSubmit,,,).
만약 컴포넌트 함수로 JSX 문법을 사용한 경우에는 이벤트 핸들러가 등록되는 것이 아니라 단지 데이터를 전달하기 위한 props 객체의 프로퍼티로서 동작합니다.

값으로는 "함수(참조값)"를 전달해야 합니다(호출문이나 문자열이 아닌 함수 참조값을 전달). 만약 호출문을 전달하게 된다면 JSX 코드가 평가될 때 이벤트 핸들러가 평가되어 실행되기 때문입니다. 즉, 이벤트가 발생했을 때 실행하는 것이 아니라 JSX 코드가 평가되었을 때 실행되는 것입니다. 그러므로 함수 참조를 전달해주어야 합니다.

참고로 onChange는 기존 자바스크립트의 onchange처럼 포커스를 잃었을 때만 발생하는 것이 아니라 값이 변경될 때마다 발생됩니다. 즉, oninput처럼 동작합니다.
차이점으로는 onChange는 인풋 타입에 상관없이 모두 동작하지만 onInput의 경우에는 특정 인풋 타입에만 동작하기 때문에 주로 onChange를 사용합니다.

이벤트 핸들러의 this 키워드

리액트에서 이벤트 핸들러는 이벤트 발생시 브라우저가 "일반 함수"로서 호출합니다.

즉, 이벤트 핸들러 내부 this는 "전역 객체(window)"를 가리키게 됩니다.

function clickHandler() {
    console.log(this);  // -> window
}

<button onClick={clickHandler}></button>

clickHandler 이벤트 핸들러의 경우 버튼을 클릭시 브라우저에 의해 호출되는 함수이며, 일반 함수로서 호출됩니다. 즉, 이벤트 핸들러 내부 this에는 전역 객체가 바인딩되어 있습니다.


컴포넌트 클래스의 경우 아래처럼 이벤트 핸들러를 등록하면 문제가 발생합니다.

class MyComponent extends React.Component {
    clickHandler(event) {
        this; // -> undefined
        ,,,
    }

    render() {
        return (
            <button onClick={this.clickHandler}>
                click
            </button>
        );
    }
}

이때 clickHandler 내부에서 this를 참조하면 undefined가 반환됩니다. 그 이유는 이벤트 핸들러는 브라우저가 일반 함수로 호출되며, 클래스 내부에는 strict mode가 적용되어 일반 함수로 호출되는 함수 내부의 this는 undefined가 바인딩됩니다.

자바스크립트의 클래스로 예를 들어보겠습니다.

class MyClass {
    myFunc() {
        console.log(this);
    }
};

const myInstance = new MyClass();

const ref = myInstance.myFunc;

ref(); // undefined

ref를 호출하면 undefined가 콘솔에 출력됩니다. this 바인딩은 함수 호출시 동적으로 결정이 되며 함수 호출 방식으로 this 바인딩 값이 결정됩니다.

위 자바스크립트 코드에서 ref는 일반 함수로 호출됩니다. 일반 함수로 호출되는 함수 내부의 this에는 전역 객체가 바인딩되지만, 클래스 내부 모든 메서드는 암묵적으로 strict mode가 적용되어 일반 함수로 호출된 함수 내부의 this 바인딩 값은 undefined가 됩니다.

이와 같은 이유로 클래스 컴포넌트에서 this.handleClickhandler를 이벤트 핸들러로 등록은 되지만, 이벤트가 발생하여 이벤트 핸들러가 호출되는 방식은 리액트가 일반 함수처럼 호출하기 때문에 내부에 this를 참조하면 undefined가 반환됩니다.

따라서 이벤트 핸들러 함수 내부에서 this 객체(컴포넌트 자기자신)를 사용하려면 그전에 미리 this를 bind 해주어야 합니다.

클래스 컴포넌트의 이벤트 핸들러 내부에서 this 바인딩 문제를 해결하기 위해서는 constructor에서 bind 메서드를 사용하여 this를 바인딩해주거나, 화살표 함수를 사용하여 해결할 수 있습니다.

Capturing

만약 캡처링 단계에서 이벤트를 캐치하고 싶은 경우 이벤트 핸들러 어트리뷰트 끝에 Capture을 붙여주면 캡처링 단계에서 이벤트를 캐치합니다.

예를 들어, click 이벤트를 캐치하는 onClick을 버블링 단계가 아닌 캡처링 단계에서 캐치하고 싶다면 onClickCapture 어트리뷰트를 사용합니다.

<button onClickCapture={clickHandler}>click here</button>;

Synthetic Event

이벤트가 발생하여 이벤트 핸들러가 호출될 때 브라우저가 호출하며 이때 암묵적으로 이벤트 핸들러의 인수로 "이벤트 객체"를 전달하면서 호출하게 됩니다. 즉, 우리는 이벤트 핸들러 내부에서 전달받은 이벤트 객체를 사용할 수 있습니다.

하지만 순수 자바스크립트와는 달리 이벤트 핸들러가 호출될 때 Event 객체가 아닌 리액트에서 제공하는 "Synthetic Event 객체"를 전달받습니다.

Synthetic Event 객체는 모든 브라우저에서 이벤트를 동일하게 처리하기 위한 래퍼 객체이며 대부분 인터페이스는 브라우저 고유 이벤트 객체와 동일합니다.

function eventHandler(event) {
    event.type;              // -> 발생한 이벤트 타입
    event.target;            // -> 이벤트 발생한 돔 요소 노드
    event.currentTarget;     // -> 이벤트 핸들러 추가한 돔 요소 노드
    event.eventPhase;        // -> 이벤트 캐치된 단계
    event.bubbles;           // -> 버블링 전파 유무
    event.cancelable;        // -> preventDefault 메서드로 기본 동작 방지 유무
    event.defaultPrevented;  // -> preventDefault 메서드 실행 유무
    event.isTrusted;         // -> 사용자 상호작용으로 발생 유무
    event.timpeStamp;        // -> 이벤트 발생된 시각을 밀리초로
    
    event.preventDefault();  // -> 요소 기본 동작 방지
    event.stopPropagation(); // -> 이벤트 전파 방지
}
profile
Frontend Dev
post-custom-banner

0개의 댓글