-
불필요한 렌더링으로 인한 UI 버벅임 개선하기프론트엔드 2025. 10. 10. 22:56
⚠️ 문제 상황

- 부트캠프 프로젝트에서 습관 형성을 돕는 히트맵 기반 챌린지 UI를 구현하던 중, 사용자가 여러 챌린지를 비교하며 확인할 수 있도록 Swiper를 활용한 슬라이드 구조를 적용했다.
- 하지만 실제로 UI를 사용해보는 과정에서 슬라이드를 전환할 때마다 이미지가 반복적으로 렌더링되며 화면 전환 시 버벅임이 발생하는 문제를 확인했다.
- React DevTools를 통해 분석한 결과, Swiper 슬라이드 이동 시 각 이미지 컴포넌트가 불필요하게 재렌더링되고 있었다.
→ 이는 사용자 입장에서 UI 반응성이 떨어지고, 체감 성능이 저하되는 문제로 이어질거라고 예상이 되어 빠른 처리가 필요해보였다
🔍 원인 분석
- 프로젝트를 구현하면서 렌더링 최적화에 대해서 고민을 하던 중, Swiper.js의 slideItem의 렌더링의 문제점이 생겼다.
- 개발자라면 익숙할 히트맵 UI를 구현하던 중 문제가 생겼다

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
- 위 영상을 보면 스와이프 될 때마다 21/66개 이미지가 각각 재렌더링 되는 것을 볼 수 있다.
어떤 것이 문제일까..?
문제: 슬라이드 전환 시 이미지 아이콘들이 불필요하게 재렌더링되어 버벅임이 발생했다.
원인: Swiper의 내부 상태 변경으로 상위 컴포넌트가 리렌더링되면서, props 변화가 없는 자식 컴포넌트까지 함께 렌더링되는 구조였다.해결 방안
- 슬라이드 전환 시 불필요한 렌더링을 줄이기 위해 처음에는 데이터 연산 비용을 줄이는 방향으로 접근했다.
- 챌린지 진행도 계산 로직은 기간 계산, 루틴 매칭, 일별 집계 등 연산량이 많아질 수 있는 부분이었기 때문에 불필요한 재계산을 방지하기 위해 useMemo를 적용했다.
const slidesData = useMemo(() => { return challenge.map(challenge => { const start = new Date(challenge.createdAt); const end = new Date(challenge.endAt); const days = Math.round((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); const { dailyCompletions } = calculateSingleChallengeProgress( challenge, routines, routineCompletions, days ); const category = CATEGORY_CONFIG.find( challengeCategory => challengeCategory.id === challenge.categoryId ); return { id: challenge.id, name: challenge.name, src: category?.src, color: category?.color, dailyCompletions, categoryName: category?.name, }; }); }, [challenge, routines, routineCompletions]);
하지만 렌더링 문제는 해결되지 않았다.
→ 원인을 다시 분석해보니, 문제는 연산 비용이 아니라 컴포넌트 리렌더링 구조에 있었다.
useMemo는 연산 결과를 캐싱하는 역할만 할 뿐 컴포넌트의 렌더링 자체를 제어하지는 못한다는 점을 확인했다.
다만 해당 계산 로직은 여전히 비용이 발생할 수 있는 부분이기 때문에 useMemo는 유지하고 렌더링 문제는 별도로 해결하기로 했다.
{slidesData.map(slide => ( <SwiperSlide key={slide.id}> <SlideContent name={slide.name} categorySrc={slide.src} categoryColor={slide.color} dailyCompletions={slide.dailyCompletions} categoryName={slide.categoryName} /> </SwiperSlide> ))}→ SlideContent를 React.memo로 감싸 props가 변경되지 않는 경우에는 재렌더링이 발생하지 않도록 최적화했다.

- 그 결과, 부모 컴포넌트가 리렌더링되더라도 자식 컴포넌트는 불필요하게 다시 렌더링되지 않도록 개선할 수 있었다.
- 특히 슬라이드 전환 시 이미지 아이콘이 반복적으로 다시 그려지는 문제를 줄이고 화면 전환 시 체감 성능을 개선할 수 있었다.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
- 또한 크롬 extension React Developer Tools를 사용해 렌더링 시간을 측정해봤다
- React.memo의 사용여부에 따라 Render duration이 4초 정도 차이를 평균 0.8~1.4ms로 감소시켰다.


📘 정리
이번 경험을 통해, 연산 최적화(useMemo)와 렌더링 최적화(React.memo)는 서로 다른 문제를 해결하는 도구라는 점을 확인했다.
- useMemo → 연산 비용 최적화
- React.memo → 컴포넌트 렌더링 제어
→ 상황에 맞게 두 가지를 구분하여 적용하는 것이 중요했다.
'프론트엔드' 카테고리의 다른 글
React 딥다이브 (0) 2025.10.15 Next Image 최적화와 SVG (0) 2025.10.12 자바스크립트 딥다이브 (비동기 처리) (0) 2025.10.08 모던 자바스크립트 딥다이브 (브라우저 렌더링 과정) (0) 2025.10.08 자바스크립트 딥다이브 ppt 클로저 (0) 2025.09.29