상세 컨텐츠

본문 제목

[리액트 스타터3] 5장. 리액트의 기본 기능 다루기 2

23-24/React.js 3

by 롱롱😋 2023. 11. 10. 10:00

본문

728x90

이벤트 처리하기 

이벤트란 웹 페이지에서 일어나는 사용자의 행위를 말한다. 이벤트의 예로 버튼 클릭, 페이지 스크롤, 새로고침 등이 있다.

 

이벤트 핸들링과 이벤트 핸들러

이벤트 핸들링은 이벤트가 발생했을 때 특정 코드가 동작하도록 만드는 작업이다. 

다음은 버튼을 클릭했을 때 경고 대화상자를 브라우저에 표시하도록 만든 이벤트 핸들링의 예시이다.

// Body.js
function Body() {
    function handleOnclick(){ //함수 handleOnClick 선언
        alert("버튼을 클릭하셨군요!"); // 경고 대화상자 표시하도록 함
    }
    return (
    //버튼 생성하고 클릭시 이벤트를 처리하는 이벤트 핸들러 함수 handleOnClick 호출함
        <div className="body">
            <button onClick={handleOnclick}>클릭하세요</button> 
        </div>
    );
}
export default Body;

 

📌 리액트 HTML의 이벤트 핸들링 차이점

      1. HTML : onclick / 리액트 : onClick로 리액트는 카멜 케이스 문법을 따른다.

      2. 리액트는 onClick = { } 문법으로 이벤트 핸들러를 설정한다.

      3. 이벤트 핸들러 설정 시 리액트는 함수 그 자체를 전달함 (함수 호출의 결괏값 전달하지 않는다)

      4. 리액트는 이벤트 핸들러를 설정할 때 소괄호를 사용하지 않는다. 

// HTML, 자바스크립트 사용시
<button onclick="handleOnClick()">
	Click Me!
</button>

// 리액트 사용시
<button onClick={handleOnClick}>클릭하세요</button>

 

이벤트 객체 사용하기

리액트에서는 이벤트가 발생하면 이벤트 핸들러에게 이벤트 객체를 매개변수로 전달한다. 이벤트 객체에는 이벤트가 

어떤 요소에서 어떻게 발생했는지에 관한 정보가 자세히 담겨있다.

아래는 버튼 2개를 만들고 이벤트가 발생하면 클릭한 버튼의 이름을 콘솔에 출력하는 예시이다.

// Body.js
function Body() {
    function handleOnclick(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;

 

target 프로퍼티에는 이벤트가 발생한 페이지의 요소가 저장되는데 여기서는 버튼이 저장된다. 즉, A버튼을 클릭하면

e.target에 A버튼이 저장되고 버튼의 이름이 콘솔에 출력되는 것이다.

 

컴포넌트와 상태 State 

정적인 리액트 컴포넌트가 아닌 사용자의 행위나 시간 변동에 따라 값이 변하는 동적인 리액트 컴포넌트를 만들기 위해서는 리액트의 핵심 기능 중 하나인 State를 알아야 한다. 지금부터 State를 이용하여 동적인 컴포넌트를 만드는 방법을 알아보도록 한다.

 

State란?

State는 상태라는 뜻으로 일상생활에서도 흔히 사용하는 개념이다. 리액트 컴포넌트는 State 값에 따라 다른 결과를 렌더링 한다. 

 

State 기본 사용법

1. State 생성하기 : useState를 사용하여 State를 생성할 수 있다. useState는 리액트가 제공하는 State를 만드는 함수이다.

useState를 호출하면 2개의 요소가 담긴 배열을 반환한다. 다음은 useState의 문법과 구성 요소에 대해 설명하고 있다.

   const [변수, 함수] = useState('초깃값');
  • 변수 : 현재 상태의 값을 저장하고 있는 변수(State 변수)
  • 함수 : State 변수의 값을 변경, 즉 상태를 업데이트하는 함수(set 함수)
  • useState('초깃값') : 호출 시 인수로 값을 전달하면 이 값이 State의 초깃값이 된다.

다음은 숫자를 카운트할 수 있는 State 변수 count를 생성하는 예이다. 

//body.js
import { useState } from "react"; // useState를 react 라이브러리에서 불러옴

function Body() {
    const [count, setCount] = useState(0); // State 변수 count 생성, 초기값:0 
    return (
        <div className="body">
            <h2>{count}</h2>  
        </div>
    );
}
export default Body;

 

2. set 함수로 State값 변경하기 

변경을 반영하기 위해 컴포넌트를 다시 렌더링 한다. 즉, State값이 변경되면 컴포넌트를 업데이트한다. 

컴포넌트가 페이지에 렌더링하는 값은 컴포넌트 함수의 반환값이다.

import { useState } from "react";

function Body() {
    const [count, setCount] = useState(0);
    const onIncrease = () => {       // 버튼의 이벤트 핸들러 onIncrease에서 set함수 호출함
        setCount(count + 1);
    };
    return (
        <div className="body">
            <h2>{count}</h2>
            <button onClick={onIncrease}>+</button>
        </div>
    );
}
export default Body;

+버튼을 클릭하면 onIncrease 이벤트 핸들러가 실행된다. onIncrease는 setCount를 호출하고, 인수로 현재 count에 1 증가된 값을 전달한다. 그러면 State값은 1이 증가되며 이 변경을 반영하기 위해 컴포넌트를 리렌더링 한다.

 

State로 사용자 입력 관리하기

웹 사이트에서는 다양한 입력 폼이 제공되며, 사용자들은 이 입력폼을 이용해 텍스트, 숫자, 날짜 등의 정보를 입력한다.

입력 폼은 로그인, 회원가입, 게시판, 댓글 등 웹 페이지에서 자주 활용되므로 필수 요소로 취급된다. 리액트에서는 State를 이용하면 다양한 입력 폼에서 제공되는 사용자의 정보를 효과적으로 처리 가능하다.

 

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

<input> 태그는 텍스트, 전화번호, 날짜, 체크박스 등 여러 형식의 정보를 입력할 수 있도록 한다. 

 

이벤트 핸들러로 사용할 함수 handleOnChange를 만들고 이벤트 객체를 매개변수로 저장해 콘솔에 출력한다. 입력 폼을 만들고 이 폼의 onChange 이벤트 핸들러로 handleOnChange를 설정한다. 사용자로부터 입력받은 즉시 콘솔에 출력된다.

import { useState } from "react";

function Body() {
    const handleOnChange = (e) => {   // 이벤트 핸들러 함수 handleOnChange 만들기
        console.log(e.target.value);    // 매개변수로 전달된 이벤트 객체를 콘솔에 출력한다
    };
    return (
    // 입력 폼을 만들고 onChange 이벤트 핸들러로 handleOnChange를 설정하기
        <div className="body">
            <input onChange={handleOnChange} /> 
        </div>
    );
}
export default Body;

 

2. State에 입력 값 저장하기

위 코드는 사용자에게 입력을 받을 수는 있지만 사용자의 입력을 State에 저장하고 있지는 않다. 

import { useState } from "react";

function Body() {
    const [text, setText] = useState("");  // 빈 문자열이 초깃값인 state변수 생성
    const handleOnChange = (e) => {
        setText(e.target.value); // text값을 입력할 때 마다 업데이트
    };
    return (
    //<input> value 속성에 State변수 설정하고 text값 페이지에 렌더링
        <div>
            <input value={text} onChange={handleOnChange} />  
            <div>{text}</div> 
        </div> 
    );
}
export default Body;

 

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

<input> 태그의 type 속성을 date로 설정하면 날짜 형식의 데이터를 입력할 수 있다.

import { useState } from "react";

function Body() {
    const [date, setDate] = useState("");
    const handleOnChange = (e) => {
        console.log("변경된 값: ", e.target.value);
        setDate(e.target.value);
    };
    return (
        <div>
            <input type="date" value={date} onChange={handleOnChange} />
        </div>
    );
}
export default Body;

 

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

<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;

 

5. 여러 줄 텍스트 입력하기

<textarea> 태그를 이용하여 여러 줄의 텍스트를 입력할 수 있다. <textarea>는 <input> 태그의 입력 폼과 동일한 형태로 텍스트를 처리한다. 글자를 입력할 때마다 입력한 텍스트가 콘솔에 표시된다.

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;

 

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

대부분의 페이지에서는 입력 폼을 하나가 아닌 여러 개를 받는다. 따라서 이번에는 여러 개의 사용자 입력을 State로 관리하는 방법을 알아보도록 한다. 아래에서는 이름, 성별, 출생 연도, 자기소개를 한 번에 입력하도록 구성되어 있다.  

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="이름" />
            </div>
            <div>
                <select value={gender} onChange={onChangeGender}>
                    <option key={""}></option>
                    <option key={"남성"}>남성</option>
                    <option key={"여성"}>여성</option>
                </select>
            </div>
            <div>
                <input type="date" value={birth} onChange={onChangeBirth} placeholder="이름" />
            </div>
            <div>
                <textarea value={bio} onChange={onChangeBio} />
            </div>
        </div>
    );
}
export default Body;

 

📌 관리할 State의 개수가 많아지면 코드 길이가 너무 길어지는 단점이 있다.

이러한 문제는 객체 자료형을 이용하면 해결할 수 있다. 객체 자료형을 이용하면 여러 내용을 입력하더라도 1개의 State에서 관리가 가능하기 때문이다.

// function Body()에서

const [state, setState] = useState({  //State 변수를 객체 자료형을 사용하여 생성
        name : "",
        gender : "",
        birth : "",
        bio : "",
    });

const handleOnChange = (e) => {   //이벤트 핸들러는 입벤트 객체를 매개변수로 저장하고 set함수 호출함
        console.log("현재 수정 대상:", e.target.name);
        console.log("수정값:", e.target.value);
        setState({
            ...state,  // 스프레드 연산자를 이용해 기존 객체 State의 값을 나열
            [e.target.name]: e.target.value // 괄호 표기법을 사용하여 name속성을 key로, 폼에 입력된 값을 value로 저장함 
        });
    };

return (
//모든 입력 폼에 name속성 지정, value를 객체의 프로퍼티 중 하나로 설정
        <div>
            <div>
                <input name="name" value={state.name} onChange={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} placeholder="이름" />
            </div>
            <div>
                <textarea name="bio" value={state.bio} onChange={handleOnChange} />
            </div>
        </div>
    );

 

Props와 State

동적으로 변하는 값인 리액트의 State 역시 일종의 값이므로 Props로 전달할 수 있다.

 

📌부모에 속해있는 State 값이 변하면 자식 컴포넌트도 페이지에 리렌더링 된다.

     아래 코드에서 볼 수 있듯이, number값이 변하면 Viewer 컴포넌트에서 구현한 짝수, 홀수 값도 바뀐다.

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

function Viewer({number}) {  //Viewer 컴포넌트 선언, Props로 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} />  // 자식 컴포넌트로 배치, Props로 number 전달

            <div>
                <button onClick={onDecrease}>-</button>
                <button onClick={onIncrease}>+</button>
            </div>
        </div>
    );
}
export default Body;

 

State와 자식 컴포넌트

위의 코드에서는 부모가 자식 컴포넌트에게 state를 Props로 전달하지만 그렇지 않은 경우는 어떻게 될까?

그래도 자식은 리렌더링 된다.즉, 부모 컴포넌트가 리렌더 되면 자식도 리렌더 된다. 

하지만 의미 없는 리렌더가 자주 발생하면 웹 브라우저 성능이 떨어지니 주의해야 한다.

 

Ref 

Ref는 참조라는 뜻으로 Ref를 이용하면 돔 요소들을 직접 조작할 수 있다. 

돔(DOM)은 문서 객체 모델을 줄여서 부르는 명칭으로 HTML 코드를 트리 형태로 변환한 구성물이다. 돔은 웹 브라우저가 직접 생성하며 HTML코드를 렌더링 하기 위해 만든다.

 

useRef 사용하기

리액트에서는 useRef 라는 리액트 함수를 이용하여 Ref 객체를 생성한다.

import { useRef, useState } from "react"; // useRef 추가

function Body() {
    const [text, setText] = useState("");
    const textRef = useRef(); //Ref 객체 생성 후 상수 textRef에 저장함

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

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

    return (
    //<input>태그에 textRef가 돔 입력폼에 접근하도록 설정함. 이제 textRef를 이용해 입력 폼을 직접 조작 할 수 있음
        <div>
            <input ref={textRef} value={text} onChange={handleOnChange} /> 
            <button onClick={handleOnClick}>작성 완료</button>
        </div>
    );
}
export default Body;

 

useRef로 입력 폼 초기화하기

웹 서비스의 로그인 페이지는 대부분 사용자가 입력한 후 로그인 버튼을 클릭하면 패스워드가 올바른지 점검한다.

그런 다음 폼에 작성한 값을 초기화한다. 리액트에서 Ref를 이용하면 이 동작을 수행할 수 있다.

const handleOnClick = () => {
        alert(text);
        textRef.current.value = ""; // 대화상자 확인 누르면 초기화됨
    };

 

useRef로 포커스 하기

폼을 입력하지 않거나 텍스트 입력이 정해진 길이보다 짧을 때 추가 입력을 유도하도록 만드는 작업이다. 이때 입력 폼을 초기화하지 않고 사용자의 입력을 기다린다.

다음 코드는 5 미만의 길이로 입력하면 포커스 한 상태로 입력을 추가할 때까지 기다린다.

const handleOnClick = () => {
        if (text.length < 5){
            textRef.current.focus();
        } else {
            alert(text);
            setText("");
        } 
    };

 

 


Quiz 

1. 이벤트가 발생했을 때 특정 코드가 동작하도록 만드는 작업을 (   이벤트 핸들링    )이라고 한다.

2. State 변수는 (   useState    )를 사용하여 생성한다.

3. set함수로 State 값을 변경하면 이 변경을 반영하기 위해 컴포넌트를 리렌더링 한다.

    리액트는 이것을 (  컴포넌트의 업데이트  )라고 한다.

4. <input> 태그로 날짜를 입력하면 <input>의 (   type   )  속성을 (   date   )로 설정한다.

5. 드롭다운 상자로 여러 옵션 중 하나를 선택하려면 (  <select>   ) 태그와 (   <option>   )를 함께 사용한다.

6. 사용자로부터 여러 개의 입력을 받으려고 할 때 (    객체 자료형     )을 이용하면 1개의 State에서 관리 가능하다.

7. Ref 객체는 (    useRef    )를 사용하여 생성한다.

 

<프로그래밍 문제>

1.  State 변수 text를 생성하고, 여러 줄의 텍스트를 입력할 수 있도록 코드를 완성하여라. 

     (초기값은 빈 문자열로 설정, 이벤트 핸들러를 작성하는데 text값을 입력할 때마다 업데이트되도록 set함수를 호출)

import { useState } from "react";

function Body() {
    //state 변수 생성
    //이벤트 핸들러 선언 + set함수 호출하기
    return (
        <div>
             //여러줄 입력폼 생성    
        </div>
    );
}
export default Body;

 

2.  10 미만의 길이로 입력하면 포커스 하도록 코드를 완성하여라

import { useRef, useState } from "react"; 

function Body() {
    const [text, setText] = useState("");
    const textRef = useRef(); //Ref 객체 생성

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

    const handleOnClick = () => {
        //이 부분에 코드를 작성하시오
        //...
        else {
        	alert(text);
            setText("");
        }
    }

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

 

 

 

 

 


Answers 

1.

import { useState } from "react";

function Body() {
    const [text, setText] = useState("");
    const handleOnChange = (e) => {
        setText(e.target.value);
    };
    return (
        <div>
            <textarea value={text} onChange={handleOnChange} />
        </div>
    );
}
export default Body;

 

2. 

const handleOnClick = () => {
        if (text.length < 10){
            textRef.current.focus();
        }
        else {
        	alert(text);
            setText("");
        }
    }

 

 

 

 

 

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

Corner React.js 3 

Editor:  via

 

728x90

관련글 더보기