Skip to content

Conversation

@minjeoong
Copy link
Member

@minjeoong minjeoong commented Dec 29, 2025

📌 Summary

close #481

해당 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가 증가하지 않았고, 즉 매 프레임 리렌더링이 발생하는 상황이 아니라는 것을 먼저 확인할 수 있었어요.

스크린샷 2025-12-29 22 50 12

Profiler는 크게 다음을 보여줍니다.

  • React가 Commit(=DOM 반영) 을 몇 번 수행했는지
  • 각 Commit 과정에서 어떤 컴포넌트들이 render phase를 실행했는지
  • 각 단계에서의 실행 시간(렌더링 비용)

Profiler에서 카운트가 계속 증가하는 것은 RecommendedInfoSection이 매 프레임 다시 렌더되고 있어서가 아니라, Carousel의 autoplay 애니메이션이 매 프레임 state 업데이트를 발생시키고 → 그 결과 React commit이 계속 일어나고 있었기 때문이었습니다.

즉,

원래 코드

<section className={styles.infoSection}>
  <img ... />
  <div className={styles.titleSection}>...</div>
  <Carousel>...</Carousel>  {/* transform 변경 */}
  <div className={styles.bottomButton}>...</div>
</section>

Carousel 의 transtform 이 변경되면 브라우저가 전체 section 을 하나의 페인팅 단위로 인식할 수 있어요.
그래서 브라우저가 변경 범위를 정확히 구분하지 못해 전체 영역을 리페인트 할 수 있습니다.

해결 방향 - repaint 범위 제한

변경 코드

<section className={styles.infoSection}>
  <StaticContent />      
  <Carousel>...</Carousel>  
  <BottomButton />       
</section>

다음과 같이 분리하여 각 컴포넌트가 독립적인 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

@minjeoong minjeoong requested a review from a team as a code owner December 29, 2025 16:12
@minjeoong minjeoong requested review from 1jiwoo27, gwagjiug, hansoojeongsj, jeonghoon11 and jogpfls and removed request for a team December 29, 2025 16:12
@github-actions
Copy link
Contributor

github-actions bot commented Dec 29, 2025

✅ Storybook이 배포되었습니다.
🔗 바로가기

@minjeoong minjeoong changed the title refactor: home 화면 리페인트 최적화 Refactor(ui): home 화면 리페인트 최적화 Dec 29, 2025
@minjeoong minjeoong changed the title Refactor(ui): home 화면 리페인트 최적화 Refactor(client): home 화면 리페인트 최적화 Dec 29, 2025
@minjeoong minjeoong self-assigned this Dec 29, 2025
Comment on lines 96 to 98
const handleNavigateReport = useCallback(() => {
navigate(routePath.REPORT);
}, [navigate]);
Copy link
Member

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 범위 분리에 있는 만큼 단순 함수로 구현해도 동작 및 성능 측면에서 문제는 없을 것 같아 보여욥!

Copy link
Member Author

Choose a reason for hiding this comment

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

동의합니다 반영했습니다 👍🏻

Copy link
Member

@jeonghoon11 jeonghoon11 left a comment

Choose a reason for hiding this comment

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

이번 PR을 통해 컴포넌트 분리가 repaint 범위 축소에 어떤 효과를 줄 수 있는지 이해할 수 있었네요 굿굿!

Copy link
Member

@jogpfls jogpfls left a comment

Choose a reason for hiding this comment

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

컴포넌트 분리가 이렇게 중요한지 처음 알았네요.. 코드리뷰 하려고 PR 읽으면서 여러모로 공부가 많이 되었어요!! 👍

Comment on lines +18 to +25
interface StaticContentProps {
userName?: string;
productName?: string;
company?: string;
keywordChips?: string[];
}

/** 보험 추천받은 유저가 볼 화면 */
export const RecommendedInfoSection = ({
const StaticContent = ({
Copy link
Member

@jogpfls jogpfls Dec 29, 2025

Choose a reason for hiding this comment

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

따로 이 컴포넌트들을 파일로 분리 안하신 이유가 있나요?!

Copy link
Member Author

Choose a reason for hiding this comment

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

파일 분리를 고민하다가, 재사용 목적이 아니고 분산시킬 필요성이 우리 눈에 깔끔한 거라면 불필요하다고 생각했어요.
StaticContent, BottomButton 이 이 파일에서만 쓰이는 로컬 UI 이기 때문에 같은 파일에 두어서 지역성을 유지하는 게 낫다고 생각했는데, 요 부분은 코드리뷰에서 논의해보고 싶었어요. 혜린님은 분리하는 게 좋다고 생각하시나요!

@minjeoong minjeoong merged commit 893132e into develop Dec 30, 2025
4 checks passed
@minjeoong minjeoong deleted the refactor/carousel-rendering-#481 branch December 30, 2025 18:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor] 무한 캐러셀 렌더링 최적화

4 participants