frontend/react

[React] React.js 강좌 5. 이벤트 처리와 Binding 이해

mirabo01 2025. 11. 11. 10:55

1. 리액트에서의 이벤트 처리란?

HTML에서는 클릭 이벤트를 처리할 때 이렇게 작성합니다.

<button onclick="alert('클릭!')">버튼</button>

하지만 리액트에서는 이벤트를 문자열이 아닌 함수로 전달합니다.
또한 HTML의 이벤트 이름은 소문자지만,
리액트에서는 **CamelCase(낙타 표기법)**으로 작성해야 합니다.

function App() {
  function handleClick() {
    alert('클릭되었습니다!');
  }

  return <button onClick={handleClick}>버튼</button>;
}

즉, HTML의 onclick → React의 onClick,
onchange → onChange 처럼 표기 방식이 달라집니다.


2. 이벤트 객체(Event Object)

이벤트가 발생하면 리액트는 이벤트 정보를 **SyntheticEvent(합성 이벤트)**라는 형태로 전달합니다.
이는 브라우저 간의 차이를 통합한 리액트만의 가상 이벤트 객체입니다.

function App() {
  function handleClick(e) {
    console.log(e); // SyntheticEvent 객체
    console.log(e.type); // "click"
  }

  return <button onClick={handleClick}>이벤트 확인</button>;
}

이 합성 이벤트는 브라우저마다 달랐던 이벤트 동작을 통일시켜
개발자가 보다 일관된 코드로 이벤트를 다룰 수 있게 해줍니다.


3. 인라인 함수로 이벤트 전달하기

이벤트 핸들러에 매개변수를 넘기고 싶을 때가 있습니다.
이럴 땐 인라인 화살표 함수를 사용합니다.

function App() {
  function handleClick(name) {
    alert(`${name}님, 클릭하셨네요!`);
  }

  return <button onClick={() => handleClick('기범')}>클릭</button>;
}

이 방법은 간단하지만, 렌더링될 때마다 새 함수가 생성되기 때문에
**렌더링 최적화가 필요한 경우에는 별도의 메모이제이션(useCallback)**을 고려해야 합니다.


4. 클래스형 컴포넌트에서의 이벤트 바인딩 문제

클래스형 컴포넌트에서는 this의 범위가 자주 혼동을 일으킵니다.

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { message: '안녕하세요!' };

    // this를 현재 클래스 인스턴스에 바인딩
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    alert(this.state.message);
  }

  render() {
    return <button onClick={this.handleClick}>클릭</button>;
  }
}

이 코드에서 .bind(this)를 하지 않으면
this가 undefined가 되어 this.state에 접근할 수 없습니다.

이유는 자바스크립트의 함수 호출 방식에 따라 this가 동적으로 결정되기 때문입니다.


5. 바인딩을 피하는 방법

(1) 화살표 함수 사용

화살표 함수는 자신만의 this를 가지지 않고,
상위 스코프의 this를 자동으로 상속받습니다.

class App extends Component {
  state = { message: '리액트 화살표 함수!' };

  handleClick = () => {
    alert(this.state.message);
  };

  render() {
    return <button onClick={this.handleClick}>클릭</button>;
  }
}

이 방식은 .bind(this)를 명시적으로 작성할 필요가 없기 때문에
리액트 최신 코드 스타일에서 가장 많이 쓰입니다.


6. 기본 동작 방지 (preventDefault)

리액트에서는 이벤트 핸들러 내부에서 event.preventDefault()를 호출해
기본 동작을 막을 수 있습니다.

예를 들어 폼(form)이 제출될 때 페이지가 새로고침되는 것을 막으려면 다음과 같이 작성합니다.

function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    alert('폼이 제출되었습니다!');
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="이름 입력" />
      <button type="submit">제출</button>
    </form>
  );
}

HTML에서는 return false;를 사용했지만,
리액트에서는 반드시 e.preventDefault()를 호출해야 합니다.


7. 이벤트 핸들러 내부에서 상태 변경

리액트에서는 이벤트를 통해 state를 직접 변경하지 않고,
setState / useState의 setter 함수를 사용해야 합니다.

function Counter() {
  const [count, setCount] = React.useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={handleClick}>+1</button>
    </div>
  );
}

버튼을 클릭하면 상태가 변경되고,
리액트가 알아서 UI를 다시 렌더링해줍니다.


8. 이벤트 처리의 핵심 요약

  • 리액트에서는 소문자 대신 CamelCase로 이벤트를 작성한다.
  • 이벤트 핸들러는 문자열이 아니라 함수를 전달한다.
  • 클래스형에서는 .bind(this) 또는 화살표 함수를 사용해야 한다.
  • preventDefault()로 기본 동작을 막을 수 있다.
  • 이벤트 내부에서는 **상태 변경 함수(setState / setCount)**로 데이터 변경을 처리한다.

9. 마무리

리액트의 이벤트 시스템은 일반 DOM 이벤트보다 조금 더 일관적이고 안전한 구조를 제공합니다.
핸들러 함수의 정의 방식만 익숙해지면 훨씬 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있습니다.

다음 강의에서는 리스트 렌더링과 key 속성의 원리를 다뤄보겠습니다.
리스트를 효율적으로 렌더링하는 방법과 key의 중요성을 직접 확인해보겠습니다.