React 상호작용성 더하기 - 이벤트에 응답하기

Sally·2026년 2월 12일

이벤트에 응답하기

React에서는 JSX에 이벤트 핸들러를 추가할 수 있다.

  • 이벤트 핸들러: 클릭, 마우스 호버, 폼 인풋 포커스 등 사용자 상호작용에 따라 유발되는 사용자 정의 함수

이벤트 핸들러 추가하기

export default function Button() {
  return (
    <button>
      I don't do anything
    </button>
  );
}

아무런 동작도 수행하지 않는 버튼에 이벤트 핸들러를 추가하는 과정은 아래와 같다.

  1. Button 컴포넌트 내부에 handleClick 함수를 선언한다.
  2. 해당 함수 내부 로직을 구현한다. (ex. alert)
  3. <button> JSX에 onClick = {handleClick}을 추가한다.
export default function Button() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}
  • 이벤트 핸들러가 갖는 특징

    • 주로 컴포넌트 내부에서 정의된다
    • handle로 시작하고 그 뒤에 이벤트명을 붙인 함수명을 갖는다
    • ex. onClick = {handleClick}, onMouseEnter = {handleMouseEnter}

    주의

    이벤트 핸들러로 전달한 함수들은 호출이 아닌 전달되어야 한다.

  • 함수를 전달하기 (올바른 예시) : <button onClick={handleClick}>

  • 함수를 호출하기 (잘못된 예시) : <button onClick={handleClick()}>

    인라인으로 코드를 작성할 때에도 마찬가지이다.

  • 함수를 전달하기 (올바른 예시) : <button onClick={() => alert('...')}>

  • 함수를 호출하기 (잘못된 예시) : <button onClick={alert('...')}>

    • <button onClick={alert('...')}> 이렇게 작성하면, 버튼을 클릭할 때마다 실행되는 것이 아니라 컴포넌트가 렌더링될 때마다 실행된다.

이벤트 핸들러 내에서 Prop 읽기

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

export default function Toolbar() {
  return (
    <div>
      <AlertButton message="Playing!">
        Play Movie
      </AlertButton>
      <AlertButton message="Uploading!">
        Upload Image
      </AlertButton>
    </div>
  );
}

이벤트 핸들러를 Prop으로 전달하기

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

function PlayButton({ movieName }) {
  function handlePlayClick() {
    alert(`Playing ${movieName}!`);
  }

  return (
    <Button onClick={handlePlayClick}>
      Play "{movieName}"
    </Button>
  );
}

function UploadButton() {
  return (
    <Button onClick={() => alert('Uploading!')}>
      Upload Image
    </Button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <PlayButton movieName="Kiki's Delivery Service" />
      <UploadButton />
    </div>
  );
}

전체 구조

Toolbar (부모)
 ├─ PlayButton
 │    └─ Button
 └─ UploadButton
      └─ Button
  • Toolbar 컴포넌트가 PlayButtonUploadButton을 렌더링함.
    • PlayButtonhandlePlayClickButtononClick prop으로 전달함.
    • UploadButton() => alert('Uploading!')ButtononClick prop으로 전달함.
  • 최종적으로 Button 컴포넌트는 onClick prop을 받고, 이후 받은 prop을 브라우저 빌트인 의 onClick={onClick}으로 직접 전달함

이벤트 핸들러 Prop 명명하기

  • <button><div> 같은 빌트인 컴포넌트는 onClick과 같은 브라우저 이벤트 이름만을 지원하는 한펴, 사용자 정의 컴포넌트에서는 이벤트 핸들러 prop의 이름을 원하는 대로 명명할 수 있음.
  • 관습적으로 이벤트 핸들러 prop의 이름은 on으로 시작하여 대문자 영문으로 이어짐.

📍 컴포넌트가 여러 상호작용을 지원한다면 이벤트 핸들러 prop을 애플리케이션에 특화시켜 명명할 수 있다.

export default function App() {
return (
  <Toolbar
    onPlayMovie={() => alert('Playing!')}
    onUploadImage={() => alert('Uploading!')}
  />
);
}

function Toolbar({ onPlayMovie, onUploadImage }) {
return (
  <div>
    <Button onClick={onPlayMovie}>
      Play Movie
    </Button>
    <Button onClick={onUploadImage}>
      Upload Image
    </Button>
  </div>
);
}

function Button({ onClick, children }) {
return (
  <button onClick={onClick}>
    {children}
  </button>
);
}
  • 위의 예시에서는 Toolbar컴포넌트가 onPlayMovieonUploadImage 이벤트 핸들러를 받음.
    • onPlayMovie와 같이 prop이름을 애플리케이션별 상호작용에 기반하여 명명한다면 나중에 이를 어떻게 이용하게 될지에 대한 유연성을 제공할 수 있음.

이벤트 전파

  • 이벤트 핸들러는 해당 컴포넌트가 가진 어떤 자식 컴포넌트의 이벤트를 수신할 수도 있고, 이를 이벤트가 트리를 따라 "bubble"되거나 "전파된다"고 표현함.
      export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <button onClick={() => alert('Playing!')}>
        Play Movie
      </button>
      <button onClick={() => alert('Uploading!')}>
        Upload Image
      </button>
    </div>
  );
}
  • 둘 중 어느 버튼을 클릭하더라도 해당 버튼의 onClick이 먼저 실행되고, 이후 부모인 <div>onClick이 뒤이어 실행됨. 따라서 두 개의 메시지가 표현됨.
  • 🌱 참고 : 부여된 JSX 태그 내에서만 실행되는 onScroll을 제외한 React 내의 모든 이벤트는 전파됨

전파 멈추기

  • 이벤트 핸들러는 이벤트 오브젝트를 유일한 매개변수로 받음. 관습을 따르자면 "event"를 의미하는 e로 호출되는 것이 일반적이고, 이 오브젝트를 이벤트의 정보를 읽어들이는데 사용할 수 있음
  • 이러한 이벤트 오브젝트는 전파를 멈출 수 있게 해주는데, 이벤트가 부모 컴포넌트에 닿지 못하도록 막기 위해 Button컴포넌트와 같이 e.stopPropagation()을 호출할 수 있음.
function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation();
      onClick();
    }}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <Button onClick={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onClick={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

버튼 클릭 시 실행되는 과정
1. React가 에 전달된 onClick 핸들러를 호출
2. Button에 정의된 해당 핸들러는 다음을 수행함

  • e.stopPropagation() 을 호출하여 이벤트가 더 이상 bubbling 되지 않도록 방지
  • Toolbar 컴포넌트가 전달해 준 onClick 함수를 호출
  1. Toolbar 컴포넌트에서 정의된 위 함수가 버튼의 alert를 표시
  2. 전파가 중단되었으므로 부모인
    의 onClick은 실행되지 않음

기본 동작 방지하기

  • 일부 브라우저 이벤트는 그와 관련된 기본 브라우저 동작을 갖는데, 그 예로 <form>의 제출 이벤트가 그 내부의 버튼을 클릭하면 페이지 전체를 리로드하는 것이 있음.
export default function Signup() {
  return (
    <form onSubmit={() => alert('Submitting!')}>
      <input />
      <button>Send</button>
    </form>
  );
}
  • 이를 방지하기 위해 e.preventDefault()를 이벤트 오브젝트에서 호출할 수 있음
export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('Submitting!');
    }}>
      <input />
      <button>Send</button>
    </form>
  );
}

📍 e.stopPropagation()와 e.preventDefault() 구분

  • e.stopPropagation() : 이벤트 핸들러가 상위 태그에서 실행되지 않도록 멈춤
  • e.preventDefault() : 기본 브라우저 동작을 가진 일부 이벤트가 해당 기본 동작을 실행하지 않도록 방지

profile
sally

0개의 댓글