16. React (Re: act #4)

2021. 9. 5. 00:22WEB/React

Event

기본적인 Props, State를 익혔으니, 본격적으로 사용자와 상호 작용할 수 있게 만들어야 한다

상호 작용이 이루어지려면 웹 브라우저의 DOM 요소를 조작해야 한다

가령 사용자가 마우스로 버튼을 클릭했을 때 onclick 이벤트를 실행하는 것과 같은 것을 말한다

 

앞전 내용에 state를 설명하기 위해 이미 몇 번의 이벤트를 사용했었다

이벤트는 상호 작용에 있어서 굉장히 중요한 기능이며, 잘 알아둘 필요가 있다고 생각한다

 

HTML 환경에서 이벤트를 만들어본 경험이 있다면 리액트 또한 익숙하게 느껴질 수 있다

리액트 또한 HTML 환경에서와 비슷하게 사용되지만, 아래와 같은 몇 가지 주의 사항들이 있다

 

- 이벤트의 이름은 카멜 표기법으로 작성해야 한다

  예) onclick → onClick, onchange → onChange

 

- 이벤트 동작시 자바스크립트 코드를 전달하는 것이 아닌, 함수 형태의 데이터를 전달해야 한다

onClick={() => {clickEvent}}
onClick={() => setColor("red")}

  데이터 전달시에는 렌더링 부분 외부에서 함수 컴포넌트로 정의해서 전달하는 방법,

  화살표 함수를 통해 바로 해당 값을 변경하여 전달하는 방법들을 이용해서 사용할 수 있다

 

- DOM 요소에만 이벤트를 설정할 수 있다

  앞전 내용들에서 직접 만들어본 컴포넌트, 해당 컴포넌트들을 HTML의 태그처럼 사용할 수 있었다

  하지만 태그의 형태로 사용되었을 뿐 태그도 아니고, DOM 요소도 아니기 때문에 이벤트를 설정할 수 없다

  이벤트는 반드시 HTML에서도 동일하게 사용되는 DOM 요소에만 설정할 수 있다

 

 

리액트의 이벤트

리액트에서 지원되는 이벤트의 종류는 아래 목록과 같다 (해당 목록은 리액트 공식 홈페이지를 캡처한 것)

참고 : 합성 이벤트(SyntheticEvent) – React (reactjs.org)

리액트에서는 이와 같이 수 많은 이벤트들의 지원을 제공하고 있다

 

 

이벤트 핸들링(Event handling)

이벤트 테스트를 진행하기 위한 기본적인 실습 환경 세팅이 필요하다

 

먼저 이벤트를 테스트할 Event.js를 생성 후 EventHandler 클래스 컴포넌트를 생성한다

import React, { Component } from "react";

class EventHandler extends Component {
    render() {
        return (
            <div>
                <h1>Event test</h1>
            </div>
        )
    }
}

export default EventHandler;

 

이후 App.js에서 아래와 같이 EvenvHandler 컴포넌트를 호출해서 랜더링해준다

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

export default App;

 

 

onChange Event Handling

첫 번째로는 <input>의 입력값을 감지하고 값의 변화를 처리해줄 onChange 이벤트다

return (
    <div>
        <h1>Event test</h1>
        <input type="text" onChange={(e) => {console.log(e)}} />
    </div>
)

Event.js의 리턴값의 코드를 위와 같이 수정하면, 개발자 도구를 통해 아래와 같은 결과를 확인할 수 있다

로그를 통해 콘솔에 찍히는 기록 중 SyntheticEvent란 웹 브라우저에서 네이티브 이벤트를 감싸는 객체다

네이티브 이벤트와 인터페이스가 동일하기 때문에 JS에서 사용하던 방법과 동일하다

 

단, SyntheticEvent는 네이티브 이벤트와 달리 이벤트 종류와 동시에 이벤트가 초기화되므로

데이터를 참조할 수는 없다(이벤트가 참조하고 있는 e 객체 내부의 모든 값이 비워진다)

 

만약 이벤트가 발생할 때마다 변하는 모든 인풋 값을 기록하고 싶다면 아래와 같이 사용하면 된다

<input type="text" onChange={(e) => {console.log(e.target.value)}} />

현재 이벤트가 발생하고 있는 타겟(<input>)의 값(value)을 기록하겠다는 의미다

값을 입력할 때마다 발생하는 이벤트를 콘솔에 기록하게 된다

 

 

state에 input 값 할당

클래스 컴포넌트의 state에도 <input>을 통해 입력받은 값을 할당할 수 있다

import React, { Component } from "react";

class EventHandler extends Component {
    state = { log: '' }
    render() {
        return (
            <div>
                <h1>Event test</h1>
                <input 
                type="text" 
                value={this.state.log}
                onChange={(e) => {this.setState({
                    log: e.target.value
                })}} />
            </div>
        )
    }
}

export default EventHandler;

클래스 컴포넌트에서 사용법을 배웠던 그대로 이벤트에 적용시키는 법을 다시 한 번

먼저 render()가 시작되기 전 정의된 state는 그 자체로는 별도의 기능을 하지 않는다

(클래스 컴포넌트의 state는 값을 객체로만 할당해야하기 때문에 객체 형태로 선언한다)

 

값을 입력받는 <input>에 value라는 속성(Attribute)을 추가한다

value는 초깃값 설정 뿐 아니라, 서버에 제출하는 데이터의 값을 의미한다

즉, 사용자가 입력한 데이터를 서버에 전달할 때 사용하는데 그 데이터를 state로 설정한 것이다

 

사용자가 데이터를 입력하면 그 즉시 onChange 이벤트가 발생하게 되고,

해당 값은 setState를 통해 state의 log값을 변경하게 된다

위와 같이 값을 입력 후 Enter를 눌러도, 오류를 발생시키지 않는다면 아무 반응이 없더라도

state에 값이 정상적으로 전달이 된 것이다

 

해당 state에 값이 잘 전달되었는지, 우리가 만든 이벤트가 잘 동작하고 있는 것인지 궁금할 수 있다

확인 과정을 거치고 싶다면 아래와 같이 코드를 추가해보자

return (
            <div>
            	<h1>Event text</h1>
            	<input (...) />
                <button onClick={() => {
                    console.log(this.state.log)
                    this.setState({
                        log: ""
                    });
                }}> log 확인 </button>
            </div>
        )

<button>의 내용만 새롭게 추가한 상태이며, 태그의 동작 과정을 설명하면 다음과 같다

 

1. 버튼을 클릭시 이벤트가 발생하게 된다

<button onClick={() => {}} // 전달하는 매개 변수는 따로 없다

2. 첫 번째로 콘솔 창에 현재 this가 가리키고 있는 컴포넌트의 state.log 값을 출력한다

console.log(this.state.log) // <input>의 이벤트를 통해 변경된 state에 접근

3. 두 번째로 state.log의 값을 ("")빈 문자열로 초기화 시킨다

this.setState({
    log: ""
});             // this.setState를 통해 state를 초기화 상태로 만든다

 

실제로 위와 같이 동작하는지 아래 영상을 통해 확인해보자

버튼 클릭과 동시에 콘솔에 state의 값이 출력되고, 인풋은 초기화되는 것을 확인할 수 있다

 

 

임의 메서드 생성

위에서 이벤트 외부에서 함수를 미리 정의하여 전달하는 방법도 있다고 언급했었다

성능상으로는 차이가 거의 없다고 하는데, 가독성은 훨씬 높아진다

 

import React, { Component } from "react";

class EventHandler extends Component {
    state = { log: '' }

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

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

    handleClick(e) {
        console.log(this.state.log)
        this.setState({
            log: ""
        });
    }

    render() {
        return (
            <div>
                <input 
                  type="text" 
                  value={this.state.log}
                  onChange={this.handleChange} />
                <button onClick={this.handleClick}> 확인 </button>
            </div>
        )
    }
}

export default EventHandler;

JS의 this는 호출부에 따라 바인드 될 객체가 정해진다(정적이 아니라 동적)

즉, 좀전에 생성했던 임의 메서드가 특정 HTML 요소의 이벤트로 등록되는 과정에서

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

 

이를 방지하고 this가 EventHandler 컴포넌트(자신)를 제대로 가리키기 위해 바인딩 과정이 필요하다

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

만약 이와 같은 바인딩 작업을 하지 않을 경우 this는 undefined를 가리키게 된다

현재는 constructor 함수를 통해 각각의 이벤트 함수들의 바인딩 작업이 정상적으로 이루어지고 있다

 

* 바인드 해제시 이러한 에러 문구를 보게 된다

 

 

Property Initializer Syntax를 사용한 메서드 작성

메서드 바인딩은 생성자 메서드에서 하는 것이 

 

 

 

 

 

 

 

 

 

 

 

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

15. React (Re: act #3.5)  (0) 2021.08.23
14. React (Re: act #3)  (0) 2021.08.22
13. React (Re: act #2)  (0) 2021.08.18
12. React (Re: act #1.5)  (0) 2021.08.17
11. React (Re: act #1)  (0) 2021.08.15