[React] 성능 최적화 4 - 전체 최적화

이재훈·2023년 6월 10일
0

React

목록 보기
18/27

목표

프로젝트 전체적인 렌더링 최적화


현재 일기 하나를 삭제하게 되면 전체 일기가 리렌더링이 일어나고 있습니다. 일기가 만약 2천개였다면, 이미지, 비디오를 렌더링했다면 메모리 낭비가 굉장히 심했을 것입니다.

DiaryItem.js

import { useRef, useState } from "react";

const DiaryItem = ({
  onEdit,
  onRemove,
  name,
  content,
  created_date,
  hungry,
  id,
}) => {
  const [isEdit, setIsEdit] = useState(false);
  const toggleIsEdit = () => setIsEdit(!isEdit);
  const [localContent, setLocalContent] = useState(content);
  const localContentInput = useRef();

  const handleRemove = () => {
    if (window.confirm(`${id}번째 일기를 정말 삭제하시겠습니까?`)) {
      onRemove(id);
    }
  };

  const handleQuitEdit = () => {
    setIsEdit(false);
    setLocalContent(content);
  };

  const handleEdit = () => {
    if (localContent.localContent < 5) {
      localContentInput.current.focus();
      return;
    }

    if (window.confirm(`${id}번째 일기를 수정하시겠습니까?`)) {
      onEdit(id, localContent);
      toggleIsEdit();
    }
  };

  return (
    <div className="DiaryItem">
      <div className="info">
        <span>
          이름 : {name} | 배고픔 정도 : {hungry}
        </span>
        <br />
        <span className="date">{new Date(created_date).toLocaleString()}</span>
      </div>
      <div className="content">
        {isEdit ? (
          <>
            <textarea
              ref={localContentInput}
              value={localContent}
              onChange={(e) => setLocalContent(e.target.value)}
            />
          </>
        ) : (
          <>{content}</>
        )}
      </div>
      {isEdit ? (
        <>
          <button onClick={handleQuitEdit}>수정 취소</button>
          <button onClick={handleEdit}>수정 완료</button>
        </>
      ) : (
        <>
          <button onClick={handleRemove}>삭제</button>
          <button onClick={toggleIsEdit}>수정</button>
        </>
      )}
    </div>
  );
};

export default DiaryItem;

DiaryItem.js가 props로 받고 있는 것은 총 7개가 있습니다.

  • onEdit : App.js에서 받은 함수
  • onRemove : App.js에서 받은 함수
  • name : data
  • content : data (변화할 수 있는)
  • created_date : data
  • hungry : data
  • id : data

onEdit, onRemove, content에 집중하여 최적화를 해보도록 하겠습니다. 최적화의 첫번째는 React.memo로 컴포넌트를 묶어주는 것 입니다.

import React, { useRef, useState } from "react";
// ... 생략
export default React.memo(DiaryItem);

그 다음 useEffact를 사용하여 어떤 item들이 리렌더링이 일어나는지 확인해보도록 하겠습니다.

const DiaryItem = ({
  onEdit,
  onRemove,
  name,
  content,
  created_date,
  hungry,
  id,
}) => {
  useEffect(() => {
    console.log(`${id}번째 아이템 렌더`);
  });


페이지 새로고침, 게시글 하나 삭제를 했을 때 콘솔 로그입니다. 모든 아이템이 리렌더링 되는 것을 확인할 수 있습니다.

React.memo로 감쌌는데 왜 모든 컴포넌트들이 리렌더링이 일어날까요?

onCreate 함수처럼 data state가 변경되면 리렌더링이 일어날 수 밖에 없습니다. App.js의 onEdit과 onRemove를 최적화 해보도록 하겠습니다.

App.js

  const onRemove = useCallback((targetId) => {
    setData((data) => data.filter((it) => it.id !== targetId));
  }, []);

  const onEdit = useCallback((targetId, newContent) => {
    setData((data) =>
      data.map((it) =>
        it.id === targetId ? { ...it, content: newContent } : it
      )
    );
  }, []);

useCallback을 사용합니다. dependencyArray는 빈 배열을 전달하고, setData에는 함수형으로 전달해줍니다.

이제 일기를 삭제해도 리렌더링이 일어나지 않는 것을 확인할 수 있습니다.

프로젝트의 모든 부분의 렌더링 최적화를 완료하였습니다.


리액트 공식 홈페이지
https://ko.legacy.reactjs.org/docs/react-api.html#reactmemo
해당 게시글은 인프런 강의
"한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지(이정환)"
를 정리한 내용입니다. 쉽게 잘 설명해주시니 여러분도 강의를 듣는 것을 추천드립니다.

profile
부족함을 인정하고 노력하자

0개의 댓글