React 독학 가이드 8 - Event Handling 완벽 가이드

learning by Seven Fingers Studio 16분
ReactEventonClickonChange이벤트처리

웹사이트에서 버튼 클릭하면 뭔가 동작하잖아요. 입력창에 글 쓰면 실시간으로 반응하고요. 이런 걸 이벤트 처리라고 해요. React에서 이벤트 다루는 법을 알아볼게요.

HTML vs React 이벤트 차이점

HTML에서 이벤트 처리하던 방식이랑 약간 달라요.

<!-- HTML 방식 -->
<button onclick="handleClick()">클릭</button>
// React 방식
<button onClick={handleClick}>클릭</button>

차이점 3가지:

  1. 이벤트명이 카멜케이스 (onclick → onClick)
  2. 함수를 문자열이 아닌 중괄호로 전달
  3. 함수를 호출하지 않고 전달 (handleClick() ❌ → handleClick ⭕)

onClick - 클릭 이벤트

가장 많이 쓰는 이벤트예요.

기본 사용법

function App() {
  const handleClick = () => {
    alert("버튼을 클릭했습니다!");
  };

  return <button onClick={handleClick}>클릭하세요</button>;
}

인라인으로 쓰기

간단한 동작은 바로 써도 돼요:

<button onClick={() => alert("클릭!")}>클릭</button>

주의: 함수 호출하면 안 됨

// ❌ 잘못된 방법 - 렌더링할 때 바로 실행됨!
<button onClick={handleClick()}>클릭</button>

// ⭕ 올바른 방법
<button onClick={handleClick}>클릭</button>

handleClick()처럼 괄호를 붙이면 컴포넌트가 렌더링될 때 바로 실행돼버려요.

매개변수 전달하기

함수에 값을 넘기고 싶으면 화살표 함수로 감싸세요:

function App() {
  const handleClick = (name) => {
    alert(`${name}님, 안녕하세요!`);
  };

  return (
    <div>
      <button onClick={() => handleClick("홍길동")}>홍길동</button>
      <button onClick={() => handleClick("김철수")}>김철수</button>
    </div>
  );
}

이벤트 객체 (event)

이벤트 핸들러는 자동으로 이벤트 객체를 받아요:

function App() {
  const handleClick = (e) => {
    console.log(e);           // 이벤트 객체
    console.log(e.target);    // 클릭된 요소
    console.log(e.type);      // 이벤트 타입 ("click")
  };

  return <button onClick={handleClick}>클릭</button>;
}

매개변수와 이벤트 객체 둘 다 받기

const handleClick = (id, e) => {
  console.log("ID:", id);
  console.log("이벤트:", e);
};

<button onClick={(e) => handleClick(123, e)}>클릭</button>

onChange - 입력 이벤트

입력창에서 값이 바뀔 때 발생해요:

function App() {
  const handleChange = (e) => {
    console.log(e.target.value);  // 입력된 값
  };

  return <input type="text" onChange={handleChange} />;
}

실시간 입력 표시

import { useState } from 'react';

function App() {
  const [text, setText] = useState("");

  const handleChange = (e) => {
    setText(e.target.value);
  };

  return (
    <div>
      <input type="text" onChange={handleChange} />
      <p>입력값: {text}</p>
    </div>
  );
}

입력할 때마다 바로 밑에 보여요!

onSubmit - 폼 제출

폼 제출할 때 발생하는 이벤트예요:

function LoginForm() {
  const handleSubmit = (e) => {
    e.preventDefault();  // 페이지 새로고침 방지!
    console.log("폼 제출됨");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="아이디" />
      <input type="password" placeholder="비밀번호" />
      <button type="submit">로그인</button>
    </form>
  );
}

e.preventDefault()가 중요해요! 안 쓰면 폼 제출하자마자 페이지가 새로고침돼요.

자주 쓰는 이벤트 목록

마우스 이벤트

<button onClick={handleClick}>클릭</button>
<button onDoubleClick={handleDoubleClick}>더블클릭</button>
<div onMouseEnter={handleEnter}>마우스 올리면</div>
<div onMouseLeave={handleLeave}>마우스 나가면</div>
<div onMouseMove={handleMove}>마우스 움직이면</div>

키보드 이벤트

<input onKeyDown={handleKeyDown} />   // 키 누를 때
<input onKeyUp={handleKeyUp} />       // 키 뗄 때
<input onKeyPress={handleKeyPress} /> // 글자 입력될 때 (deprecated)

function App() {
  const handleKeyDown = (e) => {
    if (e.key === "Enter") {
      console.log("엔터 눌림!");
    }
    if (e.key === "Escape") {
      console.log("ESC 눌림!");

    }
  };

  return <input onKeyDown={handleKeyDown} />;
}

포커스 이벤트

<input onFocus={handleFocus} />  // 포커스 받을 때
<input onBlur={handleBlur} />    // 포커스 잃을 때
function App() {
  const handleFocus = () => {
    console.log("입력창 선택됨");
  };

  const handleBlur = () => {
    console.log("입력창에서 나감");
  };

  return (
    <input
      onFocus={handleFocus}
      onBlur={handleBlur}
      placeholder="클릭해보세요"
    />
  );
}

실전 예제: 카운터 앱

import { useState } from 'react';

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

  const increase = () => setCount(count + 1);
  const decrease = () => setCount(count - 1);
  const reset = () => setCount(0);

  return (
    <div style={{ textAlign: 'center', padding: '20px' }}>
      <h1>{count}</h1>
      <button onClick={decrease}>-</button>
      <button onClick={reset}>리셋</button>
      <button onClick={increase}>+</button>
    </div>
  );
}

실행 결과:

7

버튼 클릭 이벤트로 카운터를 조작할 수 있습니다

실전 예제: 검색창

import { useState } from 'react';

function SearchBox() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);

  const fruits = ["사과", "바나나", "오렌지", "포도", "수박", "딸기"];

  const handleChange = (e) => {
    const value = e.target.value;
    setQuery(value);

    if (value.trim()) {
      const filtered = fruits.filter((fruit) =>
        fruit.includes(value)
      );
      setResults(filtered);
    } else {
      setResults([]);
    }
  };

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={handleChange}
        placeholder="과일 검색..."
        style={{ padding: '10px', width: '200px' }}
      />
      <ul>
        {results.map((fruit) => (
          <li key={fruit}>{fruit}</li>
        ))}
      </ul>
    </div>
  );
}

실시간으로 검색 결과가 필터링돼요!

실전 예제: 할 일 추가하기

import { useState } from 'react';

function TodoApp() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!input.trim()) return;

    setTodos([...todos, { id: Date.now(), text: input }]);
    setInput("");  // 입력창 비우기
  };

  const handleDelete = (id) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="할 일 입력..."
        />
        <button type="submit">추가</button>
      </form>

      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            {todo.text}
            <button onClick={() => handleDelete(todo.id)}>삭제</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

이벤트 버블링과 stopPropagation

클릭 이벤트는 부모까지 전파돼요(버블링):

function App() {
  const handleParent = () => console.log("부모 클릭");
  const handleChild = () => console.log("자식 클릭");

  return (
    <div onClick={handleParent} style={{ padding: '50px', background: '#ddd' }}>
      <button onClick={handleChild}>클릭</button>
    </div>
  );
}
// 버튼 클릭 시: "자식 클릭" -> "부모 클릭" 둘 다 출력됨

전파를 막으려면:

const handleChild = (e) => {
  e.stopPropagation();  // 부모로 이벤트 전파 막기
  console.log("자식 클릭");
};

이벤트 핸들러 네이밍 컨벤션

// 이벤트 핸들러 이름은 handle + 동작으로
const handleClick = () => {};
const handleSubmit = () => {};
const handleChange = () => {};
const handleInputChange = () => {};  // 더 구체적으로

// Props로 전달할 때는 on + 동작으로
<Button onClick={handleClick} />
<Form onSubmit={handleSubmit} />
<Input onChange={handleChange} />

다음 단계

이벤트 처리를 배웠으니, 다음 글에서는 State를 본격적으로 다뤄볼 거예요. 여기서 useState를 잠깐 썼는데, 다음 글에서 제대로 배워봅시다.

이벤트랑 State를 같이 쓰면 진짜 동적인 앱을 만들 수 있어요. 기대하세요!


React 시리즈 탐색:

← 블로그 목록으로