Lined Notebook

이벤트 핸들링 함수

by yjym33

이벤트란?

 

사용자가 웹 브라우저에서 DOM 요소들과 상호 작용하는 것을 이벤트(event)라고 합니다.

 

4.1  이벤트를 사용할때 주의사항

 

  1. 이벤트 이름은 카멜 표기법으로 작성합니다.

  2. 이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달합니다.

  3. DOM 요소에만 이벤트를 설정할 수 있습니다.

 

4.2  예제로 이벤트 핸들링 익히기

 

4.2.1 컴포넌트 생성

import React, { Component } from 'react';

class EventPractice extends Component {
  render() {
    return (
      <div>
        <h1> 이벤트 연습 </h1>
      </div>
    );
  }
}

export default EventPractice;

[EventPractice. js]

import React from 'react';
import EventPractice from './EventPractice';

const App = () => {
  return <EventPractice />;
};

export default App;

[App.js]

 

4.2.2 onChange 이벤트 설정

 

EventPractice 컴포넌트에 input 요소를 렌더링하는 코드와 onChange 이벤트를 설정하는 코드를 작성합니다.

 

import React, { Component } from 'react';

class EventPractice extends Component {
  render() {
    return (
    <div>
      <h1> 이벤트 연습 <h1>
      <input
        type="text"
        name="message"
        placeholder="아무거나 입력해 보세요"
        onChange={
          (e) => {
            console.log(e);
          }
        }
      />
     </div>
    );
  }
}
 
 export default EventPractice;

[EventPractice. js]

 

4.2.2.2 state에 input 값 담기

 

이번에는 state에 input 값을 담아 보겠습니다.

 

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    message: '',
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={e => {
            this.setState({
              message: e.target.value,
            });
          }}
        />
      </div>
    );
  }
}

export default EventPractice;

인풋에 아무거나 입력했을 때 오류가 발생하지 않는다면 state 에 텍스트를 잘 담은 것입니다.

 

4.2.2.3 버튼을 누를 때 comment 값을 공백으로 설정

 

이제 input 요소 아래에 button 을 하나 만들고, 클릭 이벤트가 발생하면 현재 input 에 담긴 텍스트를 띄운 후 공백으로 설정하도록 합니다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    message: '',
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={e => {
            this.setState({
              message: e.target.value,
            });
          }}
        />
        <button
          onClick={() => {
            alert(this.state.message);
            this.setState({
              message: '',
            });
          }}
        >
          Submit
        </button>
      </div>
    );
  }
}

export default EventPractice;

alert를 사용하여 현재 message 값을 화면에 표시하게 했습니다.

 

임의 메소드 만들기

이전에 주의사항에서 "이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수형태의 값을 전달합니다" 라고 언급한적이 있습니다. 그렇기에 이벤트를 처리할 떄 렌더링을 하는 동시에 함수를 만들어서 전달해 주었습니다. 이 방법 대신 함수를 미리 준비하여 전달하는 방법도 있습니다. 성능상으로는 차이거 거의 없지만, 가독성은 훨씬 높습니다.

앞서 onChange와 onClick에 전달한 함수를 따로 빼서 컴포넌트 임의 메서드를 만들어 보겠습니다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    message: '',
  };

  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

  handleChange(e) {
    this.setState({
      message: e.target.value,
    });
  }

  handleClick() {
    alert(this.state.message);
    this.setState({
      message: '',
    });
  }

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;

여기서 주의해야할 점은 this 바인딩입니다. this는 함수가 어디서 호출되는지에 따라 결정됩니다.

클래스의 임의 메소드가 특정 HTML 요소의 이벤트로 등록되는 과정에서 메소드와 this의 관계가 끊어지게 됩니다.

따라서 this를 컴포넌트 자신으로 가리키게 하기 위해 메소드를 this와 바인딩하는 작업이 필요하고, 현재 코드에서는 constructor 함수 내부에서 함수를 바인딩하고 있습니다.

 

메소드 바인딩은 이렇게 생성자 메소드에서 하는 것이 정석입니다. 새 메소드를 만들 때마다 constructor 도 수정해야하기 때문입니다.

하지만 클래스형 컴포넌트에서 public class fields 문법을 사용하여 화살표 함수로 메소드를 구현하면, 좀 더 편하게 작성할 수 있습니다다. 화살표 함수의 this는 부모 함수의 this를 상속받는데 JS에서 클래스는 함수로 구현되어 있기 때문에 this는 컴포넌트 자신이 되므로 bind(this)를 하지 않아도 되기 때문입니다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    message: '',
  };

  handleChange = e => {
    this.setState({
      message: e.target.value,
    });
  };

  handleClick = () => {
    alert(this.state.message);
    this.setState({
      message: '',
    });
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;

이 문법은 실험적인 문법으로 바벨을 설정해주어야 하는데 Create-React-App 으로 만든 프로젝트는 이 문법이 기본적으로 설정되어 있으므로 사용 가능합니다.

 

input 여러 개 다루기

이제 input 값을 state 에 넣는 방법을 배웠습니다.

그런데 input 이 여러 개일 때는 어떻게 해야할까요? 메소드를 여러 개 만들어 해결할 수 있지만, event 객체를 활용하면 더 쉽게 처리할 수 있습니다. onChange 이벤트 핸들러에서 e.target.name 은 해당 input의 name을 가리킵니다. 이 값을 이용해 state를 설정하면 쉽게 해결할 수 있습니다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    username: '',
    message: '',
  };

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };

  handleClick = () => {
    alert(this.state.username + ': ' + this.state.message);
    this.setState({
      username: '',
      message: '',
    });
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="username"
          placeholder="User name"
          value={this.state.username}
          onChange={this.handleChange}
        />
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;

 

다음은 EventPractice의 handleChange 함수를 살펴봅시다.

handleChange = e => {
  this.setState({
    [e.target.name]: e.target.value,
  });
};

// EventPractice의 handleChange 함수

이 코드를 보면 e.target.name  [] 로 감싸는데, 이렇게 객체 안에서 key를 [] 로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key 값으로 사용된다.

 

onKeyPress 이벤트 핸들링

키를 눌렀을 때 발생하는 KeyPress 이벤트를 처리해본다. 두 번째 인풋에서 Enter 키를 눌렀을 때 handleCick 메소드를 호출하도록 코드를 추가한다.

import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    username: '',
    message: '',
  };

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };

  handleClick = () => {
    alert(this.state.username + ': ' + this.state.message);
    this.setState({
      username: '',
      message: '',
    });
  };

  handleKeyPress = e => {
    if (e.key === 'Enter') {
      this.handleClick();
    }
  };

  render() {
    return (
      <div>
        <h1>Event Practice</h1>
        <input
          type="text"
          name="username"
          placeholder="User name"
          value={this.state.username}
          onChange={this.handleChange}
        />
        <input
          type="text"
          name="message"
          placeholder="type something"
          value={this.state.message}
          onChange={this.handleChange}
          onKeyPress={this.handleKeyPress}
        />
        <button onClick={this.handleClick}>Submit</button>
      </div>
    );
  }
}

export default EventPractice;

 

 

함수형 컴포넌트로 구현해 보기

이제 클래스형 컴포넌트로 작성했던 EventPractice 컴포넌트를 함수형 컴포넌트로 바꾸어본다.

import React, { useState } from 'react';

const 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 = () => {
    alert(username + ': ' + message);
    setUsername('');
    setMessage('');
  };
  const onKeyPress = e => {
    if (e.key === 'Enter') {
      onClick();
    }
  };

  return (
    <div>
      <h1>Event Practice</h1>
      <input
        type="text"
        name="username"
        placeholder="User name"
        value={username}
        onChange={onChangeUsername}
      />
      <input
        type="text"
        name="message"
        placeholder="type something"
        value={message}
        onChange={onChangeMessage}
        onKeyPress={onKeyPress}
      />
      <button onClick={onClick}>Submit</button>
    </div>
  );
};

export default EventPractice;

기능이 이전과 같이 동작하는지 확인해본다.

위의 함수형 컴포넌트 코드에서는 e.target.name 을 사용하지 않고 onChange 관련 함수를 따로 만들었다. 하지만 input의 개수가 많아질수록 불편해지므로, e.target.name 을 활용하는 것이 더 좋을 수 있다.

import React, { useState } from 'react';

const 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 = () => {
    alert(username + ': ' + message);
    setForm({
      username: '',
      message: '',
    });
  };
  const onKeyPress = e => {
    if (e.key === 'Enter') {
      onClick();
    }
  };

  return (
    <div>
      <h1>Event Practice</h1>
      <input
        type="text"
        name="username"
        placeholder="User name"
        value={username}
        onChange={onChange}
      />
      <input
        type="text"
        name="message"
        placeholder="type something"
        value={message}
        onChange={onChange}
        onKeyPress={onKeyPress}
      />
      <button onClick={onClick}>Submit</button>
    </div>
  );
};

export default EventPractice;

e.target.name 값을 활용하려면 useState 를 사용할 때 input 값들이 들어있는 객체를 정의해 사용하면 된다.

 

정리

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

 

같이 읽기

이벤트 처리하기 - React

https://medium.com/@wongni/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-bind-this-%EB%B9%BC-%EB%B2%84%EB%A6%AC%EA%B8%B0-dfb0bbf7bef0

 

블로그의 정보

생각보다 실천을

yjym33

활동하기