dev.syw

사용자 입력에 반응하고 변하는 데이터를 상태로 관리하기.

이벤트 처리와 state

지금까지는 화면을 "그리기"만 했습니다. 이제 클릭·입력 같은 사용자 행동에 반응하고, 시간에 따라 변하는 데이터를 state(상태) 로 다뤄 봅시다.

학습 목표

  • onClick 등 이벤트 핸들러를 연결할 수 있다.
  • state 가 무엇이고 왜 props와 다른지 안다.
  • state를 불변(immutable) 하게 업데이트할 수 있다.
  • 이전 state를 기반으로 안전하게 갱신할 수 있다.
  • 객체·배열 state를 올바르게 다룰 수 있다.

이벤트 핸들러 연결하기

JSX에서 이벤트는 카멜케이스 속성에 함수를 넘겨 처리합니다.

function Button() {
  function handleClick() {
    alert('클릭됨!');
  }
  return <button onClick={handleClick}>눌러보세요</button>;
}
  • onClick={handleClick} 처럼 함수 자체를 넘깁니다.
  • onClick={handleClick()} 는 렌더 시점에 바로 실행돼 버리니 주의하세요.
  • 인자를 넘기려면 화살표 함수로 감쌉니다: onClick={() => remove(item.id)}.
이벤트속성
클릭onClick
입력 변경onChange
폼 제출onSubmit
마우스 진입onMouseEnter
키 입력onKeyDown

state — 변하는 값 기억하기

화면에 보이면서 시간에 따라 바뀌는 값은 useState 로 관리합니다.

import { useState } from 'react';

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

  return (
    <button onClick={() => setCount(count + 1)}>
      {count} 번 클릭
    </button>
  );
}
  • useState(초깃값)[현재값, 변경함수] 를 반환합니다.
  • setCount 를 호출하면 컴포넌트가 다시 렌더링됩니다.

💡 TIP — props는 부모가 주는 "외부 입력", state는 컴포넌트가 스스로 관리하는 "내부 기억"입니다. 일반 변수에 값을 바꿔도 화면은 갱신되지 않습니다. 반드시 set 함수를 써야 React가 다시 그립니다.

불변하게 업데이트하기

React는 "이전 값과 새 값이 다른가"를 비교해 리렌더를 결정합니다. 그래서 기존 값을 직접 수정하면 안 되고, 새 값을 만들어 교체해야 합니다.

// ❌ 직접 수정 — 화면이 안 바뀜
user.name = '새이름';
setUser(user);

// ✅ 새 객체로 교체
setUser({ ...user, name: '새이름' });

이전 state 기반 업데이트

같은 이벤트에서 state를 여러 번 바꾸거나, 비동기 상황이라면 함수형 업데이트가 안전합니다.

// ❌ 한 클릭에 +1만 됨 (같은 count 값 3번 사용)
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);

// ✅ 항상 최신 값을 받아 +1 (총 +3)
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);

⚠️ 주의 — set 함수는 즉시 반영되지 않습니다. 바로 다음 줄에서 count 를 읽으면 아직 옛날 값입니다. 다음 값을 계산할 땐 prev => ... 형태를 쓰세요.

객체 state

스프레드 연산자로 기존 필드를 복사하고 바꿀 부분만 덮어씁니다.

function Form() {
  const [form, setForm] = useState({ name: '', email: '' });

  function updateName(e) {
    setForm((prev) => ({ ...prev, name: e.target.value }));
  }

  return <input value={form.name} onChange={updateName} />;
}

배열 state

배열도 마찬가지로 새 배열을 만들어 교체합니다. push, splice 같은 변경 메서드는 피하세요.

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

  const add = (text) =>
    setTodos((prev) => [...prev, { id: Date.now(), text }]);   // 추가

  const remove = (id) =>
    setTodos((prev) => prev.filter((t) => t.id !== id));       // 삭제

  const toggle = (id) =>
    setTodos((prev) =>
      prev.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) // 수정
    );

  return (/* ... */);
}
동작추천 메서드
추가[...arr, item]
삭제arr.filter(...)
수정arr.map(...)
정렬[...arr].sort(...) (복사 후)

요약

  • 이벤트는 onClick={함수} 처럼 함수 자체를 넘긴다.
  • 변하는 값은 useState 로 관리하고, set 함수로만 바꾼다.
  • state는 불변하게 — 새 객체·배열을 만들어 교체한다.
  • 이전 값을 기반으로 바꿀 땐 setX(prev => ...).
  • 배열은 push 대신 스프레드·filter·map 을 쓴다.

연습문제

  1. 버튼을 누를 때마다 1씩 줄어드는 카운터를 만들고, 0 미만으로는 안 내려가게 하세요.
  2. 한 번의 클릭으로 카운트를 +2 하도록 함수형 업데이트로 작성하세요.
  3. { name, age } 객체 state에서 나이만 1 증가시키는 버튼을 만드세요. (불변 업데이트)
  4. 입력창에 적은 텍스트를 "추가" 버튼으로 배열 state에 넣어 리스트로 보여주세요.
  5. 4번 리스트의 각 항목 옆에 "삭제" 버튼을 달아 filter 로 제거하세요.

힌트 — 1번은 setCount(prev => Math.max(0, prev - 1)). 4번 추가는 [...prev, 새항목], 삭제는 filter.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

React.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.