if(재능이 없으면 시간으로 극복하라){return 성공;}

React - Rendering part5 Events

React가 이벤트를 다루는 방법을 알아보자~

기존의 HTML에선 이벤트를 어떻게 다루었나?

<form onsubmit="hello()">
  <button type="submit">
    Hello world
  </button>
</form>
<script>
function hello(){
  alert("안녕하세요!");
}
</script>

위와 같이 화면 구성하는 부분과 javascript부분을 나누어서 구성하곤 하지 않았던가?
하지만 리액트에선 아래와 같이

function Form() {
  function hello() {
    alert("안녕하세요!");
  }

  return (
    <form onSubmit={hello}>
      <button type="submit">Submit</button>
    </form>
  );
}

구성 해놓으면 <Form>이라는 태그를 이용하여 만들어놓은 컴포넌트를 간단하게 사용 가능하다! 기본적으로 Event Handling은 위와 같이 가능하고 Synthetic Event, Responding to Events, React Event Handler에 대해 알아보자.

Synthetic Event(합성 이벤트)이란? 모든 브라우저에서 이벤트를 동일하게 처리하기 위한 Wrapper 객체이다. React는 처음 랜더링 할 때 이벤트 리스너를 제공하여 처리한다. 이 이벤트 리스너와 대응되는 이벤트 핸들러 함수는 모든 부라우저에서 동일하게 처리하기 위한 이벤트 래퍼로 만들어져야 한다.라는데 평소에 이벤트 핸들러, 리스너를 개념을 알고 이벤트를 쓰는 개발자가 몇이나 될까? 그래서 내가 정리해보기로 했다. 이벤트 핸들러는 이벤트가 발생했을 때 처리를 담당하는 실행 함수 이벤트 리스너는 이벤트에 대해서 일어날 동작을 정의하는 부분

클릭하면 다음페이지로 넘어가는 이벤트가 있다. 이벤트 리스너는 onclick이고 이벤트 핸들러가 nextPage가 되는거다.

리액트에서 이벤트가 발생할 씨 이벤트 핸들러는 기본적으로 Synthetic Event 인스턴스를 가져온다. 즉 리액트로 개발하게 되면 native envet가 아니라 Synthetic Event를 사용하게 되는 것이다. 결국 Synthetic Event라는건 native event처럼 모든 브라우저에서 사용할 수 있도록 만들어진 합성 이벤트라는 뜻이다. 이러한 이벤트의 종류는 아래와 같이 있다고 한다.

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
void persist()
DOMEventTarget target
number timeStamp
string type

그럼 Synthetic Event의 구조는 나중에 다시 와서 살펴보도록 하자 ^^ 어찌됐던 다른 브라우저에서도 동일한 동작을 할 수 있도록 만들어진 이벤트가 Synthetic Event 이벤트라고 이해하고 넘어가자.

이벤트를 쓰는 형식은 여러가지가 있다. 근데 요즘은 보통 화살표 형식으로 쓰는 듯..? 잘 안써서 안읽힘 ㅠㅠ

다음으로 우리는 Responding to Events에 대해 알아보자!

React 보통은 아래와 같이 구성을 하는데

function ButtonClickEvent() {
  function handleClick(){
    alert("클릭하셨네요!");
  }

  return (
    <button onClick={handleClick}>
      I don't do anything
    </button>
  );
}

JSX에선 인라인으로 구성 할 수 있다.

<button onclick={function handlerclick(){alert("클릭하셨네요!")}}>

위의 인라인처럼 구성하면 클릭시에 alert창이 뜨는게 아니라 랜더링할 때 뜨니 주의해야한다. 다른 방법으로는 아래와 같이 구성 가능하다.

<button onclick={() => alert("클릭하셨네요!")}>

또한 이벤트 핸들러에 전달되는 함수는 호출되는 것이 아니라 전달되는 형식으로 만들어져야 한다.

<button onClick={handleClick}> ( O ) 전달
<button onClick={handleClick()}> ( X ) 호출
<button onClick={() => alert('클릭하셨네요!')}> ( O ) 전달
<button onClick={alert('클릭하셨네요!')}> ( X ) 호출

즉 정의를 한 후에 가져오는 형식으로 쓰라는거다. 이벤트 핸들링을 할 때 props는 어떻게 전달하는지도 한 번 보자

function AlertButton({ message, children }) {
  return (
    <button onClick={() => alert(message)}>
      {children}
    </button>
  );
}

function App() {
  return (
    <>
        <div>
          <AlertButton message="message를 던져봅시다!">
            버튼1
          </AlertButton>
          <AlertButton message="두번째 message를 던져봅시다">
            버튼2
          </AlertButton>
        </div>
        
    </>
  )
}

Image

Image

Image

여기까진 이해가 쉬울 것이다. 그렇다면 이벤트 핸들러 자체를 props로 전달해보자.

unction ButtonComponent({ onClick, children }) { /*버튼은 onClick이벤트와 props를 통해 텍스트를 받는다*/
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

function FirstButton({ textValue }) { /*첫번째 버튼은 버튼 텍스트를 props로 받고(props이름 지정 가능)*/
  function FirstButtonClickEvent() {        /*받은 텍스트를 alert에 뿌려주는 함수를 구성*/
    alert(`props로 받은 텍스트 값 : ${textValue}!`); 
  }

  return (                            /*상단에서 만들어준 버튼에 함수와 props를 바인딩*/
    <ButtonComponent onClick={FirstButtonClickEvent}>
      {textValue} 버튼
    </ButtonComponent>
  );
}

function SecondButton() {
  return (
    <ButtonComponent onClick={() => alert('두번째 버튼 클릭하셨네요!')}>
      두번째 버튼
    </ButtonComponent>
  );
}


function App() {
  return (
    <>
      <div>
        <FirstButton textValue="첫번째" /> /*지정한 props로 값을 삽입*/
        <SecondButton />
        <button onClick={alert('세번째 버튼은 자동으로 클릭 되는 느낌이네요?')}>
          세번째 버튼
        </button> /*랜더링 시 자동으로 alert이 뜬다*/
      </div>
    </>
  )
}

그렇다면 이벤트 전파(Event propagation)에 대해서 알아보자. 이벤트 핸들러는 구성요소가 가질 수 있는 모든 하위 이벤트도 포착한다. 이 말이 무슨 말이냐면 이벤트가 발생한 위치에서 트리 위로 올라가서 다른 이벤트들도 실행시킨단 소리

return (
    <div onClick={() => {alert('안녕!');}}>
      <button onClick={() => alert('헬로!')}>
        헬로 버튼
      </button>
      <button onClick={() => alert('곤니찌와!')}>
        곤니찌와 버튼
      </button>
    </div>
  );

위 소스를 보면 2개의 버튼을 만들었다. 헬로 버튼을 클릭하면 어떻게 될 것 같은가? 일반적으로 생각하면 헬로! 라고 찍힌 alert창이 뜰 것이다.. 하지만 결과는 어떨까?

헬로! alert이 뜨고 안녕! alert이 뜬다.

그렇다면 이러한 이벤트 전파를 막을 수 있는 방법이 있을까? 왜냐면 엘리먼트가 엘리먼트를 감싸도 이벤트에 대한 상관관계가 없을 수 있으니까.
보통은 상관관계가 없지. 엘리먼트 별 역할이 다르니까!

그래서 이벤트 전파를 막는 방법이 있다. 아래 예시를 보자

function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation(); /* stop propagation*/
      onClick();
    }}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div onClick={() => {alert('div에 걸린 이벤트입니다!');}}>
      <Button onClick={() => alert('버튼 1 alert!')}>
        버튼1
      </Button>
      <Button onClick={() => alert('버튼 2 alert!')}>
        버튼2
      </Button>
    </div>
  );
}

이벤트 전파를 막는 방법은 e.stopPropagation()을 통해 할 수 있다.

<button onClick={e => e.stopPropagation()} />

인라인 방식으로 작성하는 방식은 위와 같이. 아! inline(인라인) 방식은 태그내에 직접 javascript를 작성하는 방식이다.

이벤트 발생시에 기본적으로 전체 페이지를 다시 로드 하는 소스들이 있다.

export default function Signup() {
  return (
    <form onSubmit={() => alert('Submitting!')}>
      <input />
      <button>Send</button>
    </form>
  );
}

해당 form을 submit시키면 페이지 전체 다시 로드한다. 이러한 페이지 로드를 막는 방법이 있다.

export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault(); /*페이지 로드 방지*/
      alert('Submitting!');
    }}>
      <input />
      <button>Send</button>
    </form>
  );
}

이것으로 Responding to Events은 정리가 되었다. 마지막으로 Event Handler는 리액트에서 어떻게 동작되는지 보자.

클릭하면 console창에 ‘Button click …’ 이라는 로그를 띄우는 소스가 있다.

import React from 'react';

function App() {
  function handleClick() {
    console.log('Button click ...');
  }

  return (
    <div>
      <button type="button" onClick={handleClick}>        /*함수 전달 (O)*/
      /*<button type="button" onClick={handleClick()}>*/  /*함수 호출 (X) --> 실행 안되니 주의!*/
        Event Handler
      </button>
    </div>
  );
}

위 코드를 화살표 함수로 바꾸면 아래와 같다.

function App() {
  const handleClick = () => {
    console.log('Button click ...');
  };

  return (
    <div>
      <button type="button" onClick={handleClick}>
        Event Handler
      </button>
    </div>
  );
}

우리는 항상 함수 안에서 함수가 또 사용하길 원한다. (콜백함수..) 이걸 리액트에선 어떻게 쓰는지 보자.

import React from 'react';

function App() {
  const [text, setText] = React.useState('');

  // 1
  function handleTextChange(event) {
    setText(event.target.value); // 3
  }

  return (
    <div>
      <MyInput inputValue={text} onInputChange={handleTextChange} />

      {text}
    </div>
  );
}

// 2
function MyInput({ inputValue, onInputChange }) {
  return (
    <input type="text" value={inputValue} onChange={onInputChange} />
  );
}

이걸로 이벤트 끝!