[React][리액트를 다루는 기술] 이벤트 핸들링

uddi·2023년 3월 16일
0

React

목록 보기
7/16

리액트에서 이벤트를 다루는 방법에 대해 알아보려고 한다.

이벤트란 사용자가 웹 브라우저에서 DOM 요소들과 상호 작용하는 것

HTML을 사용해봤다면 이벤트를 다루는 것도 익숙할 것이다.

<button onclick="alert('executed')">
	Click
</button>

HTML에서는 위 예시처럼 이벤트를 실행하면 " " 사이에 있는 JS를 실행하도록 코드를 작성한다.

그렇다면 리액트에서는 어떻게 이벤트를 다룰까?

리액트의 이벤트 시스템

리액트의 이벤트 시스템은 HTML 이벤트와 사용법이 비슷하지만 주의해야할 것이 몇 개 존재한다.

  1. 이벤트 이름은 카멜 표기법으로
    onclick 같은 경우 onClick으로 작성해야 하며, onkeyup은 onKeyUp으로 작성해야 한다.

  2. 이벤트에 실행할 JS 코드를 전달하는 것이 아닌, 함수 형태의 값을 전달
    큰 따옴표 안에 실행할 코드를 넣은 HTML과 달리 리액트에서는 함수 형태의 객체를 전달한다.

  3. DOM 요소에만 이벤트 설정 가능
    div, span, button 등의 DOM 요소에는 이벤트를 설정할 수 있지만, 직접 만든 컴포넌트에는 이벤트를 자체적으로 설정할 수 없다.

    전달받은 props를 컴포넌트 내부의 DOM 이벤트로 설정할 수는 있다.

    <div onClick={this.props.onClick}>
    </div>

리액트에서 지원하는 이벤트 종류에 대해 알아보고 싶다면 이곳을 참고하자

이벤트 핸들링 익히기

5개의 단계를 통해 이벤트 핸들링을 익혀보려고 한다.

  1. 컴포넌트 생성 및 불러오기
    컴포넌트를 생성하고 불러오는 것은 이제 다들 익숙할거라 생각하고 생략하도록 하겠다.

  2. onChange 이벤트 핸들링하기

onChange={
	(e) => {
    	console.log(e);
    }
}

위 예시에서 e 객체는 SyntheticEvent로 웹 브라우저의 네이티브 이벤트를 감싸주는 객체다.

SyntheticEvent는 흔히 사용하는 stopPropagation 과 preventDefault 를 포함하여 네이티브 이벤트와 동일한 인터페이스를 가지고 있다.

SyntheticEvent는 네이티브 이벤트와 달리 이벤트가 끝나고 나면 이벤트가 초기화되므로 정보를 참조할 수 없다.
👉 비동기적으로 이벤트 객체를 참조할 일이 있다면 e.persist( ) 함수를 호출해 줘야 한다.

onChange={
	(e) => {
    	console.log(e.target.value);
    }
}

이렇게 코드를 작성하면 값이 바뀔 때마다 바뀌는 값을 참조할 수 있다.

  1. 임의 메서드 만들기
    이벤트에 함수 형태의 값을 전달해야하기 때문에 함수를 미리 만들어 전달하면 성능차이는 별로 없지만 가독성이 훨씬 좋다

클래스형 컴포넌트에서 이벤트 핸들링

기본방식

함수가 호출될 때 this는 호출부에 따라 결정되기 때문에, 클래스의 임의 메서드가 특정 HTML 요소의 이벤트로 등록되는 과정에서 메서드와 this의 관계가 끊어진다.

this가 컴포넌트 자신으로 제대로 가리키기 위해서는 메서드와 this를 바인딩 해줘야 함
바인딩 해주지 않으면 this가 undefined를 가리키게 됨

class EventPractice extends Component {
	state = {
    	message : ''
    }
    
    constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
  	}
  
  	handleChange(e) {
      this.setState({
        message: e.target.value
    });
  }
  
  render() {
  	return (
    	<input
        	vlaue={this.state.message}
            onChange={this.handleChange}
        />
    );
}

따라서 constructor 메서드에서 바인딩하는 작업을 해준 것을 볼 수 있다.

Property Initializer Syntax 사용

메서드 바인딩은 기존 방식처럼 생성자 메서드에서 하는 것이 정석이다.

하지만, 새로운 메서드를 만들 때마다 constructor을 수정해야하는 불편함을 해소하기 위해 바벨의 transform-class-properties 문법을 사용하는 방법이 있다.
👉 화살표 함수 형태로 메서드를 정의하는 것을 의미한다.

class EventPractice extends Component {
	state = {
    	message : ''
    }
   
  	handleChange = (e) => {
      this.setState({
        message: e.target.value
    });
  }
  
  render() {
  	return (
    	<input
        	vlaue={this.state.message}
            onChange={this.handleChange}
        />
    );
}

기존 방식으로 구현한 것보다 훨씬 깔끔해진 것을 볼 수 있다.

  1. input 여러 개 다루기
    input이 여러 개일 경우 메서드를 여러 개 만들어야 할까?

그럴 수도 있겠지만 event 객체를 활용하면 더 쉽게 처리할 수 있다.

class EventPractice extends Component {
	state = {
    	message : '',
        username: ''
    }
    
  	handleChange = (e) => {
      this.setState({
        [e.target.name]: e.target.value
    });
  }
  
  render() {
  	return (
    	<>
          <input
              vlaue={this.state.username}
              name="username"
              onChange={this.handleChange}
          />
          <input
              vlaue={this.state.message}
              name="message"
              onChange={this.handleChange}
          />
       </>
    );
}

위 예시에서 handleChange 메서드의 코드를 살펴보자

해당 코드처럼 객체 안에서 key를 [ ]로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key 값으로 사용된다.

const name = 'variantKey';
const object = {
	[name]: 'value'
};

즉 위 예시는 { 'variantKey' : 'value' }와 같다.

  1. onKeyPress 이벤트 핸들링
    KeyPress 이벤트는 키를 눌렀을 때 발생하는 이벤트다.
class EventPractice extends Component {
	state = {
    	message : '',
        username: ''
    }
    
  	handleChange = (e) => {
      this.setState({
        [e.target.name]: e.target.value
    });
  }
  
   	handleClick = () => {
      this.setState({
        username: '',
        message: ''
    });
  }
  
  handleKeyPress = (e) => {
  	if(e.key === 'Enter') {
    	this.handleClick();
    }
}
  
  render() {
  	return (
    	<input
        	vlaue={this.state.username}
            name="username"
            onChange={this.handleChange}
            onKeyPress={this.handleKeyPress}
        />
    );
}

함수 컴포넌트에서 이벤트 핸들링

지금까지 구현한 예시를 함수 컴포넌트로 바꿀 수 있다.

useState에 문자열로 저장

import { useState } from 'react';

export default function EventPractice = () => {
	const [username, setUsername] = useState('');
    const [message, setMessage] = useState('');
	
    const onChangeUsername = e => setUsername(e.target.value);
    const onChangeMessage = e => setMessage(e.target.value);
  	
    const onClick = () => {
    	setUsername('');
        setMessage('');
    };
    
    const onKeyPress = e => {
    	if (e.key === 'Enter') {
    		onClick();
        }
    };
    
  	return (
    	<>
          <input
              vlaue={username}
              name="username"
              onChange={onChangeUsername}
              onKeyPress={onKeyPress}
          />
           <input
              vlaue={message}
              name="message"
              onChange={onChangeMessage}
          />
        </>
    );

input이 위 예시처럼 두 개밖에 없는 경우 e.target.name을 활용하는 것보다 저렇게 함수 두 개를 따로 만들어 주는것도 괜찮다.

하지만 input의 개수가 많아진다면 e.target.name을 활용하는 것이 더 좋다.

useState에 객체로 저장

e.target.name 값을 활용하기 위해서는 useState를 사용할 때 input 값들이 들어있는 form 객체를 만들어 사용하면 된다.

import { useState } from 'react';

export default function EventPractice = () => {
	const [form, setForm] = useState({
    	username: '',
        message: ''
    });
    const { username, message } = form;
	
    const onChange = e => {
    	const nextForm = {
        	...form,
            [e.target.name]: e.target.value
        };
        setForm(nextForm);
    };
    
    const onClick = () => {
    	setForm({
        	username:'',
            message:''
    };
    
    const onKeyPress = e => {
    	if (e.key === 'Enter') {
    		onClick();
        }
    };
    
  	return (
    	<>
          <input
              vlaue={username}
              name="username"
              onChange={onChange}
              onKeyPress={onKeyPress}
          />
           <input
              vlaue={message}
              name="message"
              onChange={onChange}
          />
        </>
    );

정리

  • 리액트에서 이벤트를 다루는 것은 순수 JS 또는 jQuery를 사용한 웹 애플리케이션에서 이벤트를 다루는 것과 비슷하다.
    👉 JS에 익숙하면 쉽게 리액트를 활용할 수 있다.
    기존 HTML DOM Event를 알고 있다면 리액트의 컴포넌트 이벤트도 쉽게 다룰 수 있을 것임

  • 클래스형 컴포넌트로 할 수 있는 대부분의 작업은 함수 컴포넌트로도 구현 가능하다.

  • 후에 공부할 useReducer와 커스텀 Hooks를 사용하면 더 편하게 할 수 있다.

profile
거북이는 느리지만 결국 결승선을 통과한다

0개의 댓글