[LG CNS AM Inspire Camp 1기] React (2) - useState, useRef

정성엽·2025년 1월 5일
1

LG CNS AM Inspire 1기

목록 보기
12/53
post-thumbnail

INTRO

리액트에서 가장 중요한 개념 중 하나인 상태(State)에 대해 알아보려고 한다.

특히 함수형 컴포넌트에서 상태를 관리하기 위해 사용하는 useState와 useRef에 대해 자세히 살펴볼 예정이다 👀


1. 상태(state)란 무엇일까?

리액트에서 상태는 컴포넌트 내부에서 관리하는 데이터를 의미한다.

props는 부모 컴포넌트가 전달해주는 읽기 전용 데이터였다면, state는 컴포넌트가 직접 관리하고 수정할 수 있는 데이터라고 보면 된다.

다만, 모든 데이터가 상태는 아니고 별도의 포맷을 가지고 있다는 것을 기억하자!

💡 상태가 필요한 이유

그렇다면 굳이 상태(state)라는 개념이 왜 필요할까?

사실, 웹 어플리케이션을 만들다보면 사용자의 입력, 서버에서 받아온 데이터, UI 상태 등 다양한 데이터를 다뤄야 한다.

이런 변경될 수 있는 값들을 관리하기 위해 상태라는 개념이 도입되었다.

간단한 코드로 살펴보자

Example

  • 상태 변수 사용 X
function Counter() {
    let count = 0; 

    const increment = () => {
        count += 1;  
        console.log(count);
    };

    return (
        <div>
            <p>횟수: {count}</p>
            <button onClick={increment}>증가</button>
        </div>
    );
}

해당 예시 코드를 살펴보면 count라는 변수는 let으로 선언되었다.

하지만 안타깝게도 리액트는 let으로 선언된 일반적인 지역변수는 렌더링을 감지하지 않는다.

위 사진처럼 증가 버튼을 누르면 내부적으로 count값이 증가하지만, 리액트는 이 변경을 감지하지 못한다.

따라서, 리렌더링이 발생하지 않아 사용자는 증가된 값을 화면으로 확인할 수 없다!


2. useState로 상태 관리하기

위 예시를 통해 짐작했겠지만, 일반적인 변수는 변경되더라도 리렌더링을 발생시키지 않는다.

이때, 리렌더링을 발생시키기 위해 컴포넌트 내부에서 상태변수를 사용한다.

앞으로 자주 사용할 상태 변수 선언 방법이 바로 useState 훅을 사용하는 것이다.

useState는 리액트의 Hook 중 하나로, 함수형 컴포넌트에서 상태를 관리할 수 있게 해준다.

💡 useState 기본 사용법

Example

import { useState } from 'react';

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

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

    return (
        <div>
            <p>횟수: {count}</p>
            <button onClick={increment}>증가</button>
        </div>
    );
}
  • count: 상태 값
  • setCount: 상태를 변경하는 함수
  • useState(0): 초기값을 0으로 설정

여기서 useState는 배열 구조 분해 할당을 통해 두 가지 값을 반환한다.

첫 번째 : 현재 상태 값
두 번째 : 상태를 변경하는 함수

위 예제는 Counter 컴포넌트가 렌더링되는 시점에 useState 훅에 의해서 상태변수 count는 0으로 세팅된다.

이후, 사용자가 증가 버튼을 클릭하면 increment 메서드가 실행되어 위 사진처럼 count값이 1로 변경된다.

이때, 리액트는 상태 변수의 변경을 감지하여 이를 반영한 Counter 컴포넌트를 다시 호출하게 된다.

이 과정이 바로 리렌더링 과정이다.

💡 여러 개의 상태 관리하기

Example

import { useState } from 'react';

function UserForm() {
    const [name, setName] = useState('');
    const [age, setAge] = useState(0);
    const [hobby, setHobby] = useState('');

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log({ name, age, hobby });
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                value={name}
                onChange={(e) => setName(e.target.value)}
                placeholder="이름"
            />
            <input
                type="number"
                value={age}
                onChange={(e) => setAge(e.target.value)}
                placeholder="나이"
            />
            <input
                value={hobby}
                onChange={(e) => setHobby(e.target.value)}
                placeholder="취미"
            />
            <button type="submit">제출</button>
        </form>
    );
}

해당 예제와 같이 여러 개의 상태변수를 하나의 컴포넌트 내부에서 선언하여 사용할 수 있다.

상태변수의 값을 변경할 때는 useState훅으로 리턴받는 상태 변경 함수를 사용해야만 변경된다는 것까지 다시 한번 기억해두자!


3. useRef 사용하기

useRef는 useState와는 조금 다른 특징을 가진 Hook이다.

값이 변경되어도 리렌더링이 발생하지 않고, 컴포넌트가 리렌더링되어도 값이 유지된다는 특징이 있다.

💡 DOM 요소 접근하기

useRef의 가장 일반적인 사용 사례는 DOM 요소에 직접 접근하는 경우다.

필자가 이해한 ref를 간단하게 정리하면 다음과 같다.

HTML 태그와 연결하기 위한 매개체

useRef 훅으로 선언한 변수를 HTML 태그의 ref 속성에 넣어주면, 해당 변수를 통해 연결된 HTML 태그의 고유 속성을 사용할 수 있는 것이다.

간단한 예시를 살펴보자

Example

import { useRef } from "react";

function RefExample() {
  const inputRef = useRef();
  const handleClickBtn = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input placeholder="아무값이나 입력해주세요" ref={inputRef} />
      <button onClick={handleClickBtn}>Input 태그 포커스</button>
    </div>
  );
}

Result

이처럼 inputRef라는 변수를 사용해서 focus()라는 input의 고유 속성을 버튼 클릭으로 제어할 수 있게 되는 것이다.

💡 이전 값 기억하기

useRef는 렌더링에 영향을 주지 않는 값을 기억해야 할 때도 유용하다.

Example

import { useRef, useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  const countRef = useRef(0);

  const handleCountRef = () => {
    countRef.current += 5;
  };

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

  return (
    <div>
      <p>useState Count : {count}</p>
      <p>useRef Count : {countRef.current}</p>
      <button onClick={handleCount}>useState Count 값 + 1</button>
      <button onClick={handleCountRef}>useRef Count 값 + 5</button>
    </div>
  );
}

  • Result

위 예시에는 2가지 버튼이 있다.

useState 훅으로 선언된 변수의 count값을 +1 증가시키는 버튼, 그리고 useRef 훅으로 선언된 변수의 count값을 +5 증가시키는 버튼이다.

Result 부분을 살펴보면 useRef Count 값 + 5 버튼을 클릭해도 렌더링은 발생하지 않는다.

이후, useState Count 값 + 1 버튼을 클릭하면 리렌더링이 발생하면서 이전에 useRef Count 값 + 5 버튼을 클릭한 횟수만큼 값이 적용되어서 렌더링되는 모습을 볼 수 있다.

즉, useRef로 선언된 값은 리렌더링을 발생하더라도 이전의 값을 유지하는 모습을 확인할 수 있다!

(이해가 안된다면 Result에서 동작하는 모습을 잘 살펴보자 👀)


OUTRO

이렇게 리액트의 상태 관리와 관련된 핵심 Hook들을 살펴봤다.

상태 관리는 리액트 애플리케이션을 만들 때 가장 기본이 되는 개념이니, 이 부분은 확실히 이해하고 넘어가는 게 좋다.

화면에 표시되는 데이터는 useState, 내부적으로만 필요한 값은 useRef를 사용하자

다음 포스팅에서는 관련 예제를 살펴보려고 한다 👊

profile
코린이

0개의 댓글

관련 채용 정보