프론트앤드/React

[포스코x코딩온] 웹개발자 입문 과정 8주차 | 이벤트 핸들링

영최 2023. 4. 25. 00:41
728x90

1. 리액트에서의 이벤트 핸들링

  이벤트를 통해서 사용자와 웹 브라우저는 서로 상호작용을 할 수 있다.

  이벤트 핸들러는 "어떠한 사건에 대한 동작을 다룬다" 라는 의미이다.

  리액트에서의 이벤트 핸들러와 html에서의 이벤트 핸들러는 아래와 같이 몇개의 차이점이 있다.

  리액트에서는 객체로 전달해야하고, html에서는 괄호()까지 작성해야 동작하는데,

  작동 방식의 차이라고 생각하면 될 것 같다. 

HTML Event VS React Event

  아래 주의 사항을 통해 더 자세히 알아보도록 하자.

 


 가.React의 Event 주의점

   1)카멜표기법 

    첫번째로, 이벤트 이름은 카멜표기법으로 작성해야한다.

    예를 들면 onclick -> onClick / onkeyup -> onKeyUp으로 작성한다.

 

   2)함수 자체를 객체로 전달

    두번째로, 실행할 자바스크립트 코드를 전달하는 것이 아닌 함수형태의 값을 전달해야한다.

    또한 큰따옴표("")가 아닌 객체({})로 전달해야한다.

    아래 예제 처럼 화살표 함수 문법으로 함수를 만들어

    함수 자체(함수 형태의 값을) 전달해야한다.

const Say = () => {
  const [ message, setMessage ] = useState( "" );
  const onClickEnter = () => setMessage( "안녕하세요!" );
 
  return (
    <div>
      <button onClick={onClickEnter}>입장</button>
    </div>
  );
};
 
export default Say;

 

   3)기본 DOM요소에만 이벤트 설정 가능

    세번째로, DOM요소에만 이벤트를 설정할 수 있다.(div, span, button ...etc)

    그러나, 직접 만든 컴포넌트에는 이벤트를 설정할 수 없다.

 

    만약 아래처럼 직접 만든 컴포넌트에 onClick을 설정했을 경우

    이는 Something이란 함수를 실행하는 것이 아닌,

    이름이 onClick이라는 props를 hello라는 컴포넌트에 전달해주는 역할을 한다.

<Hello onClick={Something}></Hello>

 

    그러나 아래처럼 전달받은 함수형태의 props를 

    컴포넌트 내부의 DOM이벤트로 설정하는 것은 가능하다

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

 나.React 합성이벤트

   우선 onChange이벤트를 클래스형 컴포넌트방식에 적용해보면 아래 코드와 같다.

   onChange 이벤트는 입력이 끝났을 때 발생되는 이벤트이다.

	
import React, { Component } from 'react';
 
class Event extends Component {
    render() {
        return (
            <div>
                <h1>리액트의 이벤트!!</h1>
 
                <input
                    type="text"
                    name="message"
                    placeholder="이곳에 입력해보세요."
                    onChange={
                        (e) => {
                            console.log(e);
                        }
                    }
                />
            </div>
        );
    }
}
 
export default Event;

 

   코드를 저장하고 개발자도구를 연다음, input에 아무 글이나 입력하면 이벤트 객체가 콘솔에 나타난다.

onChange = { (e) => {console.log(e)} }; // 출력 결과 >> SyntheticBaseEvent { …, nativeEvent: PointerEvent, …}

 

   여기서 출력 결과를 보면 e = SyntheticBaseEvent 라는 것을 확인할 수 있다.

 

   즉, 콘솔에 기록되는 e 객체는 SynthticEvent(합성이벤트)이다.

   리액트가 DOM이 아닌 VirtualDOM을 사용하는 것처럼, 

   리액트는 웹 브라우저의 nativeEvent가 아닌,

   nativeEvent를 감싼 SyntheticEvent를 사용한다.

   (SyntheticEvent ⊃ nativeEvent)

 

   다시 정리하면,

   리액트에서 사용되는 onClick이나 onChange와 같은 이벤트  

   ≠ 브라우저 기본 이벤트(nativeEvent)

   = 리액트 고유 이벤트 객체 (합성이벤트, SyntheticEvent)

 

   이 SyntheticEvent는 이벤트가 끝나고 나면 초기화되어

   정보를 참조할 수 없다는 특징이 있다.

   그래서 1초 뒤에 e객체를 참조해보면 e객체 안에 값이 사라져있다.

 

   만약 onChange 이벤트가 발생할 때, 인풋값의 변화를 확인하고 싶다면,

   아래처럼 e.target.value를 통해 input값을 불러올 수 있다.

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

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

 

 가.함수 형태의 값을 Event에 전달하는 방법

   위의 코드에 이어서 이렇게 값이 바뀔 때마다 state로 값을 변경하기 위해서는

   state 초기값을 설정하고

  state = { message: "" }

 

   이벤트 핸들링 함수 내에서 this.setState메서드를 호출한다.

   이후 이 메서드를 이용해서 state를 input의 value값으로 설정한다.

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

 

   전체 코드는 아래와 같다.

import React, { Component } from 'react';
 
class Event extends Component {
    state = {
        message: ""
    }
 
    render() {
        return (
            <div>
                <h1>리액트의 이벤트!!</h1>
 
                <input
                    type="text"
                    name="message"
                    placeholder="이곳에 입력해보세요."
                    value = {this.state.message}
                    onChange={
                        (e) => {
                            this.setState({
                                message : e.target.value
                            })
                        }
                    }
                />
            </div>
        );
    }
}
 
export default Event;

 

   다음으로, button을 누르면 input에 입력한 내용이 alert창에 띄워지고

   input창을 초기화하고 싶다면 아래 코드를 input 태그 아래 작성하면 된다.

   <button onClick={ () => { alert(this.state.message); this.setState({ message: "" }); } }>클릭</button>

 나.bind 를 사용하여 함수를 밖에서 정의한 후 Event에  전달하는 방법

   위 방법처럼 이벤트에 함수형태의 값을 전달하는 것이 아닌,

   미리 함수를 따로 빼서(정의해서) 전달하는 방법은 아래와 같다.

import React, { Component } from 'react';
 
class Event extends Component {
    state = {
        message: ""
    }
 
    constructor(props) {
        super(props);
        this.eventChange = this.eventChange.bind(this);
        this.eventClick = this.eventClick.bind(this);
    }
 
    eventChange(e) {
        this.setState({
            message: e.target.value
        })
    }
 
    eventClick() {
        alert(this.state.message);
        this.setState({
            message: ""
        });
    }
 
    render() {
        return (
            <div>
                <h1>리액트의 이벤트!!</h1>
 
                <input
                    type="text"
                    name="message"
                    placeholder="이곳에 입력해보세요."
                    value = {this.state.message}
                    onChange={this.eventChange}
                />
 
                <button onClick={this.eventClick}>클릭</button>
            </div>
        );
    }
}
 
export default Event;

 

   위 코드 중에 아래 부분의 코드를 보면 bind가 나온다.

   bind는 this가 현재 컴포넌트(자기 자신)을 제대로 지칭하기 위해서 사용한다.

 

   함수가 호출 될 때 this는 호출부에따라 결정되는데,

   임의 메서드가 특정 html요소의 이벤트로 등록되면 (함수를 밖에서 따로 정의하게 되면)

   메서드와 this의 관계가 끊어지게 된다.

 

   따라서 임의 메서드가 이벤트로 등록되어도 this가 컴포넌트 자신을 지칭하기 위해서는

   bind로 this와 연결해주는 작업이 필요하다.

   만약 bind를 하지 않는다면, this = undefined값을 나타낸다. 

constructor(props) {
    super(props);
    this.eventChange = this.eventChange.bind(this);
    this.eventClick = this.eventClick.bind(this);
}

 

   (여기서 constructor 안에 state 초기값을 설정해줘도 된다.)

constructor(props) { 
    super(props); 
    this.state = { message: "" }  
    this.eventChange = this.eventChange.bind(this);
    this.eventClick = this.eventClick.bind(this); 
}

 

   또한 클래스형 컴포넌트에서는 this를 사용해야 함수를 찾아갈 수 있다.

   onChange={this.eventChange},  onClick={this.eventClick}

<input
    type="text"
    name="message"
    placeholder="이곳에 입력해보세요."
    value = {this.state.message}
    onChange={this.eventChange}
/>

<button onClick={this.eventClick}>클릭</button>

 다.(권장) 화살표 함수로 함수를 밖에서 정의한 후 Event에  전달하는 방법

   위 방법처럼 매번 새로운 매서드를 만들 때마다 생성자 메서드를 수정하는건

   너무 너무! 번거롭다!!

   (더 좋은 방법은 그냥 함수형 컴포넌트를 사용하자..)

 

   바인드를 하지 않아도 클래스형 컴포넌트에서 임의의 함수를 이벤트로 지정하는 방법은

   바로바로 화살표 함수를 사용하는 것이다.

    eventChange = (e) => {
        this.setState({
            message: e.target.value
        })
    }
 
    eventClick = () => {
        alert(this.state.message);
        this.setState({
            message: ""
        });
    }

 

   전체 코드는 아래와 같다.

import React, { Component } from 'react';
 
class Event extends Component {
    state = {
        message: ""
    }
 
    eventChange = (e) => {
        this.setState({
            message: e.target.value
        })
    }
 
    eventClick = () => {
        alert(this.state.message);
        this.setState({
            message: ""
        });
    }
 
    render() {
        return (
            <div>
                <h1>리액트의 이벤트!!</h1>
 
                <input
                    type="text"
                    name="message"
                    placeholder="이곳에 입력해보세요."
                    value = {this.state.message}
                    onChange={this.eventChange}
                />
 
                <button onClick={this.eventClick}>클릭</button>
            </div>
        );
    }
}
 
export default Event;

   (+) input이 여러 개일 때 이벤트를 다루는 방법? 

    -> 메서드를 여러개 만들 수도 있겠지만 event 객체(e.target.name값)를 활용하는 방법이 있다.

         여기서 name은 input태그 내에서 지정한 name값을 지칭한다.

 

   아래처럼 input이 2개이고 각 name값을  "username", "message"로 지정했다고 한다면

<input
    type="text"
    name="username"
    placeholder="사용자 이름"
    value = {this.state.username}
    onChange={this.eventChange}
/>

<input
    type="text"
    name="message"
    placeholder="이곳에 입력해보세요."
    value = {this.state.message}
    onChange={this.eventChange}
/>

 

   eventChange함수에서 [e.target.name] : e.target.value를 이용하여

   input창에 입력될 때 해당 input의 name을 키값으로 하고

   입력된 값을 value로 지정하여 저장한다.

   이를 이용하면 구분해서 값을 지정해 줄 수 있다.

 

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

 

   전체 코드는 아래와 같다.

import React, { Component } from 'react';
 
class Event extends Component {
    state = {
        username: "",
        message: ""
    }
 
    eventChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }
 
    eventClick = () => {
        alert(this.state.username + ": " + this.state.message);
        this.setState({
            username: "",
            message: ""
        });
    }
 
    render() {
        return (
            <div>
                <h1>리액트의 이벤트!!</h1>
 
                <input
                    type="text"
                    name="username"
                    placeholder="사용자 이름"
                    value = {this.state.username}
                    onChange={this.eventChange}
                />
 
                <input
                    type="text"
                    name="message"
                    placeholder="이곳에 입력해보세요."
                    value = {this.state.message}
                    onChange={this.eventChange}
                />
 
                <button onClick={this.eventClick}>클릭</button>
            </div>
        );
    }
}
 
export default Event;

   (+) 키를 눌렀을 때 발생하는 keyPress이벤트를 사용해서

         input창에서 Enter를 눌렀을 때도 eventClick메서드를 호출해보자.

 

   아래처럼 input에 onKeyPress={this.eventKeyPress}를 지정한다.

<input
    type="text"
    name="message"
    placeholder="이곳에 입력해보세요."
    value = {this.state.message}
    onChange={this.eventChange}
    onKeyPress={this.eventKeyPress}
/>

 

   그다음 eventKeyPress 함수를 정의한다.

eventKeyPress = (e) => {
    if( e.key === "Enter" ) {
        this.eventClick();
    }
}

 

   전체 코드는 아래와 같다.

import React, { Component } from 'react';
 
class Event extends Component {
    state = {
        username: "",
        message: ""
    }
 
    eventChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }
 
    eventClick = () => {
        alert(this.state.username + ": " + this.state.message);
        this.setState({
            username: "",
            message: ""
        });
    }
 
    eventKeyPress = (e) => {
        if( e.key === "Enter" ) {
            this.eventClick();
        }
    }
 
    render() {
        return (
            <div>
                <h1>리액트의 이벤트!!</h1>
 
                <input
                    type="text"
                    name="username"
                    placeholder="사용자 이름"
                    value = {this.state.username}
                    onChange={this.eventChange}
                />
 
                <input
                    type="text"
                    name="message"
                    placeholder="이곳에 입력해보세요."
                    value = {this.state.message}
                    onChange={this.eventChange}
                    onKeyPress={this.eventKeyPress}
                />
 
                <button onClick={this.eventClick}>클릭</button>
            </div>
        );
    }
}
 
export default Event;

   (+) 함수에 인자가 있는 경우 작성 법

         빨간색으로 표시된 코드가 자주 사용 된다.

 


3. 함수형 컴포넌트에서의 이벤트 핸들링

  클래스형 컴포넌트에서보다 훨~~~~씬 편하다. 

  따로 생성자 안에 bind를 할 필요도 없고, 이벤트 지정시 this를 붙일 필요도 없다.

 

  여기서...form은 기존의 form내용을 복사한다는 뜻이다.
  그리고 [e.target.name]: e.target.value써서 원하는 값을 덮어씌운다.

const onChange = (e) => {
        const nextForm = {
            ...form,
            [e.target.name]: e.target.value
        };
 
        setForm(nextForm);
};

 

  전체 코드는 아래와 같다.

import React, { useState } from "react";
 
const Event = () => {
    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>리액트의 이벤트!!</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 Event;

 

728x90