ReactNextCentral
Published on

리액트와 Next.js에서 사용자 상호작용 및 이벤트 핸들링 이해하기

웹 개발에서 사용자와의 상호작용은 애플리케이션을 동적이고 생동감 있게 만드는 핵심 요소입니다. 이 글은 Next.js의 서버 사이드 렌더링과 리액트의 클라이언트 사이드 렌더링에서의 사용자 이벤트 처리 방법과 리액트에서 기본 UI 컴포넌트와 이벤트 핸들링을 어떻게 구현하는지에 대해 알아봅니다.
리액트와 Next.js에서 사용자 상호작용 및 이벤트 핸들링 이해하기
Authors
Table of Contents

서론

웹 애플리케이션과 사용자 간의 상호작용은 오늘날 웹 경험의 핵심입니다. 초기 웹 페이지는 단순히 정보를 전달하는 수단이었지만 현재는 사용자가 동적으로 상호 작용할 수 있는 풍부한 인터페이스를 제공합니다. 이러한 변화는 HTML과 자바스크립트, 특히 바닐라 자바스크립트를 사용한 초기 개발 방식에서 더 발전된 현대적인 접근 방식으로의 전환을 필요로 했습니다.

기본적으로 HTML은 웹의 구조를 마련하고 자바스크립트는 그 구조 위에서 동적인 상호 작용을 가능하게 합니다. 예를 들어서 자바스크립트를 사용하여 버튼 클릭 시 알림을 표시하거나 데이터를 동적으로 불러와 웹 페이지에 표시하는 기능을 구현할 수 있습니다.

// HTML에서 버튼 요소
<button id="myButton">클릭하세요</button>

// JavaScript
document.getElementById('myButton').addEventListener('click', () => {
  alert('버튼이 클릭되었습니다!');
  // 데이터를 동적으로 불러와 웹 페이지에 표시하는 예제 코드
  fetch('https://jsonplaceholder.typicode.com/posts/1')
    .then(response => response.json())
    .then(json => {
      const dataDisplay = document.createElement('div');
      dataDisplay.textContent = `데이터: ${json.title}`;
      document.body.appendChild(dataDisplay);
    });
});

그러나 프로젝트의 규모가 커지면서 원시 자바스크립트만으로는 유지 관리가 어려워지고 개발 효율성이 떨어지는 문제가 발생했습니다. 이러한 문제를 해결하기 위해 Next.js와 리액트와 같은 현대적인 프레임워크 및 라이브러리가 등장했습니다. 이 도구들은 웹 개발 프로세스를 단순화하고 성능 최적화, 서버 사이드 렌더링, 코드 분할 등 현대 웹 개발에서 필요한 기능을 쉽게 구현할 수 있도록 도와줍니다.

Next.js는 리액트 기반의 프레임워크로 사전 렌더링, 정적 생성, 서버 사이드 렌더링 같은 기능을 제공하여 애플리케이션의 초기 로딩 시간을 단축하고 검색 엔진 최적화(SEO)를 개선합니다. 리액트는 사용자 인터페이스를 구축하기 위한 자바스크립트 라이브러리로 선언적인 뷰를 만들어 예측 가능하고 효율적인 방식으로 사용자와의 상호 작용을 관리할 수 있게 해줍니다.

리액트에서 위 코드에 대해 동일한 기능을 구현하기 위해 함수형 컴포넌트와 useStateuseEffect 훅을 사용할 수 있습니다. 여기서는 클릭 이벤트가 발생할 때마다 데이터를 불러오는 기능을 구현합니다:

import React, { useState } from 'react';

function FetchButton() {
  const [postData, setPostData] = useState('');

  const fetchData = () => {
    fetch('https://jsonplaceholder.typicode.com/posts/1')
      .then(response => response.json())
      .then(json => {
        setPostData(`데이터: ${json.title}`);
      });
  };

  return (
    <div>
      <button onClick={fetchData}>클릭하세요</button>
      {postData && <div>{postData}</div>}
    </div>
  );
}

export default FetchButton;

이 코드에서는 버튼 컴포넌트 내부에 fetchData 함수를 정의하고 이 함수는 API에서 데이터를 가져와 컴포넌트의 상태를 업데이트합니다. 사용자가 버튼을 클릭하면 fetchData 함수가 호출되며 가져온 데이터는 화면에 표시됩니다. useState 훅을 사용하여 컴포넌트의 상태를 관리하며 이 상태는 가져온 데이터를 저장하는 데 사용됩니다.

이처럼 Next.js와 리액트는 복잡한 웹 애플리케이션을 더 쉽게 구축하고 사용자 경험을 향상시키는 데 필수적인 도구입니다. 이들은 개발자가 보다 직관적이고 성능이 우수한 웹 애플리케이션을 제작할 수 있도록 지원합니다. 따라서 현대 웹 개발에서 Next.js와 리액트의 역할은 매우 중요하며 이를 통해 사용자와의 상호 작용을 한 차원 높은 수준으로 끌어올릴 수 있습니다.

리액트의 이벤트 핸들링과 신세틱 이벤트 이해하기

리액트에서 사용자와의 상호작용을 처리하는 것은 애플리케이션의 사용성과 직접적으로 관련되어 있습니다. 이를 위해 리액트는 고유한 이벤트 핸들링 시스템을 사용합니다. 이 시스템의 핵심에는 신세틱 이벤트(Synthetic Event)가 있습니다.

리액트의 이벤트 핸들링 시스템

리액트의 이벤트 핸들링 시스템은 신세틱 이벤트를 중심으로 구축되어 있습니다. 신세틱 이벤트는 브라우저의 기본 이벤트를 감싸는 리액트의 래퍼(wrapper) 객체입니다. 이 래퍼는 크로스 브라우징 이슈를 해결하고 리액트의 선언적인 자연스러움을 이벤트 핸들링에도 적용시킵니다.

신세틱 이벤트는 다양한 브라우저에서 일관된 이벤트 객체를 제공합니다. 이는 개발자가 다양한 환경에서 동일한 사용자 경험을 제공할 수 있도록 돕습니다. 신세틱 이벤트는 이벤트의 위임을 자동으로 처리하며, 리액트 이벤트 핸들러 내에서 event.preventDefault()와 같은 메소드를 사용할 수 있게 합니다.

신세틱 이벤트 사용 예시

import React from 'react';

function ExampleComponent() {
  const handleEvent = event => {
    console.log('이벤트 타입:', event.type);
    event.preventDefault();
  };

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

이 예시에서는 버튼 클릭 이벤트를 처리하는 함수 handleEvent를 정의하고 있습니다. 이 함수는 신세틱 이벤트 객체를 인자로 받아 콘솔에 이벤트 타입을 출력하고 기본 동작을 방지합니다. 리액트에서 이벤트 핸들러를 이런 식으로 작성함으로써 사용자 인터랙션에 따른 동작을 손쉽게 정의할 수 있습니다.

리액트의 이벤트 핸들링 시스템과 신세틱 이벤트를 통해 개발자는 사용자 상호작용을 효율적으로 관리하고 애플리케이션의 사용성을 향상시킬 수 있습니다. 이 시스템은 리액트가 제공하는 선언적 UI 구축 방식의 일부로 개발자가 보다 직관적이고 유지보수가 용이한 코드를 작성할 수 있도록 돕습니다.

리액트에서의 기본 UI 컴포넌트

리액트는 다양한 사용자 인터페이스를 구축하는 데 필요한 기본 UI 컴포넌트를 제공합니다. 이들 컴포넌트를 활용하면 개발자는 효율적으로 웹 애플리케이션을 구축할 수 있습니다. 여기에는 버튼, 입력 필드, 체크박스, 라디오 버튼, 드롭다운 메뉴, 텍스트 영역 등이 포함됩니다.

버튼

버튼은 사용자가 클릭할 수 있는 가장 기본적인 UI 요소입니다. 리액트에서 버튼은 이벤트 리스너와 함께 사용되어 사용자의 동작에 반응합니다.

<button onClick={() => console.log('클릭!')}>클릭하세요</button>

입력 필드

사용자로부터 텍스트 입력을 받기 위해 입력 필드를 사용합니다. useState 훅을 사용하여 입력 값의 상태를 관리할 수 있습니다.

import React, { useState } from 'react';

function TextInput() {
  const [inputValue, setInputValue] = useState('');

  return (
    <input
      type="text"
      value={inputValue}
      onChange={(e) => setInputValue(e.target.value)}
    />
  );
}

체크박스

체크박스는 사용자가 하나 이상의 항목을 선택할 수 있게 해줍니다. 선택된 항목의 상태 역시 useState로 관리됩니다.

import React, { useState } from 'react';

function Checkbox() {
  const [isChecked, setIsChecked] = useState(false);

  return (
    <label>
      <input
        type="checkbox"
        checked={isChecked}
        onChange={(e) => setIsChecked(e.target.checked)}
      />
      선택
    </label>
  );
}

라디오 버튼

라디오 버튼은 여러 선택지 중 하나를 선택할 수 있게 해줍니다. 각 버튼은 동일한 name 속성을 공유합니다.

import React, { useState } from 'react';

function RadioButton() {
  const [selectedOption, setSelectedOption] = useState('');

  return (
    <form>
      <label>
        <input
          type="radio"
          value="option1"
          checked={selectedOption === 'option1'}
          onChange={(e) => setSelectedOption(e.target.value)}
        />
        옵션 1
      </label>
      <label>
        <input
          type="radio"
          value="option2"
          checked={selectedOption === 'option2'}
          onChange={(e) => setSelectedOption(e.target.value)}
        />
        옵션 2
      </label>
    </form>
  );
}

드롭다운 메뉴

드롭다운 메뉴를 사용하면 사용자가 목록에서 하나의 옵션을 선택할 수 있습니다.

import React, { useState } from 'react';

function Dropdown() {
  const [selectedValue, setSelectedValue] = useState('');

  return (
    <select value={selectedValue} onChange={(e) => setSelectedValue(e.target.value)}>
      <option value="option1">옵션 1</option>
      <option value="option2">옵션 2</option>
    </select>
  );
}

텍스트 영역

텍스트 영역은 사용자가 여러 줄의 텍스트를 입력할 수 있는 컴포넌트입니다.

import React, { useState } from 'react';

function TextArea() {
  const [text, setText] = useState('');

  return (
    <textarea
      value={text}
      onChange={(e) => setText(e.target.value)}
    ></textarea>
  );
}

리액트의 이러한 기본 UI 컴포넌트들은 웹 애플리케이션의 다양한 사용자 인터페이스 요구사항을 충족시키기 위한 기반이 됩니다. 각 컴포넌트는 특정 사용 사례에 맞게 확장하거나 수정할 수 있어서 개발자는 더욱 유연하고 효과적인 웹 애플리케이션을 구축할 수 있습니다.

리액트에서의 사용자 이벤트 다루기

리액트는 사용자 상호작용을 다루는 다양한 이벤트 핸들러를 제공합니다. 이들을 통해 버튼 클릭부터 입력 변경과 폼 제출까지 사용자의 동작에 반응하여 애플리케이션을 동적으로 만들 수 있습니다.

클릭 이벤트: onClick

클릭 이벤트는 가장 일반적인 사용자 상호작용 중 하나입니다. 예를 들어, 버튼 컴포넌트에 클릭 이벤트를 추가하여 사용자가 버튼을 클릭할 때 특정 작업을 수행하도록 할 수 있습니다.

function MyButton() {
  return <button onClick={() => console.log('버튼 클릭!')}>클릭하세요</button>;
}

입력 변경 이벤트: onChange

입력 필드의 내용이 변경될 때마다 작업을 수행하려면 onChange 이벤트를 사용합니다. 이를 통해 사용자가 입력하는 동안 입력값을 검증하거나 상태를 업데이트할 수 있습니다.

function MyInput() {
  const [inputValue, setInputValue] = React.useState('');

  return (
    <input
      type="text"
      value={inputValue}
      onChange={(e) => setInputValue(e.target.value)}
    />
  );
}

폼 제출 이벤트: onSubmit

폼 제출 이벤트는 사용자가 폼을 제출할 때 발생합니다. 이 이벤트를 사용하여 예를 들어 사용자 입력을 검증하고 데이터를 서버에 전송할 수 있습니다.

function MyForm() {
  const handleSubmit = (e) => {
    e.preventDefault();
    // 폼 제출 로직
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">제출</button>
    </form>
  );
}

포커스 이벤트: onFocus & onBlur

포커스 이벤트를 사용하면 입력 필드에 포커스가 맞춰지거나 해제될 때 작업을 수행할 수 있습니다. 이는 사용자가 어떤 입력 필드에 집중하고 있는지 파악할 때 유용합니다.

function MyInput() {
  return (
    <input
      onFocus={() => console.log('입력 필드에 포커스됨')}
      onBlur={() => console.log('입력 필드에서 포커스 해제됨')}
    />
  );
}

이 외에도 onKeyPress, onKeyDown, onKeyUp 같은 키보드 이벤트와 onMouseMove, onMouseOver, onMouseOut 같은 마우스 이벤트, 그리고 터치 이벤트 등 리액트에서 다룰 수 있는 사용자 이벤트는 다양합니다. 각 이벤트는 사용자와의 상호작용을 풍부하게 하고 더 나은 사용자 경험을 제공하는 데 기여합니다.

Next.js에서 사용자 이벤트 핸들링과 조건부 스타일링 구현하기

Next.js에서 사용자 이벤트 핸들링의 이해와 use client 사용

Next.js는 리액트를 기반으로 한 프레임워크로 리액트에서의 이벤트 핸들링 방식을 그대로 사용합니다. 특히 Next.js 12에서 도입된 use client 지시어는 클라이언트 컴포넌트와 서버 컴포넌트 사이의 명확한 경계를 정의하는 데 사용됩니다.

use client 지시어를 파일 상단에 추가함으로써 해당 파일과 그 파일로부터 임포트되는 모든 모듈 또는 컴포넌트는 클라이언트 사이드에서만 실행됨을 명시합니다. 이는 클라이언트 사이드에서만 사용 가능한 API들, 예를 들어 useStateuseEffect 같은 리액트의 상태 관리 훅이나 이벤트 핸들러를 사용할 때 필요합니다.

'use client';

import React, { useState } from 'react';

function ToggleButton() {
  const [isActive, setIsActive] = useState(false);

  return (
    <button onClick={() => setIsActive(!isActive)}>
      {isActive ? '활성 상태' : '비활성 상태'}
    </button>
  );
}

이 예제에서 useState를 사용하여 컴포넌트의 상태를 관리하고 버튼 클릭 이벤트를 처리합니다. use client 지시어가 없다면 이러한 클라이언트 사이드 API를 사용하는 것이 불가능합니다. 왜냐하면 Next.js의 App 라우터는 기본적으로 모든 컴포넌트를 서버 컴포넌트로 간주하기 때문입니다.

use client의 중요성

Next.js에서는 "use client" 지시어를 통해 클라이언트 사이드에서만 실행되어야 하는 로직과 서버 사이드에서 실행되는 로직을 명확히 구분할 수 있습니다. 이를 통해 개발자는 서버 사이드 렌더링을 활용하여 애플리케이션의 성능을 최적화하면서도 클라이언트 사이드에서 필요한 동적인 상호작용을 구현할 수 있게 됩니다.

결론적으로, "use client" 지시어는 클라이언트 컴포넌트와 서버 컴포넌트의 경계를 명확하게 정의하며, Next.js 애플리케이션에서의 사용자 이벤트 핸들링을 가능하게 하는 핵심 요소입니다. 이를 통해 개발자는 리액트의 다양한 기능을 Next.js에서도 원활하게 활용할 수 있습니다.

clsx 라이브러리를 활용한 조건부 스타일링

clsx는 조건부로 클래스를 결합하는 데 사용되는 작고 빠른 유틸리티로 리액트에서 스타일을 동적으로 적용하는 데 유용합니다.

'use client';

import React, { useState } from 'react';
import clsx from 'clsx';

function ToggleButton() {
  const [isActive, setIsActive] = useState(false);

  return (
    <button
      className={clsx('base-style', { 'active-style': isActive, 'inactive-style': !isActive })}
      onClick={() => setIsActive(!isActive)}
    >
      {isActive ? '활성 상태' : '비활성 상태'}
    </button>
  );
}

이 예시에서 clsx를 사용하여 버튼의 isActive 상태에 따라 다른 스타일 클래스를 적용합니다. 이를 통해 사용자의 상호작용에 반응하는 동적인 UI를 구현할 수 있습니다.

상태 관리와 이벤트 핸들링의 통합

복잡한 상호작용을 구현할 때는 상태 관리와 이벤트 핸들링을 통합해야 할 필요가 있습니다. 예를 들어서 사용자 입력을 기반으로 서버에서 데이터를 가져와 UI를 업데이트하는 경우를 생각해 볼 수 있습니다.

'use client';

import React, { useState, useEffect } from 'react';

function UserData() {
  const [userId, setUserId] = useState(1);
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
      .then(response => response.json())
      .then(data => setUserData(data));
  }, [userId]);

  return (
    <div>
      <button onClick={() => setUserId(userId + 1)}>다음 사용자</button>
      <div>사용자 이름: {userData?.name}</div>
    </div>
  );
}

이 코드에서는 버튼 클릭 이벤트를 처리하여 userId 상태를 변경하고 변경된 userId를 기반으로 서버에서 새로운 사용자 데이터를 가져옵니다. 그리고 가져온 데이터를 화면에 표시합니다.

Next.js와 리액트를 사용하면 이처럼 상호작용이 풍부한 웹 애플리케이션을 효율적으로 구현할 수 있습니다. 사용자 이벤트를 핸들링하고 조건부 스타일링을 적용함으로써 동적이고 반응형의 인터페이스를 만들 수 있습니다.

결론

웹 애플리케이션의 사용자 경험을 높이는 데 있어 사용자 상호작용과 이벤트 핸들링은 중심적인 역할을 합니다. 사용자가 웹사이트와 상호작용하는 방식을 향상시키는 것은 단순히 정보를 제공하는 것을 넘어서 사용자가 웹사이트와 ‘대화’할 수 있게 만듭니다. 이는 사용자의 만족도를 크게 향상시키고 더욱 몰입감 있는 경험을 제공합니다.

리액트와 Next.js는 이러한 상호작용이 풍부한 웹 애플리케이션을 구축하는 데 있어 강력한 도구입니다. 리액트는 사용자 인터페이스를 구성하는 선언적이고 효율적인 방식을 제공하며 Next.js는 리액트 기반 애플리케이션에 서버 사이드 렌더링, 정적 사이트 생성 등의 기능을 추가하여 성능과 SEO 최적화를 도모합니다.

리액트와 Next.js를 사용하면 개발자는 사용자의 행동에 따라 동적으로 반응하는 웹 페이지를 쉽게 구현할 수 있습니다. 예를 들어, 사용자가 버튼을 클릭했을 때 새로운 데이터를 불러와 화면에 표시하거나 사용자의 입력에 따라 실시간으로 검색 결과를 업데이트하는 기능을 손쉽게 추가할 수 있습니다. 이러한 동적 상호작용은 사용자가 웹사이트를 더욱 적극적으로 탐색하고 사용하게 만듭니다.