-
Notifications
You must be signed in to change notification settings - Fork 5
Refactor(client): home 화면 리페인트 최적화 #492
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
Conversation
|
✅ Storybook이 배포되었습니다. |
| const handleNavigateReport = useCallback(() => { | ||
| navigate(routePath.REPORT); | ||
| }, [navigate]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분에서 useCallback이 꼭 필요할까? 라는 의문이 들었어요!
현재 handleNavigateReport는 memoized 된 컴포넌트에 전달되지 않고 이 PR의 핵심 개선 포인트가 리렌더링 최적화가 아닌 repaint 범위 분리에 있는 만큼 단순 함수로 구현해도 동작 및 성능 측면에서 문제는 없을 것 같아 보여욥!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
동의합니다 반영했습니다 👍🏻
jeonghoon11
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번 PR을 통해 컴포넌트 분리가 repaint 범위 축소에 어떤 효과를 줄 수 있는지 이해할 수 있었네요 굿굿!
jogpfls
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
컴포넌트 분리가 이렇게 중요한지 처음 알았네요.. 코드리뷰 하려고 PR 읽으면서 여러모로 공부가 많이 되었어요!! 👍
| interface StaticContentProps { | ||
| userName?: string; | ||
| productName?: string; | ||
| company?: string; | ||
| keywordChips?: string[]; | ||
| } | ||
|
|
||
| /** 보험 추천받은 유저가 볼 화면 */ | ||
| export const RecommendedInfoSection = ({ | ||
| const StaticContent = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
따로 이 컴포넌트들을 파일로 분리 안하신 이유가 있나요?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
파일 분리를 고민하다가, 재사용 목적이 아니고 분산시킬 필요성이 우리 눈에 깔끔한 거라면 불필요하다고 생각했어요.
StaticContent, BottomButton 이 이 파일에서만 쓰이는 로컬 UI 이기 때문에 같은 파일에 두어서 지역성을 유지하는 게 낫다고 생각했는데, 요 부분은 코드리뷰에서 논의해보고 싶었어요. 혜린님은 분리하는 게 좋다고 생각하시나요!
📌 Summary
해당 PR에 대한 작업 내용을 요약하여 작성해주세요.
Carousel의 autoPlay 동작으로 인해 RecommendedInfoSection 전체가 매 프레임 업데이트 감지(Highlight) 되던 문제를 해결했습니다.
문제의 원인이 React 리렌더가 아닌 브라우저 repaint 범위 확장에 있음을 확인하고,
repaint 범위를 Carousel 영역으로 한정하도록 구조를 개선했습니다.
📚 Tasks
문제점
캐러셀의 autoPlay 기능이 캐러셀 렌더링만 담당하지 않고 Recommended-info-section 안에 있는 모든 컴포넌트에 대해서 React Profiler 의 rendering Highlight 에 계속 감지가 되는 문제가 있었습니다.
이게 리렌더링인지, 리페인트인지 무한으로 highlight 되고 있어서 해당 문제를 해결해야겠다고 생각했어요.
2025-12-24.18.48.16.mov
문제 원인 파악
처음에는 리렌더링이 원인이라고 판단했어요. 그래서 리렌더링 여부를 확인하기 위해, 전체 부모 컴포넌트인 RecommendedInfoSection과 내부 요소인 InsuranceSubtitle에 console.count()를 찍어 렌더 횟수를 확인했습니다.
아래와 같이 매 프레임마다 count가 증가하지 않았고, 즉 매 프레임 리렌더링이 발생하는 상황이 아니라는 것을 먼저 확인할 수 있었어요.
Profiler는 크게 다음을 보여줍니다.
Profiler에서 카운트가 계속 증가하는 것은 RecommendedInfoSection이 매 프레임 다시 렌더되고 있어서가 아니라, Carousel의 autoplay 애니메이션이 매 프레임 state 업데이트를 발생시키고 → 그 결과 React commit이 계속 일어나고 있었기 때문이었습니다.
즉,
Carousel 의 transtform 이 변경되면 브라우저가 전체 section 을 하나의 페인팅 단위로 인식할 수 있어요.
그래서 브라우저가 변경 범위를 정확히 구분하지 못해 전체 영역을 리페인트 할 수 있습니다.
해결 방향 - repaint 범위 제한
다음과 같이 분리하여 각 컴포넌트가 독립적인 Fiber 노드가 되어 브라우저가 각 부분을 독립적인 레이어로 인식할 수 있도록 했어요.
따라서 최적화의 목표를 “리렌더를 줄이는 것”이 아니라, Carousel 애니메이션으로 인해 발생하는 repaint 범위를 최소화하는 것 으로 바꾸었습니다.
repaint 를 줄이기 위해 StaticContent, Carousel, BottomButton 등 역할 단위로 분리하여
애니메이션이 발생하는 영역을 기준으로 브라우저가 자주 바뀌는 영역만 인식할 수 있도록 repaint 범위를 격리했어요.
결과적으로 이전에는 모든 요소가 매 프레임 repaint 되었다면, 개선 이후에는 repaint 범위가 carousel 로 국한되면서 불필요한 화면 갱신 비용을 낮출 수 있었습니다.
[As Is]
2025-12-24.18.48.16.mov
[To-Be]
2025-12-30.00.08.26.mov