본문 바로가기
3주차

JavaScript와 React에서의 이벤트(event) 처리 방식

by 두밥비 2025. 5. 1.

 

안녕하세요, 웹 YB 윤소은 입니다. 이번 아티클은 지난 세미나 시간에 언급되었던 클래스 컴포넌트와 함수 컴포넌트와 더불어, 자바스크립트와 리액트에서의 이벤트 처리 방식에 대해 다뤄보겠습니다 :)

 

서론

React에서 버튼을 눌렀는데 이벤트가 안 먹히는 경험을 많이 해보셨을 거라고 생각합니다. 이번 아티클에서는 리액트 개발을 시작하면서 자주 마주치는 this 바인딩 문제를 예제와 함께 쉽게 설명하고, 함수형 컴포넌트에서는 어떻게 간결하게 처리할 수 있는지도 알려드리려고 합니다!

 

이벤트는 사용자의 특정 행동(버튼 클릭, 키 입력 등)에 반응하는 중요한 요소입니다. JavaScript DOM 이벤트와 React 이벤트는 이벤트 이름 표기법, 함수 전달 방식, 그리고 this 바인딩에서 차이를 보입니다. 이 글에서는 이러한 차이점과 함께 bind 메서드, this 키워드의 사용법, 클래스 및 함수 컴포넌트에서 이벤트 핸들러를 구현하는 방법을 자세히 다뤄 보겠습니다.

 


Event?

프로그래밍에서의 Event = 사건
ex) 사용자가 버튼을 클릭한 사건 → 버튼 클릭 이벤트

 

 

DOM의 Event vs React의 Event

1) DOM의 Event 

<button onclick="activate()">
  Activate
</button>
  • 함수 이름을 문자열로 직접 작성합니다.
  • HTML 문서 안에서 글로벌 스코프에 있는 함수를 호출하는 구조입니다.
  • onclick은 소문자로 작성합니다.
  • 내부적으로 브라우저의 addEventListener를 통해 이벤트를 등록합니다.
  • 유지보수 및 확장성이 떨어지고, 스코프 관리가 어렵습니다.
단점: HTML과 JS가 분리되지 않아 코드가 혼잡해질 수 있습니다.

 

2) 리액트의 Event 

<button onClick={activate}>
  Activate
</button>
  • 함수 그 자체(참조)를 중괄호 {} 안에 전달합니다.
  • 이벤트 이름은 카멜 표기법(onClick) 을 사용합니다.
  • 이벤트 객체(event)는 첫 번째 인자로 자동 전달됩니다.
  • React의 이벤트는 브라우저 native 이벤트가 아닌 SyntheticEvent로 래핑되어 cross-browser 호환성을 제공합니다.
장점: 명확한 스코프, 함수 분리 가능, 재사용성 높음

 

비교 Dom의 Event 리액트의 Event
이벤트 이름 표기법 onclick onClick(카멜 표기법)
함수 전달 방식 문자열로 전달 함수 그대로 전달

 

→ DOM과 React 모두 Event가 있지만, 사용하는 방법이 조금 다릅니다! (물론 둘 다 사용자 입력(클릭, 입력 등)에 반응한다는 점은 같습니다.)


camelCase(카멜 표기법)

(그림에 있는 혹 → C)

카멜 표기법(Camel Case)은 프로그래밍 및 식별자 이름 작성 규칙 중 하나로, 다양한 프로그래밍 언어 및 플랫폼에서 사용되는 네이밍 규칙 중 하나입니다.

 

 

카멜 표기법의 주요 특징은 다음과 같습니다.

1. 단어의 첫 글자는 소문자로 시작합니다.
2. 두 번째 단어부터는 첫 글자를 대문자로 표기합니다.
3. 단어 사이에는 공백이나 특수 문자가 없고, 단어들을 연결하여 작성합니다.

 

카멜케이스(CamelCase)를 사용하는 이유

 

1. 가독성 향상: 여러 단어를 이어 붙일 때 각 단어의 첫 글자를 대문자로 구분해 읽기 쉽도록 구분할 수 있습니다.

2. 공백 대체: 프로그래밍 언어에서는 변수나 함수 이름에 공백을 사용할 수 없기 때문에, 이를 대체하는 방식으로 활용됩니다.

3. 일관성: 다양한 언어와 프레임워크에서 표준 네이밍 규칙으로 사용되어 코드 스타일의 통일성을 유지할 수 있습니다.

4. 명확성: 변수, 함수, 클래스 이름을 보다 직관적으로 나타낼 수 있어 코드 이해도를 높입니다.

5. 키워드와 구분: 언어의 예약어와 변수명이 겹치는 것을 피하는 데 도움이 됩니다.

 


bind 메서드 설명

bind()는 함수의 this 값을 명시적으로 설정하고, 새로운 함수를 반환하며, 객체 메서드를 다른 객체에 할당하거나, this 값을 고정하기 위해 사용합니다.
const boundFunction = originalFunction.bind(thisArg, arg1, arg2, ...);
  • thisArg: 함수 실행 시 this로 사용할 값
  • 새로운 함수는 원래 함수와 달리 this가 고정됩니다.
  • 즉시 실행되지 않고, 나중에 호출할 수 있습니다.
const obj = { value: 42 };
function showValue() { console.log(this.value); }
const boundFunc = showValue.bind(obj);
boundFunc(); // 출력: 42

 

활용 사례

  • 이벤트 핸들러에서 this 문제 해결
  • 특정 객체 컨텍스트에서 함수 호출

 

Event Handler (Event Listener)

어떤 사건이 발생했을 때 해당 사건을 처리하는 역할
사용자 이벤트(예: 클릭, 키 입력, 마우스 이동 등)에 대한 응답으로 실행되는 함수
  • 사용법: React에서 이벤트 핸들러는 JSX에서 onClick, onChange 등과 같은 속성에 연결됩니다.
  • this 바인딩 문제: 클래스 컴포넌트에서 this가 자동으로 이벤트 핸들러에 바인딩되지 않습니다.
  • 해결 방법: constructor에서 bind(this)로 명시적 바인딩하거나, 클래스 필드 문법으로 arrow function을 사용합니다.

 

예시

this.handleClick = this.handleClick.bind(this);

handleClick = () => { /* 함수 내용 */ };
이벤트 핸들러는 컴포넌트 상태(state) 및 속성(props)에 접근하여 UI를 동적으로 업데이트할 수 있습니다.

 

클래스 컴포넌트

class Toggle extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isToggleOn: true };

    // callback에서 'this'를 사용하기 위해서는 바인딩을 필수적으로 해줘야 합니다.
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? '켜짐' : '꺼짐'}
      </button>
    );
  }
}
  • 핸들 클릭 함수 정의: 일반 함수처럼 괄호와 중괄호를 사용하여 클래스 내부에 정의합니다.  
  • 바인딩 필요성: this.handleClick은 constructor 내부에서 bind 메서드를 사용해 this와 연결해야 합니다.  
    • JavaScript 클래스 함수는 기본적으로 this가 바인딩되지 않기 때문입니다.
  • 문제점: 바인딩하지 않으면 this.handleClick이 글로벌 스코프에서 undefined로 처리됩니다.
  • 결론: bind를 통해 this를 명확하게 바인딩해야 함수가 올바르게 동작합니다.

 

자바스크립트에서 this의 의미

자바스크립트 내에서 this는 말 그대로 ‘이것’이라고 합니다. 더 쉽게 말하자면 '누가 나를 불렀느냐'를 뜻한다고 합니다.
즉, 선언이 아닌 호출에 따라 달라진다는 겁니다.

 

this는 호출 방식, 함수 유형, 실행 컨텍스트에 따라 달라진다.

 

  • 일반 함수: this는 함수를 호출한 객체를 가리킵니다. (window 또는 undefined – 엄격 모드에서)
  • 메서드: this는 해당 메서드를 호출한 객체를 참조합니다.
  • Arrow function: this는 정의된 위치의 상위 스코프에서 this를 상속합니다.
  • 클래스: this는 생성된 인스턴스 객체를 가리킵니다.
  • 이벤트 핸들러: this는 이벤트가 바인딩된 DOM 요소 또는 클래스 인스턴스를 참조해야 하며, 명시적으로 bind 또는 Arrow function을 사용해 설정해야 합니다.

 

[일반 함수 호출]         [메서드 호출]              [arrow function]
  this → window         this → 호출한 객체         this → 외부 스코프(this)

 

Q. 만약 bind 사용이 번거롭거나 복잡하게 느껴진다면?

1. Class fields syntax 사용

클래스 내부에서 변수를 선언할 수 있는 문법
클래스 변수를 명확하게 정의
class MyButton extends React.Component {
  handleClick = () => {
    console.log('this is:', this);
  }

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

 

 

사용법

class Example {
field = 'value';
}

 

특징

  • 인스턴스 필드를 클래스 본문에 직접 선언이 가능합니다.
  • 초기값을 설정할 수 있습니다.
  • 화살표 함수와 함께 사용하여 this 바인딩 문제 해결 가능합니다.

 

2. Arrow function 사용

class MyButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 이렇게 하면 'this'가 바운드됩니다.
    return (
      <button onClick={() => this.handleClick()}>
        클릭
      </button>
    );
  }
}
class Example {
    handleClick = () => {
    console.log(this);
    };
}

 

특징

  • 화살표 함수는 this를 클래스 인스턴스에 자동으로 바인딩됩니다.
  • bind(this)를 명시적으로 사용할 필요가 없습니다.
  • 주로 이벤트 핸들러에서 사용합니다.
단, arrow function을 JSX 내부에 직접 정의할 경우, 컴포넌트가 리렌더링될 때마다 함수가 새로 생성되므로, 불필요한 렌더링이 발생할 수 있습니다. 성능이 민감한 상황에서는 `useCallback` 훅을 사용하는 것도 고려해보세요!

 

함수 컴포넌트

위에서 본 Toggle 컴포넌트(클래스 컴포넌트)를 함수 컴포넌트로 변환

function Toggle(props) {
  const [isToggleOn, setIsToggleOn] = useState(true);

  // 방법 1. 함수 안에 함수로 정의
  function handleClick() {
    setIsToggleOn((isToggleOn) => !isToggleOn);
  }

  // 방법 2. arrow function을 사용하여 정의
  const handleClick = () => {
    setIsToggleOn((isToggleOn) => !isToggleOn);
  }

  return (
    <button onClick={handleClick}>
      {isToggleOn ? "켜짐" : "꺼짐"}
    </button>
  );
}

 

이벤트 정의 방식

  1. 함수 안에 일반 함수로 선언하거나
  2. arrow function로 선언할 수 있습니다.

이벤트 사용 방식

  1. 클래스 컴포넌트와 달리, 함수 컴포넌트에서는 `this`를 사용할 필요가 없습니다.
  2. 따라서 이벤트 핸들러를 `onClick={handleClick}`과 같이 간단하게 직접 전달할 수 있습니다.

 

Event Handler의 Arguments 전달하기

용어 정리

1) Arguments - Event Handler에 전달할 데이터
2) Parameter(매개변수)

 

클래스 컴포넌트에서 인자 전달

// 1번 방식
<button onClick={(event) => this.deleteItem(id, event)}>삭제하기</button>

// 2번 방식
<button onClick={this.deleteItem.bind(this, id)}>삭제하기</button>

 

두 방법 모두 첫 번째 매개변수 id, 두 번째 매개변수 event(리액트의 이벤트 객체)가 전달됩니다.

  1. arrow function - 명시적으로 이벤트를 두 번째로 전달
  2. bind - event가 자동으로 id 이후에 두 번째로 전달

 

🔹 1번 방식 – Arrow function으로 직접 호출 

  • 클릭 시 익명 함수가 호출되면서 `this.deleteItem(id, event)`가 실행됩니다.
  • 이 방식은 가독성이 좋고 직관적이지만, 컴포넌트가 리렌더링될 때마다 새로운 함수가 생성되므로, 리스트 내 반복 요소에 쓰일 경우 성능에 영향을 줄 수 있습니다.
  • 간단한 경우, 성능 이슈가 없는 컴포넌트

 

🔹 2번 방식 – .bind(this, id)로 사전 바인딩 

  • bind(this, id)는 this.deleteItem 함수의 this를 현재 인스턴스로 고정하고, id를 첫 번째 인자로 고정한 새로운 함수를 반환합니다.
  • 클릭 시, event 객체는 두 번째 인자로 자동으로 전달됩니다.
  • 성능상 렌더링마다 함수를 새로 만들지 않으므로 리스트나 반복 요소에 상대적으로 안전하지만, 코드가 다소 길어지고 가독성이 떨어질 수 있습니다.
  • 반복 렌더링 컴포넌트, 성능 최적화가 중요한 상황에 사용하면 좋습니다.

 

🔹 비교

방식 장점 단점
arrow function 코드 간결, 명시적 event 전달 매 렌더링마다 함수 생성
bind 성능상 이점, 코드 재사용 可 가독성 떨어질 수 있음

 

 

함수 컴포넌트에서 인자 전달

function MyButton(props) {
  const handleDelete = (id, event) => {
    console.log(id, event.target);
  };

  return (
    <button onClick={(event) => handleDelete(1, event)}>
      삭제하기
    </button>
  );
}

 

 

클래스 컴포넌트 vs 함수 컴포넌트

Q. 클래스 컴포넌트와 함수 컴포넌트에서 이벤트 핸들링을 어떻게 다르게 하는가?
항목 클래스 컴포넌트 함수 컴포넌트
this 바인딩 필요함(bind 또는 arrow function) 필요 없음
state 사용 this.state와 this.setState useState
이벤트 핸들러 선언 메서드로 선언 + 바인딩 함수 안에서 선언
코드 길이 상대적으로 길고 복잡  짧고 간결 

 


결론

이벤트 핸들링은 사용자와 애플리케이션 간의 상호작용을 처리하는 프론트엔드 개발의 핵심 개념입니다. JavaScript와 React 모두 이벤트를 다루지만, 표기 방식, this 바인딩, 이벤트 객체의 형태 등에서 중요한 차이를 보입니다.

 

특히 클래스 컴포넌트에서는 this 바인딩 이슈로 인해 bind 메서드나 화살표 함수를 반드시 사용해야 합니다. 반면 함수 컴포넌트에서는 이러한 번거로움 없이 간결하게 이벤트를 정의하고 전달할 수 있어, 최근에는 함수 컴포넌트 방식이 더 보편화되고 있습니다.

React에서는 상황에 따라 다양한 방식으로 이벤트를 정의할 수 있습니다. 중요한 건 "내가 지금 어떤 컴포넌트를 쓰고 있으며, 어떤 방식이 더 명확하고 유지보수하기 쉬운가?"를 판단하는 것입니다. 이벤트에 인자를 함께 전달해야 하는 경우에도, 코드의 가독성과 성능을 함께 고려해야 합니다.

 

 

참고자료

 

이벤트 처리하기 – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

 

 

클래스형과 함수형 차이

클래스형 컴포넌트react 컴포넌트 선언하는 두가지 방식 중 하나. 클래스 컴포넌트 & 함수 컴포넌트현재 자주 사용하지 않지만, 사용하는 기업들이 있다. 그 프로젝트의 유지보수를 위해서는 클

velog.io