Skip to main content

React 앱 느려짐? 프레임워크 탓 그만! 브라우저 API인 **template**와 **requestIdleCallback**으로 UI jank 완전 정복

Published 3 min readMarkdown

# 서론 – “왜 내 React가 이렇게 느릴까?”

React 애플리케이션을 개발하다 보면 UI jank(프레임이 끊기는 현상) 때문에 사용자가 불편함을 호소하는 경우가 많습니다. 

많은 개발자들이 즉시 “React 자체가 느리다”거나 “가상 DOM이 문제다”라고 결론짓지만, 실제 원인은 프레임워크가 아닌 브라우저 레벨에서의 비효율적인 작업에 있을 때가 훨씬 많습니다.

이번 글에서는 초보자도 바로 적용할 수 있는 브라우저 API인 template 요소와 requestIdleCallback을 활용해 React 앱의 성능 최적화와 UI jank 해결 방법을 단계별로 안내합니다.


# 1️⃣ UI jank가 발생하는 대표적인 상황

| 상황 | 원인 | 해결 힌트 |

|------|------|----------|

| 대량의 리스트를 한 번에 렌더링 | 메인 스레드에 과도한 DOM 조작 | virtualization(가상 스크롤) + requestIdleCallback |

| 이미지·비디오 등 무거운 리소스 로드 | 네트워크와 디코딩 지연 | loading='lazy' + template 활용 |

| 복잡한 컴포넌트 트리 재렌더링 | 불필요한 상태 업데이트 | React.memo, useCallback + 비동기 작업 분리 |

위 표를 보며 현재 프로젝트에서 가장 많이 겪는 상황을 체크해 보세요.


# 2️⃣ template 요소로 “렌더링 비용”을 미리 계산하기

# 2.1 template이란?

<template> 태그는 DOM에 바로 삽입되지 않는 HTML 조각을 보관하는 컨테이너입니다.

브라우저는 이 안의 마크업을 파싱하지만 렌더링은 하지 않으므로 메모리만 차지하고, 필요할 때 content.cloneNode(true) 로 복제해 삽입하면 됩니다.

# 2.2 React와 함께 쓰는 방법

jsx

function ListItem({item}) {

  return (

    <li>{item.title}</li>

  );

}

// template을 미리 선언

const listTemplate = document.createElement('template');

listTemplate.innerHTML = `

  <ul id='item-list'></ul>

`;

export default function ItemList({data}) {

  const containerRef = useRef(null);

  useEffect(() => {

    // 최초 마운트 시 template 복제

    const listEl = listTemplate.content.cloneNode(true).firstElementChild;

    containerRef.current.appendChild(listEl);

    // data를 한 번에 삽입하지 말고 requestIdleCallback으로 나눔

    data.forEach(item => {

      requestIdleCallback(deadline => {

        if (deadline.timeRemaining() > 0) {

          const li = document.createElement('li');

          li.textContent = item.title;

          listEl.appendChild(li);

        }

      });

    });

  }, [data]);

  return <div ref={containerRef} />;

}
  • 장점

    1. 초기 파싱 비용은 한 번만 발생 → 렌더링 비용 감소

    2. requestIdleCallback과 결합해 메인 스레드가 바쁠 때는 작업을 미뤄 UI jank 방지


# 3️⃣ requestIdleCallback으로 “백그라운드 작업” 처리하기

# 3.1 API 개요

js

requestIdleCallback(callback, { timeout: 2000 });
  • 브라우저가 유휴 시간(idle time)일 때 콜백을 실행합니다.

  • deadline.timeRemaining() 으로 남은 유휴 시간을 확인해 작업량을 조절할 수 있습니다.

# 3.2 실제 활용 예시

# a) 대량 데이터 페이징 로드

js

function loadMoreItems(items) {

  let i = 0;

  function schedule() {

    requestIdleCallback(deadline => {

      while (i < items.length && deadline.timeRemaining() > 0) {

        renderItem(items[i]); // 실제 DOM 삽입 로직

        i++;

      }

      if (i < items.length) schedule(); // 남은 작업이 있으면 재귀 호출

    });

  }

  schedule();

}

# b) 이미지 프리로드

js

function preloadImages(urls) {

  urls.forEach(url => {

    requestIdleCallback(() => {

      const img = new Image();

      img.src = url;

    });

  });

}

# 3.3 주의사항

| 상황 | 권고사항 |

|------|----------|

| 유휴 시간이 거의 없을 때 | setTimeout 대체 사용을 고려 |

| 중요한 UI 업데이트가 동시에 필요 | requestIdleCallback 대신 requestAnimationFrame 사용 |


# 4️⃣ 초보자를 위한 실전 체크리스트

  1. 렌더링 비용 측정 – Chrome DevTools > Performance 탭에서 “Main” 스레드의 긴 작업을 확인한다.

  2. 불필요한 재렌더링 차단 – React.memo, useMemo, useCallback을 적절히 적용한다.

  3. template으로 미리 파싱 – 정적 마크업은 <template>에 보관하고 필요 시 복제한다.

  4. 백그라운드 작업 분산 – requestIdleCallback을 활용해 비동기 작업을 유휴 시간에 수행한다.

  5. Lazy Load 적용 – 이미지·비디오 등 무거운 리소스는 loading='lazy'와 IntersectionObserver로 지연 로드한다.


# 결론 – 지금 바로 UI jank와 작별하고 빠른 React를 경험하세요!

React 자체는 강력하지만 프레임워크를 탓하기보다는 브라우저 레벨에서의 최적화가 핵심입니다.

template과 requestIdleCallback을 적절히 활용하면 메인 스레드의 부담을 크게 줄이고, 사용자는 부드러운 화면 전환을 체감하게 됩니다.

행동 유도

  • 오늘 프로젝트에서 template을 하나라도 적용해 보세요.

  • requestIdleCallback을 사용해 비동기 작업을 분산시키고, 성능 측정 결과를 공유해 주세요!

React 성능 최적화, UI jank 해결, requestIdleCallback 사용법을 키워드로 검색하면 더 많은 사례와 튜토리얼을 확인할 수 있습니다.

지금 바로 적용해 보고, 느려지던 앱이 눈에 띄게 빨라지는 변화를 직접 경험해 보세요! 🚀

No comments yet. Be the first to comment.

More from this blog