티스토리 뷰

WEB/React

[React] 이벤트 핸들링

춘햄 2023. 3. 23. 18:56

리액트의 이벤트 시스템은 웹 브라우저의 HTML 이벤트와 인터페이스가 동일하기 때문에 사용법이 꽤나 비슷하다. 

 

리액트에서 이벤트를 사용할 때 주의할 사항들이 몇 가지가 존재하는데, 이는 다음과 같다.

 

1. 이벤트 이름은 Camel Case: 예를 들어 HTML의 onclick은 onClick으로 작성해야 한다.

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

3. DOM 요소에만 이벤트를 설정할 수 있다. 직접 만든 컴포넌트에는 자체적으로 이벤트를 설정할 수 없다.

 

바로 한번 예제와 함께 알아보도록 하자.


우선, 예제로 사용할 컴포넌트를 생성하여 불러오자.

 

◎EventPractice.js

import {Component} from 'react';

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

export default EventPractice;

 

◎App.js

import EventPractice from "./EventPractice";

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

export default App;

이제 input 태그를 렌더링하는 코드와 해당 태그에 onChange 이벤트를 추가해주자.

import {Component} from 'react';

class EventPractice extends Component {
    render() {
        return (
            <div>
                <h1>이벤트 연습</h1>
                <input
                    type="text"
                    name="message"
                    placeholder="입력해주세요."
                    onChange={
                        (e) => {
                             console.log(e.target.value);
                        }
                    }
                />
            </div>
        );
    }
}

export default EventPractice;

사실 별 건 없다. element 내부에 이벤트를 선언해주고, 그 안에 호출할 함수를 작성해주면 된다.

 


이제 state에 input 태그의 값을 보내보자. 캡처하기가 좀 불편해서 부모 태그에 margin도 추가했다.

import {Component} from 'react';

class EventPractice extends Component {

    state = {
        message: ''
    }

    render() {
        return (
            <div style={{margin: 20}}>
                <h1>이벤트 연습</h1>
                <h2>입력 값: {this.state.message}</h2>
                <input
                    type="text"
                    name="message"
                    placeholder="입력해주세요."
                    value={this.state.message}
                    onChange={
                        (e) => {
                           this.setState({
                               message: e.target.value
                           })
                        }
                    }
                />
            </div>
        );
    }
}

export default EventPractice;

 

간단하게 버튼 하나만 더 추가해보자.

import {Component} from 'react';

class EventPractice extends Component {

    state = {
        message: ''
    }

    render() {
        return (
            <div style={{margin: 20}}>
                <h1>이벤트 연습</h1>
                <h2>입력 값: {this.state.message}</h2>
                <input
                    type="text"
                    name="message"
                    placeholder="입력해주세요."
                    value={this.state.message}
                    onChange={
                        (e) => {
                           this.setState({
                               message: e.target.value
                           })
                        }
                    }
                />
                <button onClick={() => {
                    alert(this.state.message);
                    this.setState({
                        message: ''
                    })
                }}>확인</button>
            </div>
        );
    }
}

export default EventPractice;

임의 메서드 만들기

 별 다른 내용이 있는 건 아니고, 이벤트 선언 부에서 바로 작성하는 게 아니라 미리 선언해놓은 메서드 또한 사용이 가능하다는 내용이다. 

 

코드는 아래와 같이 작성할 수 있다.

import {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(e) {
        alert(this.state.message);
        this.setState({
            message: ''
        })
    }

    render() {
        return (
            <div style={{margin: 20}}>
                <h1>이벤트 연습</h1>
                <h2>입력 값: {this.state.message}</h2>
                <input
                    type="text"
                    name="message"
                    placeholder="입력해주세요."
                    value={this.state.message}
                    onChange={
                        this.handleChange
                    }
                />
                <button onClick={this.handleClick}>확인</button>
            </div>
        );
    }
}

export default EventPractice;

주의할 점은 미리 생성해놓은 메서드를 이벤트로 등록하기 위해서는 생성자에서 해당 메서드를 컴포넌트 자체(this)와 Bind 하는 작업이 필요하다. 그렇지 않으면 함수가 호출될 때 this는 호출부에 따라 결정되기 때문에 클래스의 임의 메서드가 특정 HTML 요소의 이벤트로 등록되는 과정에서 메서드와 this의 관계가 끊어진다.

 

바인딩이 되지 않는 경우라면, 각 메서드가 보고 있는 this가 undefined를 가리키게 된다.


메서드 바인딩은 생성자 메서드에서 하는 것이 정석이지만, 이 작업이 다소 번거롭다고 느껴질 수도 있다. 

새 메서드를 만들 때마다 constructor도 수정해야 하기 때문인데, 이 작업을 좀 더 간단하게 하는 방법이 있다.

 

바벨의 transform-class-properties 문법을 사용하여 화살표 함수 형태로 메서드를 정의하면 된다. 

화살표 형태로 함수를 지정하면, 보고 있는 this가 부모 컴포넌트로 고정되기 때문에 문제 없이 동작한다.

import {Component} from 'react';

class EventPractice extends Component {

    state = {
        message: ''
    }

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

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

    render() {
        return (
            <div style={{margin: 20}}>
                <h1>이벤트 연습</h1>
                <h2>입력 값: {this.state.message}</h2>
                <input
                    type="text"
                    name="message"
                    placeholder="입력해주세요."
                    value={this.state.message}
                    onChange={
                        this.handleChange
                    }
                />
                <button onClick={this.handleClick}>확인</button>
            </div>
        );
    }
}

export default EventPractice;

input 여러 개 다루기

input 태그가 여러 개이고, state 또한 여러 개를 다뤄야 한다면 아래와 같이 state 객체의 키 값과 input 요소의 이름을 맞춰준 뒤에  event 객체를 활용하여 이를 구분할 수 있다.

import {Component} from 'react';

class EventPractice extends Component {

    state = {
        message: '',
        username: ''
    }

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

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

    render() {
        return (
            <div style={{margin: 20}}>
                <h1>이벤트 연습</h1>
                <input
                    type="text"
                    name="username"
                    placeholder="사용자명"
                    value={this.state.username}
                    onChange={
                        this.handleChange
                    }
                />
                <input
                    type="text"
                    name="message"
                    placeholder="입력해주세요."
                    value={this.state.message}
                    onChange={
                        this.handleChange
                    }
                />
                <button onClick={this.handleClick}>확인</button>
            </div>
        );
    }
}

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

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


onKeyPress 이벤트도 추가해보자.

import {Component} from 'react';

class EventPractice extends Component {

    state = {
        message: '',
        username: ''
    }

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

    handleClick = (e) =>{
        alert(this.state.username + ': ' + this.state.message);
        this.setState({
            message: ''
        })
    }
    
    handleKeyPress = (e) => {
        if(e.key === 'Enter') {
            this.handleClick(e);
        }
    }

    render() {
        return (
            <div style={{margin: 20}}>
                <h1>이벤트 연습</h1>
                <input
                    type="text"
                    name="username"
                    placeholder="사용자명"
                    value={this.state.username}
                    onChange={
                        this.handleChange
                    }
                />
                <input
                    type="text"
                    name="message"
                    placeholder="입력해주세요."
                    value={this.state.message}
                    onChange={
                        this.handleChange
                    }
                    onKeyPress={this.handleKeyPress}
                />
                <button onClick={this.handleClick}>확인</button>
            </div>
        );
    }
}

export default EventPractice;

함수 컴포넌트로 바꿔보기

위 예제 코드를 함수형 컴포넌트로 변경해보자.

import {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>이벤트 연습</h1>
            <input
                type="text"
                name="username"
                placeholder="사용자명"
                value={username}
                onChange={onChangeUsername}
            />
            <input
                type="text"
                name="message"
                placeholder="아무거나 입력해보세요."
                value={message}
                onChange={onChangeMessage}
                onKeyPress={onKeyPress}
            />
            <button onClick={onClick}>확인</button>
        </div>
    )
}

input이 2개 뿐이라면 위 코드도 괜찮지만, input의 개수가 많아진다면 e.target.name을 활용하는 것이 훨씬 좋다.

 

또한 useState를 통해 사용하는 상태에 문자열이 아닌 객체를 넣어보자.

import {useState} from 'react'

const EventPractice = () => {

    const [form, setForm] = useState({
        username: '',
        message: ''
    });

    const {username, message} = form;

    const onChange = e => {
        const nextForm =  {
            ...form, //기존의 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>이벤트 연습</h1>
            <input
                type="text"
                name="username"
                placeholder="사용자명"
                value={username}
                onChange={onChange}
            />
            <input
                type="text"
                name="message"
                placeholder="아무거나 입력해보세요."
                value={message}
                onChange={onChange}
                onKeyPress={onKeyPress}
            />
            <button onClick={onClick}>확인</button>
        </div>
    )
}

export default EventPractice

 

 

 

끝!

'WEB > React' 카테고리의 다른 글

[React] 컴포넌트 반복  (0) 2023.03.24
[React] ref: DOM에 이름 달기  (0) 2023.03.24
[React] Component  (0) 2023.03.23
[React] JSX 문법  (0) 2023.03.23
[React] Hello, React!  (0) 2023.03.22
Comments