UseState와 UseRef를 이용한 렌더링 개선

성찬홍·2023년 6월 1일

Code Refactoring

목록 보기
1/5

렌더링: 코드로 정의된 내용이 실제 브라우저 화면에 그려지는 과정

React 렌더링 시점

→ 대표적으로 아래 다섯 가지 정도의 렌더링 상황이 있다.

  1. state의 변경(useState, dispatch)
  2. 부모 컴포넌트의 리렌더링
  3. root.render(…) 함수 호출(React 18) / React 18 이전은 ReactDOM.render(…)
  4. props의 변경
  5. 중앙 상태 값(Redux, MobX, Recoil, Context API 등)의 변화

이번에는 useRef와 useState를 다룰 예정이므로 state 변경에 따른 렌더링을 다룰 것입니다.

useRef vs useState

useRef → 값이 변화할 때마다 리렌더링이 이루어지지 않는다.
useState → 상태가 변화할 때마다 리렌더링이 이루어진다.

useState

  • useState는 함수형 컴포넌트의 상태를 관리하고 변경할 수 있도록 도와주는 React Hook이다.
  • 아래와 같은 형태로 사용하며, setValue를 통해 값을 변경할 수 있고 setValue를 통해 값이 변경될 때마다 리렌더링된다.
const [value, setValue] = useState(초기값);

useRef

  • 저장 공간 또는 DOM 요소에 접근하기 위해 사용되는 React Hook이다.

  • JavaScript에서는 querySelector 등의 함수를 사용하는데, React에서 DOM을 선택해야 하는 상황에 querySelector처럼 사용하기 위한 React Hook이다.

    • tip) querySelector는 실제 DOM을 가져오게 되는데, React에서는 Virtual DOM을 사용하기 때문에 React가 제어하고 있는 요소를 참조하는 방식이 더 신뢰할 만하다.
  • useState와는 다르게 useRef로 관리되는 값은 값이 변해도 화면이 리렌더링되지 않는다.

코드 설명(댓글 추가 기능) 문제점 및 개선 전·후 코드 설명

문제점: 백엔드로 보낼 input의 value 값을 onChange를 이용해 useState에 저장하였다.
이렇게 되면 텍스트가 입력될 때마다 리렌더링이 발생해 불필요한 리렌더링이 지속적으로 발생하므로 성능에 좋지 못했다.
그래서 아래와 같이 useStateuseRef로 변경하여 개선해 보았다.

개선 전 코드

(1) 댓글 input 박스에 텍스트를 입력한다.
(2) 입력할 때마다 useState에 텍스트가 저장된다.
(3) 버튼 클릭 시 useState에 저장되어 있는 텍스트를 API 호출을 통해 백엔드로 보낸다.
(4) 텍스트 값이 MySQL에 INSERT된다.
(5) MySQL에 저장된 댓글을 불러와 클라이언트에 보여 준다.

개선 후 코드

(1) 댓글 input 박스에 텍스트를 입력한다.
(2) 댓글 입력 완료 버튼 클릭 시 useRef를 이용해 텍스트 값을 추출한다.
(3) 추출된 값을 백엔드로 보낸다.
(4) 텍스트 값이 MySQL에 INSERT되고, 댓글을 불러와 클라이언트에 보여 준다.

& 수정 전 코드
// TS 코드
// insertContext = API 통신에 사용될 데이터
const [insertContext, setInsertContext] = useState<string>("");

const handleContext = (e: React.ChangeEvent<HTMLInputElement>) => {
  setInsertContext(e.target.value);
};

// text의 값을 백엔드로 보낸다.
const handleSubmit = (event: React.MouseEvent<HTMLButtonElement>) => {
  event.preventDefault();

  const axiosData = {
    CO_CONTEXT: insertContext,
    S_IDX,
  };

  const result = axiosInstance.post("/clubDetail/insertContext", axiosData);

  setInsertContext("");
};

// JSX 코드
<input
  type="text"
  placeholder="댓글을 입력해 주세요"
  className="w-4/5 ml-1 border-y-neutral-800"
  onChange={handleContext}
  value={insertContext}
/>

현재 필요한 데이터는 버튼을 클릭했을 때의 input value 값이다.
하지만 위 코드에서는 텍스트가 변화할 때마다 useState의 값이 변화하여 계속해서 불필요한 리렌더링이 발생하고 성능 저하의 요인이 된다.

& 수정 후 코드
// TS
const insertText = useRef<HTMLInputElement>(null);

const handleSubmit = (event: React.MouseEvent<HTMLButtonElement>) => {
  const data = insertText.current?.value;

  event.preventDefault();

  const axiosData = {
    CO_CONTEXT: data,
    S_IDX,
  };

  const result = axiosInstance.post("/clubDetail/insertContext", axiosData);

  if (insertText.current) {
    insertText.current.value = "";
  }
};

// JSX
<input
  type="text"
  placeholder="댓글을 입력해 주세요"
  className="w-4/5 ml-1 border-y-neutral-800"
  ref={insertText}
/>

useRef를 이용하여 input value 값을 참조할 수 있도록 변경하였다.
이렇게 하여 값의 변화에 따른 불필요한 리렌더링이 일어나지 않게 되어 성능 측면에서 더 좋아졌다.

정리

구글이나 네이버의 검색 기능을 사용할 때는 텍스트 입력창의 value에 따라 지속적으로 결과를 보여 줘야 하므로 리렌더링이 필요하다. 따라서 useState를 이용한 방법이 더 적절할 수 있다.

그러나 위와 같은 경우에는 입력값의 지속적인 변화에 따라 화면을 갱신할 필요가 없고, 버튼을 눌렀을 때 백엔드로 보낼 값만 필요하기 때문에 useRef를 이용한 방법이 좀 더 효율적이다.

참고 사이트

https://yceffort.kr/2022/04/deep-dive-in-react-rendering

https://velog.io/@jinyoung985/React-useRef%EB%9E%80

profile
꾸준한 개발자

0개의 댓글