Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#26] 워크스페이스 페이지의 불필요한 리렌더링 개선 #35

Merged
merged 14 commits into from
Jan 24, 2025

Conversation

lee0jae330
Copy link
Member

@lee0jae330 lee0jae330 commented Jan 23, 2025

🔗 #26

🙋‍ Summary (요약)

  • CSS 툴팁 렌더링 시 발생하는 불필요한 리렌더링 해결
  • CSS 속성 편집 시 발생하는 불필요한 리렌더링 해결

😎 Description (변경사항)

CSS 툴팁 렌더링 시 불필요한 리렌더링 발생 해결

  • CSS 툴팁이 렌더링 될 경우 아래와 같이 모든 CSS 속성 선택 컴포넌트가 리렌더링되는 문제가 있었습니다.

BooLock--Chrome2025-01-2322-26-36-ezgif com-video-to-gif-converter

  • 해당 문제는 CSS 툴팁의 렌더링 위치를 결정하는 X, Y 좌표를 전역 상태로 관리하여 발생하는 문제였습니다.
문제의 코드
export const CssOptionItem = ({ cssItem, index }: CssOptionItemProps) => {
  const { totalCssPropertyObj, currentCssClassName } = useCssPropsStore();
  const { handleCssPropertyCheckboxChange, handleCssOptionChange, handleColorChange } =
    useCssOptions();

  const {
    cssOptionValue,
    isHover,
    indexOfHover,
    isChecked,
    cssOption,
    handleMouseEnter,
    handleEnterKey,
    handleMouseLeave,
    handleChangeInputValue,
  } = useCssOptionItem(cssItem);

  const { leftX, topY } = useCssTooltip(); // 전역 상태 사용
  • 모든 CssOptionItem 컴포넌트들이 전역 상태인 leftX, topY를 구독해서 발생하는 문제임을 확인했습니다.
  • 해결은 다음과 같이 했습니다.
  • tooltip의 좌표를 전역 상태가 아닌 local 상태로 관리하도록 변경했습니다.
  • 또한 기존 tooltip의 높이를 40이라는 매직넘버로 관리하던 방식에서 useLayoutEffect를 통해 화면 렌더링 전에 툴팁의 높이를 계산하는 방식으로 변경하여 40이라는 매직넘버를 없앨 수 있었습니다.
변경된 useCssTooltip
import { useLayoutEffect, useRef, useState } from 'react';

import { useWindowSize } from '@/shared/hooks';

export const useCssTooltip = (leftX: number, topY: number) => {
  const tooltipRef = useRef<HTMLDivElement | null>(null);
  const [tooltipHeight, setTooltipHeight] = useState<number>(0);
  const { screenHeight } = useWindowSize();

  useLayoutEffect(() => {
    if (tooltipRef.current) {
      setTooltipHeight(tooltipRef.current.getBoundingClientRect().height);
    }
    return () => setTooltipHeight(0);
  }, [tooltipRef.current]);

  let tooltipX = leftX;
  let tooltipY = 0;

  if (topY + tooltipHeight > screenHeight) {
    tooltipY = -topY + tooltipHeight;
  } else {
    tooltipY = topY;
  }

  return { tooltipX, tooltipY, tooltipRef };
};
변경된 CssTooltip 코드
import { createPortal } from 'react-dom';
import { useCssTooltip } from '@/shared/hooks';

type CssTooltipProps = {
  description: string;
  leftX: number;
  topY: number;
};

export const CssTooltip = ({ description, leftX, topY }: CssTooltipProps) => {
  const { tooltipX, tooltipY, tooltipRef } = useCssTooltip(leftX, topY);
  return createPortal(
    <div
      className={`text-gray-white text-tooltip-sm fixed z-[9999] rounded-3xl ${tooltipY >= 0 ? 'rounded-tl-none' : 'rounded-bl-none'} bg-green-500 px-3 py-2`}
      style={{
        left: `${tooltipX + 18}px`,
        top: tooltipY >= 0 ? `${tooltipY + 8}px` : `${-tooltipY}px`,
      }}
      ref={tooltipRef}
    >
      <p>{description}</p>
    </div>,
    document.body
  );
};
- 개선 결과

BooLock--Chrome2025-01-2323-08-10-ezgif com-video-to-gif-converter (1)

작업 내용 요약2

  • CSS 클래스 속성 편집, CSS클래스명 선택 시 워크스페이스 헤더, Workspace Workcontent 등의 컴포넌트가 리렌더링되는 문제가 발생했습니다.

BooLock--Chrome2025-01-2323-11-42-ezgif com-video-to-gif-converter

  • 문제의 원인은 CssPropsStore라는 전역 상태를 사용하는 모든 컴포넌트에서 잘못된 방식으로 상태를 구독하고 있어서 발생하는 문제였습니다.
const { totalCssPropertyObj } = useCssPropsStore();
  • 위 코드처럼 구조분해할당으로 상태 변수를 구독할 경우, useCssPropsStore((state) => state)의 결과 중에서 totalCssPropertyObj만 구독하는 것이 아닌, 내부적으로 CssPropsStore의 모든 상태를 구독해서 발생하는 문제 였습니다.
    • CssPropsStore에서 totalCssPropertyObj만 구독해야 하는 컴포넌트가 currentClassName처럼 필요하지 않는 상태들을 구독했고 이는 곧 의도치 않은 리렌더링이라는 결과를 낳았습니다.
  • 그래서 구조분해할당으로 상태를 구독하는 것이 아닌 아래와 같이 state에서 직접 원하는 상태만 반환하였습니다.
  const totalCssPropertyObj = useCssPropsStore((state) => state.totalCssPropertyObj);
  const currentCssClassName = useCssPropsStore((state) => state.currentCssClassName);
  • useCssPropsStore를 사용하는 모든 컴포넌트를 위와 같은 코드로 변경하니 불필요한 리렌더링을 방지할 수 있었습니다.

BooLock--Chrome2025-01-2323-22-03-ezgif com-video-to-gif-converter

🔥 Trouble Shooting (해결된 문제 및 해결 과정)

🤔 Open Problem (미해결된 문제 혹은 고민사항)

  • 추후에 zustand 사용법 관련해서 깊게 학습하고 문서화하겠습니다.

@lee0jae330 lee0jae330 self-assigned this Jan 23, 2025
@lee0jae330 lee0jae330 requested a review from a team as a code owner January 23, 2025 07:58
@lee0jae330 lee0jae330 requested review from Ujaa and chichoc and removed request for a team January 23, 2025 07:58
@lee0jae330 lee0jae330 changed the title [#26] 리렌더링 개선 [#26] 워크스페이스 페이지의 불필요한 리렌더링 개선 Jan 23, 2025
@inhachoi
Copy link
Contributor

inhachoi commented Jan 23, 2025

제가 승인자는 아니지만 글이 좋아서 읽다가 코멘트 남깁니다 ㅎㅎ
매직넘버 하다가 문득 든 생각인데,

let tooltipY = 0;

0도 매직넘버로 생각하고 상수 처리하는 대해서 어떻게 생각하시나요?
뭔가 지금까지는 0이 아닌 숫자들을 매직넘버로 자연스럽게 생각했던 것 같아서요.

고생 많으셨습니다 대 영 재 님 ❤️

@lee0jae330 lee0jae330 added the refactor 리팩토링 label Jan 23, 2025
@lee0jae330 lee0jae330 linked an issue Jan 23, 2025 that may be closed by this pull request
5 tasks
Copy link
Collaborator

@chichoc chichoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리렌더링 개선하느라 고생하셨습니다~ 타입과 훅 분리 등 코드 구조도 개선하신 점 리스팩입니다 역시 대영재<3

Comment on lines +9 to +11
const currentCssClassName = useCssPropsStore((state) => state.currentCssClassName);
const totalCssPropertyObj = useCssPropsStore((state) => state.totalCssPropertyObj);
const selectedCssCategory = useCssPropsStore((state) => state.selectedCssCategory);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구조분해할당을 지양하는 것이 베스트지만, 여러 상태를 불러와야할 경우 useShallow도 추천드립니당

@Ujaa
Copy link
Contributor

Ujaa commented Jan 24, 2025

구조 분해 할당이 문제였던 거군요..! 덕분에 zustand에 대해 조금 더 이해할 수 있었습니다. 감사합니다!

@lee0jae330 lee0jae330 merged commit 8e6a057 into dev Jan 24, 2025
5 checks passed
@lee0jae330 lee0jae330 deleted the refactor/26 branch January 24, 2025 06:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
refactor 리팩토링
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[refactor] 워크스페이스 페이지 불필요한 리렌더링 개선
4 participants