상세 컨텐츠

본문 제목

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

23-24/React.js 2

by YUZ 유즈 2023. 11. 10. 17:39

본문

728x90

1.  이벤트 처리하기

이벤트 : 웹 페이지에서 일어나는 사용자의 행위이다.

ex) 버튼 클릭, 페이지 스크롤, 새로고침 등

 

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

이벤트 핸들링 : 이벤트가 발생하면 특정 코드가 동작하도록 만드는 작업이다.

ex) 버튼을 클릭했을 때 경고 대화상자를 브라우저에 표시

 

<리액트의 이벤트 핸들링>

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

  return (
    <div className="body">
      <button onClick={handleOnClick}>클릭하세요</button> 
    </div>
  );
}
export default Body;
  • 이벤트 핸들러 표기에서 HTML은 onclick이지만 리액트는 카멜 케이스 문법에 따라 onClick으로 표기한다.
  • Props로 전달할 값을 지정할 때처럼 onClick={} 문법으로 이벤트 핸들러를 설정한다.
  • 이벤트 핸들러를 설정할 때는 함수 호출의 결괏값을 전달하는 것이 아니라 콜백 함수처럼 함수 그 자체를 전달한다.

 

<이벤트 객체 사용하기>

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

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 프로퍼티에는 이벤트가 발생한 페이지의 요소(여기서는 버튼)가 저장된다.
  • 함수 handleOnClick에서 e.target.name을 콘솔에 출력하면 현재 이벤트가 발생한 요소의 name 속성값을 출력한다.

 

function handleOnClick(e) {
    console.log(e); 
    console.log(e.target.name);
  }
  • 이벤트 객체에 정확히 어떤 값들이 저장되어 있는지 알아볼 수 있다.

 

2.  컴포넌트와 상태

 

<State 이해하기>

State는 상태라는 뜻이다.

리액트 컴포넌트 또한 State 값에 따라 다른 결과를 렌더링 한다.

 

<State의 기본 사용법>

  • useState로 State 생성하기
useState의 용법
const [light, setLight] = useState('off');
      State 변수 set 함수 생성자(초깃값)
  • 배열의 첫 번째 요소 ‘light’는 현재 상태의 값을 저장하고 있는 변수 State 변수이다.
  • setLight는 State 변수의 값을 변경하는 즉 상태를 업데이트하는 함수 set 함수이다.
  • 인수로 값을 전달하면 이 값이 State의 초깃값 위 코드에서는 ‘off’를 전달한다.

 

    • set 함수로 State 값 변경하기
import { useState } from "react";

function Body() {
  const [count, setCount] = useState(0); 

  const onIncrease = () => { 
    setCount(count + 1);
  };

  return (
    <div>
      <h2>{count}</h2>
      <button onClick={onIncrease}>+</button>
    </div>
  );
}
export default Body;
  • <+> 버튼을 클릭하면 onIncrease 이벤트 핸들러가 실행된다. 함수 onIncrease는 setCount를 호출하고, 인수로 현재의 count 값에 1 더한 값을 전달한다. 그 결과 State(count) 값은 1 증가한다.

 

  • 컴포넌트의 업데이트 : set 함수를 호출해 State 값을 변경하면, 변경값을 페이지에 반영하기 위해 컴포넌트를 다시 렌더링 한다.
  • 컴포넌트를 다시 렌더링 한다고 함은 컴포넌트 함수를 다시 호출한다는 의미 한다.
  • State 값이 변해 컴포넌트를 다시 렌더링 하는 것을 ‘리렌더’ 또는 ‘리렌더링’이라 한다. 
  • 리액트 컴포넌트는 자신이 관리하는 State 값이 변하면 자동으로 리렌더 한다.

 

<State로 사용자 입력 관리하기>

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

텍스트, 전화번호, 날짜, 체크박스 등

import { useState } from "react";

function Body() {
  const handleOnChange = (e) => { 
    console.log(e.target.value);
  };
  return (
    <div>
      <input onChange={handleOnChange} /> 
    </div>
  );
}
export default Body;
  • type 속성으로 다음 값들이 올 수 있다.
  • text : 텍스트 폼
  • date : 날짜 형식
  • tel : 전화번호 형식
  • 아무것도 지정 x : 텍스트 폼(기본)

-> 현재 텍스트가 리액트 컴포넌트가 관리하는 State에 저장되어 있지는 않다.

 

 

 

  • 다음은 State를 하나 만들고 사용자가 폼에서 입력할 때마다 텍스트를 State 값으로 저장하는 코드이다.
import { useState } from "react";

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

export default Body;
  • State 값이 변경되면 컴포넌트는 자동으로 리렌더 한다.

 

  • <input> 태그로 날짜 입력하기
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;
  • 날짜는 yyyy-mm-dd 형식이다.
  • 입력 폼에서 날짜를 변경하면 콘솔에서도 변경된 날짜가 바로 출력된다.

 

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

<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;
  • 사용자가 3번 옵션을 선택하면 해당 옵션의 key 속성인 3번이 e.target.value에 저장된다.

 

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

여러 줄의 텍스트를 입력할 때 <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;
  • <input> 태그의 입력 폼과 동일한 형태로 텍스트를 처리한다.

 

  • 여러 개의 사용자 입력 관리하기
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} /> ③
      </div>
      <div>
        <textarea value={bio} onChange={onChangeBio} /> ④
      </div>
    </div>
  );
}
export default Body;

 

-> 객체 자료형을 이용해 간결하게 코드를 작성할 수 있다.

 

  • set 함수로 State 값 변경하기
 import { useState } from "react";

function Body() {
  const [state, setState] = useState({ ①
    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" ②
          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}
        />
      </div>
      <div>
        <textarea name="bio" value={state.bio} onChange={handleOnChange} />
      </div>
    </div>
  );
}

export default Body;

객체 자료형으로 State를 하나 생성하고 초깃값을 설정한다. State 변수인 객체 state의 초깃 값은 모두 공백 문자열("")이며, name, gender, birth, bio 프로퍼티가 있다.

모든 입력 폼에서 name 속성을 지정한다.

모든 입력 폼의 value를 객체 state의 프로퍼티 중 하나로 설정한다.

사용자의 입력을 처리할 이벤트 핸들러 handleOnChange를 생성한다.

 

  • 함수 setState에서는 새로운 객체를 생성해 전달한다. 이때 스프레드 연산자를 이용해 기존 객체 state의 값을 나열한다. 그리고 객체의 괄호 표기법을 사용하여 입력 폼의 name 속성(e.target.name)을 key로, 입력 폼에 입력한 값(e.target.value)을 value로 저장한다.
  • e.target.name은 현재 이벤트가 발생한 요소의 name 속성이다. 예를 들어 성별을 입력하는 <select> 태그에서 onChange 이벤트가 발생했다면, e.target.name은 gender가 된다.

 

<Props와 State>

State를 Props로 전달할 수 있다.

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

function Viewer({ 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 컴포넌트를 선언한다. 이 컴포넌트에는 Props로 Body 컴포넌트에 있는 State 변수 number가 전달된다.
  • Body에서 Viewer를 자식 컴포넌트로 사용한다.

부모에 속해 있는 State(number) 값이 변하면 Viewer 컴포넌트에서 구현한 ‘짝수’, ‘홀수’ 값도 따라서 변한다.

->자식 컴포넌트는 Props로 전달된 State 값이 변하면 자신도 리렌더 된다는 것을 알 수 있다.

 

<State와 자식 컴포넌트>

다음은 부모 컴포넌트가 자식에게 State를 Props로 전달하지 않는 경우이다.

import { useState } from "react";

function Viewer() { 
  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 />
      <div>
        <button onClick={onDecrease}>-</button>
        <button onClick={onIncrease}>+</button>
      </div>
    </div>
  );
}

export default Body;
  • Viewer 컴포넌트가 Props를 받지 않는다.
  • Body도 더 이상 Viewer 컴포넌트에 Props로 State를 전달하지 않는다.

 

콘솔에서 6번의 viewer component update! 가 출력된다.

->  리액트에서는 부모 컴포넌트가 리렌더 하면 자식도 함께 리렌더 된다는 것을 알 수 있다.


 

3.  Ref

 

돔(DOM) 요소들을 직접 조작할 수 있다.

Reference의 줄임말로 참조를 뜻한다.

 

<useRef 사용하기>

useRef라는 리액트 함수를 이용해 Ref 객체를 생성한다.

import { useRef, useState } from "react";

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

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

  return (
    <div>
      <input ref={textRef} value={text} onChange={handleOnChange} />
      <button onClick={handleOnClick}>작성 완료</button>
    </div>
  );
}
export default Body;
  • useRef는 리액트가 제공하는 기능이므로 react 라이브러리에서 불러온다.
  • 함수 useRef는 인수로 전달한 값을 초깃값으로 하는 Ref 객체를 생성한다. 생성한 Ref를 상수 textRef에 저장한다.
  • <input> 태그에서 ref={textRef} 명령으로 textRef가 돔 입력 폼에 접근하도록 설정한다.

-> 입력 폼에 대한 어떤 조작도 아직 시도하지 않았기 때문에 아직 아무런 변화가 없다.

 

  • useRef로 입력 폼 초기화하기
import { useRef, useState } from "react";

function Body() {
  (...)
  const handleOnClick = () => {
    alert(text);
    textRef.current.value = "";
  };
  (...)
}
export default Body;

 

  • 텍스트 입력 후 작성완료 버튼 클릭하고 대화상자가 나오면 확인 버튼을 클릭한다.
  • 이후 텍스트 입력 폼에서 입력한 문자열이 사라지고 빈 공백만 남는다.

 

  • useRef로 포커스 하기

사용자가 특정 폼에 내용을 입력하지 않거나 내용이 정한 길이보다 짧으면 해당 폼을 포커스(focus)하여 사용자의 추가 입력을 유도한다.

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(); ①
    } else {
      alert(text);
      setText(""); ②
    }
  };

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

<input> 태그로 지정한 폼에 입력한 텍스트가 다섯 글자보다 적다면 textRef.current가 참조하는 입력 폼에 포커스를 실행한다. focus()는 현재 돔 요소에 포커스를 지정하는 메서드이다.

텍스트 폼에 입력한 값을 초기화하기 위해 set 함수 setText를 호출하고 인수로 빈 문자열을 전달한다. Ref를 사용하지 않고도 set 함수로 입력 폼을 초기화할 수 있다.

 

<리액트  훅>

리액트 컴포넌트에서 클래스로 만든 리액트 컴포넌트의 기능을 이용하도록 도와주는 함수들이다.

이름이 use로 시작한다.

State를 만드는 함수 useState와 참조 객체를 만드는 함수 useRef가 훅에 해당한다.

 

 

 



Quiz

이론문제

 

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

2. 이벤트 핸들러를 설정할 때는 함수 호출의 결괏값을 전달하는 것이 아니라 (    )를 전달한다.

3. State 값이 변해 컴포넌트를 다시 렌더링 하는 것을 (    ) 또는 (    )이라 한다. 

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

5. 글상자로 여러 줄의 텍스트 입력하려면 (    ) 태그를 사용한다.

6. (    )라는 리액트 함수를 이용해 Ref 객체를 생성한다.

7. 리액트 훅은 이름이 (    )로 시작한다.

 

실습문제

 

1. <textarea> 태그를 사용하여 글상자로 여러 줄의 텍스트 입력하는 코드를 짜보아라.

2. <input> 태그로 지정한 폼에 입력한 텍스트가 열 글자보다 적다면 포커스를 실행하는 코드를 작성해라.

 

 

 

 


 

이론문제 답 : 이벤트 핸들링 / 함수 그 자체 / 리렌더, 리렌더링 / select, option / textarea / useRef / use

 

실습문제 1번 답

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;

 

실습문제 2번 답 

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 < 10) {
      textRef.current.focus(); 
    } else {
      alert(text);
      setText(""); 
    }
  };

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

 


 

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

Editor: yunseul

 

728x90

관련글 더보기