리액트 - 이벤트핸들링과 Hooks 기초

심영민·2025년 4월 10일
1

유레카

목록 보기
26/33

이번 포스팅에는 이벤트 핸들링과 컴포넌트 라이프사이클을 이해하여 Hooks의 기초를 다뤄보겠다.

이벤트 핸들링

리액트에서의 이벤트 핸들링은 자바스크립트에 직접 html요소에 적용하는 이벤트 핸들링과 차이가 있다.

정의

  • 사용자의 행동(클릭, 키보드 입력, 마우스 이동 등) 즉, 이벤트(Event) 가 발생했을 때, 이를 감지하고 미리 정의된 특정 JavaScript 함수(이벤트 핸들러) 를 실행시켜 상호작용에 반응하는 메커니즘임.

  • 동적으로 이벤트 처리할 때 필수임.

처리 방식 (함수 컴포넌트 기준)

주로 함수 컴포넌트 방식으로 사용되므로 함수 컴포넌트 기준으로 정리해보겠다.

  • 이벤트 리스너 등록: HTML 요소에 직접 on<이벤트이름> 형태로 속성을 부여하는 대신, JSX 요소에 카멜 케이스(camelCase) 형태의 이벤트 속성(예: onClick, onChange)을 사용함.

  • 핸들러 함수 연결: 이벤트 속성의 값으로는 문자열이 아닌, 실행될 JavaScript 함수(이벤트 핸들러) 자체를 {} 안에 넣어 전달함 (예: onClick={handleClick}).

  • 핸들러 함수 정의: 이벤트 핸들러 함수는 주로 해당 컴포넌트 내부에 함수로 선언됨.

    • 화살표 함수(const handleClick = () => { ... }) 또는 일반 함수 선언(function handleClick() { ... }) 모두 사용 가능함.
  • 이벤트 객체 (e) 활용: 이벤트 핸들러 함수는 호출될 때 자동으로 이벤트 객체(e)를 첫 번째 인자로 받음.

    • 이 객체는 이벤트에 대한 상세 정보(타입, 발생 위치, 발생 요소 등)를 담고 있음.
    • e.target: 이벤트가 실제로 발생한 DOM(html) 요소를 가리킴.
    • e.target.value: e.target의 실제 값으로, 주로 <input>, <textarea>, <select> 요소에서 현재 입력되거나 선택된 값을 가져올 때 사용함.
  • 상태 업데이트 연동: 이벤트 핸들러 내에서는 useState 훅으로 관리하는 상태를 상태 변경 함수(setState)를 이용해 업데이트함으로써 UI 변경을 유발하는 것이 일반적임.

  • 함수 이름으로 전달 (주로 쓰임)

     function MyComponent() {
       const handleClick = () => { console.log('클릭됨!'); };
       return <button onClick={handleClick}>클릭</button>;
     }

예시 코드 (함수 컴포넌트)

import React, { useState } from 'react';

function SimpleEventExample() {
  // useState 훅으로 하나의 상태(name) 관리, 초기값은 빈 문자열
  const [name, setName] = useState('');

  // input 값이 변경될 때 실행될 이벤트 핸들러 함수
  const handleChange = (e) => {
    // e.target.value로 현재 input 값 가져와서 name 상태 업데이트
    setName(e.target.value);
  };

  // 버튼 클릭 시 실행될 이벤트 핸들러 함수
  const handleClick = () => {
    // 현재 name 상태 값을 알림창으로 표시
    alert(`입력된 이름: ${name}`);
    // 필요하다면 상태 초기화
    // setName('');
  };

  // 화면에 렌더링될 JSX
  return (
    <div>
      <h1>간단한 이벤트 예제</h1>
      <input
        type="text"
        placeholder="이름을 입력하세요"
        value={name} // 상태와 input 값을 동기화 (제어 컴포넌트)
        onChange={handleChange} // 값이 변경될 때마다 handleChange 함수 호출
      />
      {/* 버튼 클릭 시 handleClick 함수 호출 */}
      <button onClick={handleClick}>확인</button>
      <p>현재 입력: {name}</p>
    </div>
  );
}

export default SimpleEventExample;

리액트 컴포넌트 반복 (데이터 배열 -> 컴포넌트 배열)

동적으로 렌더링 하는 리액트에서는 리스트를 하나하나 나열하기 보다는 map함수로 반복되는 컴포넌트를 렌더링할 수 있다.

정의

  • 배열 형태의 데이터를 기반으로 하여, 각 데이터 항목에 해당하는 여러 개의 리액트 컴포넌트를 동적으로 생성하고 화면에 렌더링하는 기법임.

  • 정적인 목록 대신, API 응답 등 동적으로 변하는 데이터를 화면에 표시하는 데 필수적임.

주로 map() 메소드 활용

  • JavaScript의 내장 배열 메소드인 map()을 사용하는 것이 핵심임.

  • map() 메소드는 배열의 각 요소를 순회하면서, 주어진 콜백 함수를 실행하고, 콜백 함수의 반환값들로 이루어진 새로운 배열을 생성함.

  • 리액트에서는 이 원리를 이용하여, 데이터 배열을 컴포넌트 배열로 변환함.

사용 방법

  1. 데이터 배열 준비
    const posts = [
      { id: 1, title: '첫 번째 글', content: '내용...' },
      { id: 2, title: '두 번째 글', content: '내용...' },
      { id: 3, title: '세 번째 글', content: '내용...' },
    ];
  2. JSX 내에서 map() 사용
    {posts.map(post => /* ... */)}
  3. 컴포넌트 변환
    {posts.map(post => (
      <PostItem key={post.id} title={post.title} content={post.content} />
      // 또는 간단히: <li key={post.id}>{post.title}</li>
    ))}
  4. 렌더링

key Prop의 중요성 및 규칙

key는 virtual DOM으로 변화를 감지할때 더욱 빠르게 알아낼 수 있기에 필수적이다.

  • 필수성: map()을 사용하여 엘리먼트 목록을 생성할 때는, 반드시 각 엘리먼트에 key prop을 포함시켜야 함. key는 리액트가 배열의 각 항목을 식별하는 데 사용하는 고유한 식별자임.

  • 역할: 리액트는 key를 통해 배열의 어떤 항목이 변경, 추가, 또는 삭제되었는지 효율적으로 파악하고, 최소한의 DOM 조작으로 화면을 업데이트함.

  • 권장 사항: 데이터 항목 자체에 포함된 고유한 ID (예: 데이터베이스의 기본 키, uuid 등)를 key 값으로 사용하는 것이 가장 좋음 (key={item.id}).

    • 인덱스는 원본 데이터가 수정될 수 있기에 주의하란다.

예시 코드 (함수 컴포넌트)

import React from 'react';

// 예시 데이터 배열 (각 객체는 고유한 id를 가짐)
const todos = [
  { id: 't1', text: '리액트 공부하기' },
  { id: 't2', text: '운동하기' },
  { id: 't3', text: '장보기' },
];

// 할 일 목록을 렌더링하는 컴포넌트
function TodoList() {
  return (
    <div>
      <h1>오늘 할 일</h1>
      <ul>
        {/* todos 배열을 map() 메소드로 순회 */}
        {todos.map((todo) => (
          // 각 todo 객체에 대해 li 엘리먼트를 반환함
          // *** 중요: map()으로 생성하는 각 엘리먼트에는 고유하고 안정적인 key prop을 반드시 지정해야 함 ***
          // 여기서는 각 todo 항목의 고유 id를 key로 사용
          <li key={todo.id}>
            {/* todo 객체의 text 속성 값을 화면에 표시 */}
            {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TodoList;

useState & useEffect

위 이미지는 클래스 컴포넌트의 라이프사이클 다이어그램이다.

함수 컴포넌트에서는 useStateuseEffect 훅(Hook)을 사용하여 이와 유사한 시점과 목적의 작업을 수행하고 요즘은 주로 함수 컴포넌트를 사용한다~~

useState Hook

  • 정의: 함수 컴포넌트 내에서 상태(state) 를 관리하기 위한 기본적인 훅임. 컴포넌트가 기억해야 할 값이자 변경 시 리렌더링을 유발하는 데이터를 다룸.

  • 사용법: const [상태 값, 상태를 설정하는 함수] = useState(상태의 기본 값);

    • useState(상태의 기본 값) 호출은 클래스 컴포넌트의 constructor에서 this.state를 초기화하는 것과 유사한 초기 상태 설정 역할임 (다이어그램의 "생성될 때" 단계의 시작 부분 해당).

    • 반환된 state는 현재 상태 값을 나타냄.

    • 반환된 setState 함수는 상태 값을 업데이트하는 함수임. 이 함수를 호출하는 것은 클래스 컴포넌트의 this.setState() 호출과 유사하며, 컴포넌트의 업데이트(리렌더링) 과정을 시도함.

  • 역할: 컴포넌트의 상태 정의, 초기화, 그리고 업데이트 트리거 역할을 수행함.

useEffect Hook

  • 정의: 함수 컴포넌트 내에서 부수 효과(Side Effects)를 수행하기 위한 훅임. (데이터 가져오기, 구독 설정/해제, DOM 직접 조작 등 렌더링 자체와 직접 관련 없는 작업들)

  • 라이프사이클과의 연관성: useEffect는 두 번째 인자인 의존성 배열 을 어떻게 설정하느냐에 따라 클래스 컴포넌트의 여러 라이프사이클 메서드의 목적과 유사한 시점에서 작업을 수행하도록 제어 가능함.
    useEffect의 effect 함수는 기본적으로 렌더링 결과가 화면(DOM)에 반영된 이후에 실행됨

    • 마운트 시 작업 (componentDidMount 유사):

      • useEffect(() => { /* 마운트 시 1회 실행할 작업 */ }, []);

      • 의존성 배열을 빈 배열([])로 설정하면, effect 함수는 컴포넌트가 처음 렌더링되어 DOM에 마운트된 직후 딱 한 번만 실행됨.

      • 주로 초기 데이터 로딩, 외부 라이브러리 연동, 이벤트 리스너/타이머/구독 설정 등에 사용됨 (다이어그램의 componentDidMount가 호출되는 시점에 수행할 작업과 유사).

    • 업데이트 시 작업 (componentDidUpdate 유사):

      • useEffect(() => { /* 특정 값 변경 시 실행할 작업 */ }, [dep1, dep2]);

      • 의존성 배열에 특정 값(dep1, dep2 등)을 넣으면, 마운트 시 1회 실행 + 지정된 의존성 중 하나라도 값이 변경되어 리렌더링될 때마다 다시 실행됨.

      • 특정 props나 state의 변경에 따라 부수 효과를 재실행해야 할 때 사용됨 (다이어그램에서 props나 state 변경 후 componentDidUpdate가 호출되는 시점에 수행할 작업과 유사).

    • 언마운트 시 정리 작업 (componentWillUnmount 유사):

      • useEffect(() => { /* 설정 작업 */ return () => { /* 정리 작업(cleanup) */ }; }, [상황에 맞는 의존성 배열]);

      • Effect 함수 내에서 함수를 반환(return)하면, 이 반환된 함수(클린업 함수)는 컴포넌트가 DOM에서 제거되기 직전(언마운트 시) 또는 effect가 재실행되기 직전에 호출됨.

      • 주로 useEffect에서 설정했던 구독 해제, 타이머 제거, 이벤트 리스너 제거 등 메모리 누수 방지를 위한 정리 작업에 사용됨 (다이어그램의 componentWillUnmount가 호출되는 시점에 수행할 작업과 유사).

    • 매 렌더링 시 작업 (거의 안씀):

      • useEffect(() => { /* 매 렌더링 시 실행할 작업 */ });

      • 의존성 배열을 생략하면, effect 함수는 컴포넌트가 렌더링될 때마다 실행됨 (마운트 시 포함).

      • 성능 문제를 유발하거나 무한 루프에 빠질 수 있으므로 거의 사용 X

profile
코딩너무어려운대 어떡할과 재학중

0개의 댓글