DEVELOPMENT/react

6. 리액트 기본 개념 다루기 - 2

Tiny Commit 2024. 11. 10. 08:37

 


 
 

4. 이벤트 처리하기

(1) 이벤트 핸들링과 이벤트핸들러

이벤트란 웹 페이지에서 일어나는 사용자의 행위
( 버튼 클릭, 페이지 스크롤, 새로고침, 텍스트 입력) 

이벤트 핸들링은 이벤트가 발생하면 특정 코드가 동작하도록 만드는 작업입니다.
( 버튼을 클릭했을 때 경고 대화상자를 브라우저에 표시하는 동작)


다음은 리액트를 사용하지 않고 HTML과 자바스크립트만으로 이벤트를 핸들링하는 예입니다.

<script>
  function handleOnClick() { // 이벤트 핸들러: 이벤트를 처리하는 함수
    alert("button clicked!");
  }
</script>

<button onclick="handleOnClick()"> // 이벤트 발생시 함수 실행
	Click Me!
</button>

 
 
 
 
 
 

(2) 리액트의 이벤트 랜들링

function Body() {
  function handleOnClick() { // 함수
    alert("버튼을 클릭하셨군요!");
  }

  return (
    <div className="body">
      <button onClick={handleOnClick}>클릭하세요</button> // 버튼 생성, 함수호출
    </div>
  );
}
export default Body;

 

 

 

 

 

// HTML, 자바스크립트를 사용할 때의 이벤트 핸들러 설정 (소괄호 포함)
<button onclick="handleOnClick()"> 
Click Me!
</button>

// 리액트를 사용할 때의 이벤트 핸들러 설정 (함수 이름만 명시)
<button onClick={handleOnClick}>클릭하세요</button> // 함수 그 자체를 전달




(3) 이벤트 객체 사용하기

 리액트에서는 이벤트가 발생하면 이벤트 핸들러에게 이벤트 객체를 매개변수로 전달합니다. 이벤트 객체에는 이벤트가 어떤 요소에서 어떻게 발생했는지에 관한 정보가 상세히 담겨 있습니다.
 

function Body() {
  function handleOnClick(e) { // 버튼 이벤트 핸들러 handleOnClick 생성, 이벤트 객체(e)를 매개변수에 저장
    console.log(e.target.name); // 프로퍼티 값을 콘솔에 출력
  }
  return (
    <div className="body">
      <button name="A버튼" onClick={handleOnClick}> // 핸들러 handleOnClick 설정 및 A버튼 만들기
        A 버튼
      </button>
      <button name="B버튼" onClick={handleOnClick}>  // 핸들러 handleOnClick 설정 및 B버튼 만들기
        B 버튼
      </button>
    </div>
  );
}

export default Body;


이벤트 객체의 target 프로퍼티에는 이벤트가 발생한 페이지의 요소(여기서는 버튼)가 저장됩니다. 따라서 A 버튼을 클릭하면 e.target에는 A 버튼이 저장되고, B 버튼을 클릭하면 e.target에는 B 버튼이 저장됩니다. 따라서 함수 handleOnClick에서 e.target.name을 콘솔에 출력하면 현재 이벤트가 발생한 요소의 name 속성값을 출력하게 됩니다.

 

 

 

 

 

 

 

 

 

 

 

 



이벤트 객체 정보 확인하기

function Body() {
  function handleOnClick(e) {
    console.log(e); // 함수 실행시, 매개변수에 저장되어있던 e값 출력
    console.log(e.target.name);
  }
  return (
    <div className="body">
      <button name="A버튼" onClick={handleOnClick}>
        A 버튼
      </button>
      <button name="B버튼" onClick={handleOnClick}>
        B 버튼
      </button>
    </div>
  );
}

export default Body;

 

 



콘솔에 출력된 이벤트 객체를 살펴보면 상당히 많은 프로퍼티가 저장되어 있음을 알 수 있습니다. 그러나 아주 복잡한 이벤트 처리가 아니라면 실무에서는 대체로 1~2개의 값만 활용하므로 이 값들을 모두 상세히 알 필요는 없습니다.


 

 

 

 

5. 컴포넌트와 상태

사용자의 행위나 시간 변동에 따라 값이 변하는 동적인 리액트 컴포넌트

State를 이용해 동적인 컴포넌트를 만드는 방법

 


 (1) State 이해하기

State는 상태라는 뜻으로, 어떤 사물의 형편이나 모양을 일컫는다.
 
< 전구와 스위치 >

전구의 상태변화 State
전구의 상태는 소등과 점등으로 나눌 수 있다.  전구 State는 off(소등), on(점등) 둘 중 하나의 값을 갖는다. 
소등 상태일 때 스위치를 켜면 ‘점등’으로 상태 변화가 일어남. 전구 State의 값이 off일 때 스위치를 켜면 값이 on으로 바뀜,
점등 상태일 때 스위치를 끄면 ‘소등’으로 상태 변화가 일어남, 전구 State의 값이 on일 때 스위치를 끄면 값이 off로 바뀐다. 





 (2) State의 기본 사용법

 a. useState로 State 생성하기 : 현재 상태의 값(  State 변수 )과 업데이트하는 함수가 담긴 배열(set 함)을 반환 

useState의 용법
const [light, setLight] = useState('off'); // ‘off’를 전달했으므로 State 변수 light의 초깃값은 off가 됩니다.
      State 변수 set 함수 생성자(초깃값) // useState를 호출할 때 인수로 값을 전달하면 이 값이 State의 초깃값이 됩니다.

 

 


숫자를 카운트할 수 있는 State 변수 count를 생성

import { useState } from "react"; // State를 만드는 함수, react 라이브러리에서 불러옴

function Body() {
  const [count, setCount] = useState(0); // 함수 useState는 인수로 State의 초깃값 0을 전달
  return (
    <div>
      <h2>{count}</h2>
    </div>
  );
}

export default Body;

// 그 결과 State 변수 count와 set 함수 setCount를 반환합니다.




b. set 함수로 State값 변경하기
컴포넌트에서 버튼을 하나 만들고, 버튼을 클릭할 때마다 State(count) 값을 1씩 늘리

import { useState } from "react";

function Body() {
  console.log("Update!");
  const [count, setCount] = useState(0); 

  const onIncrease = () => { // 이벤트 핸들러에서 set함수호출, 인수로 count +1을 전달
    setCount(count + 1);
  };

  return (
    <div>
      <h2>{count}</h2>
      <button onClick={onIncrease}>+</button>
    </div>
  );
}
export default Body;


컴포넌트의 업데이트: 변경값을 페이지에 반영하기 위해 컴포넌트를 다시 렌더링 (set 함수를 호출해 State 값을 변경하면)

 

컴포넌트가 페이지에 렌더링하는 값은 컴포넌트 함수의 반환값 입니다. 따라서 컴포넌트를 다시 렌더링한다고 함은 컴포넌트 함수를 다시 호출한다는 의미와 같습니다.

 

<+> 버튼을 클릭할 때마다 Body 컴포넌트는 자신이 관리하는 State 값이 변하면 다시 호출됩니다. 그리고 변경된 State 값을 페이지에 렌더링( 리런터 or 리렌더링 )합니다. 


리액트 컴포넌트는 자신이 관리하는 State 값이 변하면 자동으로 리렌더됩니다.


 

 

 

 



 (3) State로 사용자 입력 관리하기

<HTML>
- <Input> 태그: 웹 사이트 - 입력 폼 - 텍스트, 숫자, 날짜 등의 정보를 입력
- <Select> 태그: 여러 옵션에서 하나를 선택하도록 드롭다운(DropDown) 목록을 보여주기 
- <Textarea> 태그: 여러 줄의 텍스트를 입력

리액트에서 State를 이용하면 다양한 입력 폼에서 제공되는 사용자 정보를 효과적으로 처리할 수 있습니다.

 


a. <input> 태그로 텍스트 입력하기

텍스트, 전화번호, 날짜, 체크박스 등 여러 형식의 정보를 입력

 

 

 

<input> 태그로 텍스트를 입력하는 폼을 하나 만들고, 사용자가 텍스트를 입력할 때마다 콘솔에 출력하는 이벤트 핸들러를 구현

import { useState } from "react";

function Body() {
  const handleOnChange = (e) => { // 입력폼에서 이벤트 핸들러 함수 만들기
    console.log(e.target.value);
  };
  return (
    <div>
      <input onChange={handleOnChange} /> // 텍스트를 입력할 폼 만들기, onChange 이벤트 핸들러로 handleOnChange를 설정
    </div>
  );
}
export default Body;

// onChange 이벤트는 사용자가 입력 폼에서 텍스트를 입력하면 바로 발생합니다.

텍스트를 입력하는 즉시 콘솔에서도 입력한 텍스트를 출력합니다.

 

 

<input> 태그의 type 속성

text: 텍스트 폼 - default, date: 날짜 형식의 폼, tel: 전화번호 형식의 폼, 라디오 버튼, 체크박스

 

 

 

 

 

State를 하나 만들고 사용자가 폼에서 입력할 때마다 텍스트를 State 값으로 저장 

import { useState } from "react";

function Body() { 
  const [text, setText] = useState(""); // 빈 문자열을 초깃값으로 하는 State 변수 text를 생성
  const handleOnChange = (e) => {
    setText(e.target.value); // 텍스트를 변경할 때마다 set 함수를 호출해 text 값을 현재 입력한 텍스트로 변경
  };
  return (
    <div>
      <input value={text} onChange={handleOnChange} /> // <input> 태그의 value 속성에 변수 text를 설정
      <div>{text}</div> // 변수 text의 값을 페이지에 렌더링
    </div>
  );
}

export default Body;

 

사용자 텍스트 입력 -> onChange이벤트가 발생 -> 이벤트 핸들러 handleOnChnage호출 -> handleOnChnage내부 set함수 호출 -> 인수로 사용자 입력 텍스트 전달 -> text에 저장되서 State값을 업데이트 -> state값 변경 -> 컴포넌트 자동 리렌더

 

 

 

b. <input> 태그로 날짜 입력하기

<input> 태그에서 type 속성을 "date"로 설정

아이콘을 클릭해 날짜를 바꾸면서 State 값이 콘솔에서도 변경된 날짜가 바로 출력됩니다.

이벤트가 발생했을 때 이벤트 객체의 e.target.value에는 문자열로 이루어진 yyyy-mm-dd 형식의 날짜가 저장

별도의 처리 없이도 텍스트 폼에서 입력할 때 처럼 State 값을 날짜 형식으로 저장할 수 있습니다. 

 

 

 

c. 드롭다운 상자로 여러 옵션 중에 하나 선택하기

<select> 태그는 <option>과 함께 사용합니다.

이 폼 목록에서 하나를 선택하면 해당 항목을 입력할 수 있습니다.

 

import { useState } from "react";

function Body() {
  const [option, setOption] = useState("");
  const handleOnChange = (e) => {
    console.log("변경된 값: ", e.target.value);
    setOption(e.target.value);
  };

  return (
    <div>
      <select value={option} onChange={handleOnChange}>
        <option key={"1번"}>1번</option>
        <option key={"2번"}>2번</option>
        <option key={"3번"}>3번</option>
      </select>
    </div>
  );
}
export default Body;

 

다롭다운: 사용자 옵션 변경 -> onChange이벤트 발생 -> 이벤트 행들러의 이벤트 객체에 key속성이 저장된다.-> 이 값으로 현재 State에 저장된 값을 변경한다.

 

 

 

 

 

 

 

 

d. 글상자로 여러 줄의 텍스트 입력하기

<textarea> 태그는 사용자가 여러 줄의 텍스트를 입력할 때 사용하는 폼을 만듭니다.

 

import { useState } from "react";

function Body() {
  const [text, setText] = useState("");
  const handleOnChange = (e) => {
    console.log("변경된 값 : ", e.target.value);
    setText(e.target.value);
  };

  return (
    <div>
      <textarea value={text} onChange={handleOnChange} />
    </div>
  );
}
export default Body;ㅍ

 

사용자 텍스트 입력 -> onChange이벤트 발생 -> 히벤트 핸들러에 이벤트 객ㅊ체 e.target.value에 저장된 값을 인수로 전달해 State값 변경

 

글자를 입력할 때마다 콘솔에서는 입력한 텍스트가 바로 표시됩니다.

 

 

 

 

 

e. 여러 개의 사용자 입력 관리하기

입력폼이 여러개 일때

import { useState } from "react";

function Body() {
  const [name, setName] = useState("");
  const [gender, setGender] = useState("");
  const [birth, setBirth] = useState("");
  const [bio, setBio] = useState("");

  const onChangeName = (e) => {
    setName(e.target.value);
  };
  const onChangeGender = (e) => {
    setGender(e.target.value);
  };
  const onChangeBirth = (e) => {
    setBirth(e.target.value);
  };
  const onChangeBio = (e) => {
    setBio(e.target.value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={onChangeName} placeholder="이름" /> // 이름을 받고, State(name)으로 관리
      </div>
      <div>
        <select value={gender} onChange={onChangeGender}> // 드롭다운 폼에서 성별을 받고, State(gender)로 관리
          <option key={""}></option>
          <option key={"남성"}>남성</option>
          <option key={"여성"}>여성</option>
        </select>
      </div>
      <div>
        <input type="date" value={birth} onChange={onChangeBirth} /> // 입력 폼에서 생년월일을 받고, State(birth)로 관리
      </div>
      <div>
        <textarea value={bio} onChange={onChangeBio} /> // 글상자에서 자기소개 내용을 받고, State(bio)로 관리
      </div>
    </div>
  );
}
export default Body;

총 4개의 State 변수와 이벤트 핸들러를 생성

 

 

 

 

사용자로부터 여러 입력 정보를 받아 State로 처리하는 경우, 관리할 State의 개수가 많아지면 코드의 길이 또한 길어집니다. 객체 자료형을 이용하면 입력 내용이 여러 가지라도 하나의 State 에서 관리할 수 있어 더 간결하게 코드를 작성할 수 있습니다.

import { useState } from "react";

function Body() {
  const [state, setState] = useState({ // 객체 자료형으로 State, 초깃 값은 모두 공백 문자열("")이며, name, gender, birth, bio 프로퍼티가 있습니다.
    name: "",
    gender: "",
    birth: "",
    bio: "",
  });

  const handleOnChange = (e) => {
    console.log("현재 수정 대상:", e.target.name);
    console.log("수정값:", e.target.value);
    setState({
      ...state,
      [e.target.name]: e.target.value,
    });
  };

  return (
    <div>
      <div>
        <input
          name="name" // 모든 입력 폼에서 name 속성을 지정
          value={state.name} // 객체 state의 name 프로퍼티와 동일한 값으로 설정
          onChange={handleOnChange} // 사용자의 입력을 처리할 이벤트 핸들러 handleOnChange를 생성
          placeholder="이름"
        />
      </div>
      <div>
        <select name="gender" value={state.gender} onChange={handleOnChange}>
          <option key={""}></option>
          <option key={"남성"}>남성</option>
          <option key={"여성"}>여성</option>
        </select>
      </div>
      <div>
        <input
          name="birth"
          type="date"
          value={state.birth}
          onChange={handleOnChange}
        />
      </div>
      <div>
        <textarea name="bio" value={state.bio} onChange={handleOnChange} />
      </div>
    </div>
  );
}

export default Body;

 

기존 객체 state의 값을 나열합니다. 그리고 객체의 괄호 표기법을 사용하여 입력 폼의 name 속성(e.target.name)을 key로, 입력 폼에 입력한 값(e.target.value)을 value로 저장합니다.

e.target.name은 현재 이벤트가 발생한 요소의 name 속성입니다. 예를 들어 성별을 입력하는 <select> 태그에서 onChange 이벤트가 발생했다면, e.target.name은 gender가 됩니다. 결국 객체 state의 4가지 프로퍼티 중 현재 이벤트가 발생한 요소인 gender 프로퍼티의 value 값을 변경하게 됩니다. 객체 자료형을 이용하면 하나의 State로 여러 개의 입력을 동시에 관리할 수 있습니다. 

 

 

 

 

 

 

 

 

 







 (4) Props와 State

State 역시 일종의 값이므로 Props로 전달할 수 있습니다.

 

 

Body에 자식 컴포넌트를 만들고, Body의 State를 Props로 전달

import "./Body.css";
import { useState } from "react";

function Viewer({ number }) { // Viewer 컴포넌트를 선언, Props로 Body 컴포넌트에 있는 State 변수 number가 전달됩니다.
  return <div>{number % 2 === 0 ? <h3>짝수</h3> : <h3>홀수</h3>}</div>;
}

function Body() {
  const [number, setNumber] = useState(0);

  const onIncrease = () => {
    setNumber(number + 1);
  };
  const onDecrease = () => {
    setNumber(number - 1);
  };

  return (
    <div>
      <h2>{number}</h2>
      <Viewer number={number} />
      <div>
        <button onClick={onDecrease}>-</button>
        <button onClick={onIncrease}>+</button>
      </div>
    </div>
  );
}
export default Body;


// Viewer 컴포넌트는 조건부 렌더링을 이용해 변수 number의 값을 평가하고, 값에 따라 짝수 또는 홀수 값을 페이지에 렌더링합니다.
// Body에서 Viewer를 자식 컴포넌트로 사용하며, Props로 변수 number를 전달

 

 

 

 


  (5) State와 자식 컴포넌트

부모의 State 값이 변하면 해당 State를 Props로 받은 자식 컴포넌트 역시 리렌더된 다는걸 알았습니다. 

 

import { useState } from "react";

function Viewer() { // Viewer 컴포넌트가 Props를 받지 않습니다.
  console.log("viewer component update!");
  return <div>Viewer</div>;
}

function Body() {
  const [number, setNumber] = useState(0);
  const onIncrease = () => {
    setNumber(number + 1);
  };
  const onDecrease = () => {
    setNumber(number - 1);
  };

  return (
    <div>
      <h2>{number}</h2>
      <Viewer /> // Body도 더 이상 Viewer 컴포넌트에 Props로 State를 전달하지 않습니다.
      <div>
        <button onClick={onDecrease}>-</button>
        <button onClick={onIncrease}>+</button>
      </div>
    </div>
  );
}

export default Body;

 

 

첫 번째 출력은 Viewer 컴포넌트를 페이지에 처음 렌더링할 때 출력된 것입니다. 나머지 5번은 부모인 Body 컴포넌트의 State가 변할 때마다 출력되었습니다.
리액트에서는 부모 컴포넌트가 리렌더하면 자식도 함께 리렌더됩니다. 사실 지금의 Viewer는 Body 컴포넌트의 State가 변한다고 해서 리렌더할 이유가 없습니다. Viewer 컴포넌트의 내용에는 변한 게 없기 때문입니다. 
의미 없는 리렌더가 자주 발생하면 웹 브라우저의 성능은 떨어집니다. 따라서 컴포넌트의 부모-자식 관계에서 State를 사용할 때는 늘 주의가 필요합니다. 리액트에서는 이런 성능 낭비를 막는 최적화 기법이 있는데, 이는 추후에 살펴보겠습니다.

 

 

 

 

 

6. Ref

(1) useRef 사용하기

리액트의 Ref를 이용하면 돔(DOM) 요소들을 직접 조작할 수 있다.

리액트에서는 useRef라는 리액트 함수를 이용해 Ref 객체를 생성

 

 

State 변수 text로 관리하는 텍스트 입력 폼 하나와 버튼 하나를 생성합니다.

import { useState } from "react";

function Body() {
  const [text, setText] = useState("");

  const handleOnChange = (e) => {
    setText(e.target.value);
  };

  const handleOnClick = () => {
    alert(text);
  };

  return (
    <div>
      <input value={text} onChange={handleOnChange} />
      <button onClick={handleOnClick}>작성 완료</button>
    </div>
  );
}
export default Body;

 

버튼 클릭 -> 이벤트 핸들러 handleOnClick이 실행되어 입력 폼에서 작성한 텍스트를 메세지 대화상자에 표시

 

 

 

 

 

 

 

 

 

a. useRef로 입력 폼 초기화하기

웹 서비스 - 로그인 페이지 - ID와 패스워드를 입력 -> 로그인 버튼을 클릭 -> 패스워드가 올바른지 점검 -> 패스워드 입력 폼에서 작성한 값을 초기화
리액트에서 Ref를 이용하면 이런 동작을 수행할 수 있습니다.


이번에는 useRef를 이용해 텍스트 입력 폼을 초기화하는 법을 알아보겠습니다. 

import { useRef, useState } from "react"; // react 라이브러리에서 불러오기

function Body() { // 인수로 전달한 값을 초깃값으로 하는 Ref 객체를 생성
  const [text, setText] = useState("");
  const textRef = useRef(); // 생성한 Ref를 상수 textRef에 저장

  const handleOnChange = (e) => {
    setText(e.target.value); // textRef가 돔 입력 폼에 접근하도록 설정
  };
  const handleOnClick = () => {
    alert(text);
    textRef.current.value = ""; // 대화상자에서 <확인> 버튼을 클릭 하면, textRef.current(textRef가 현재 참조하고 있는 돔 요소)의 value 값을 공백 문자열로 초기화
    };

  return (
    <div>
      <input ref={textRef} value={text} onChange={handleOnChange} />
      <button onClick={handleOnClick}>작성 완료</button>
    </div>
  );
}
export default Body;

 

 

b. useRef로 포커스하기

웹 서비스 - 내용 입력x, 내용이 정한 길이보다 짧으면 - 해당 폼을 포커스(focus)하여 사용자의 추가 입력을 유도. 
리액트의 Ref 기능을 이용하면 특정 요소에 포커스 기능을 지정할 수 있습니다.


이번에는 텍스트 입력 폼에서 사용자가 문자를 다섯 글자 미만으로 입력하면 이 요소에 포커스한 상태로 사용자가 입력을 추가할 때까지 대기합니다. 

import { useRef, useState } from "react";

function Body() {
  const [text, setText] = useState("");
  const textRef = useRef();

  const handleOnChange = (e) => {
    setText(e.target.value);
  };

  const handleOnClick = () => {
    if (text.length < 5) {
      textRef.current.focus(); // 다섯 글자보다 적다면 textRef.current가 참조하는 입력 폼에 포커스를 실행
    } else {
      alert(text);
      setText(""); // 입력한 값을 초기화하기 위해 set 함수 setText를 호출하고 인수로 빈 문자열을 전달
    }
  };

  return (
    <div>
      <input ref={textRef} value={text} onChange={handleOnChange} />
      <button onClick={handleOnClick}>작성 완료</button>
    </div>
  );
}
export default Body;

 

focus()는 현재 돔 요소에 포커스를 지정하는 메서드

 

 

 

 

 

 

<리액트 훅 >

리액트 훅(React Hook)이란 함수로 만든 리액트 컴포넌트에서 클래스로 만든 리액트 컴포넌트의 기능을 이용하도록 도와주는 함수들입니다.
앞서 State를 만드는 함수 useState와 참조 객체를 만드는 함수 useRef는 모두 리액트 훅입니다. 이 두 함수 모두 이름이 use로 시작하는데, 리액트 훅은 이름 앞에 항상 use를 붙입니다. 


State와 Ref 모두 원래는 함수로 만든 컴포넌트에서는 사용할 수 없는 기능이지만 이 훅 기능 덕분에 사용할 수 있습니다.

 

 

 

 


 


출처 :  이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023).

 



'DEVELOPMENT > react' 카테고리의 다른 글

8. 라이프 사이클과 리액트 개발자 도구  (0) 2024.12.23
7. 카운터 앱만들기  (0) 2024.11.11
5. 리액트 기본 개념 다루기 - 1  (0) 2024.11.03
4. react - 리액드 시작하기  (0) 2024.10.31
3. react - Node.js  (0) 2024.10.31