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

---

# # 서론 – “왜 내 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 사용법**을 키워드로 검색하면 더 많은 사례와 튜토리얼을 확인할 수 있습니다.

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