런타임 성능 최적화: FPS, requestAnimationFrame, React Profiler, Performance Timeline API

김진아·2026년 6월 8일

페이지가 빠르게 로딩되는 것만으로 충분하지는 않다.
화면이 처음 나타난 이후 사용자가 스크롤하거나 버튼을 클릭하고, 모달을 열거나 애니메이션을 볼 때도 화면이 부드럽게 반응해야 한다.

이번 글에서는 런타임 성능 최적화와 관련된 핵심 개념을 정리해보겠다.

  • FPS와 Frame rendering stats
  • requestAnimationFrame
  • React Profiler
  • Performance Timeline API

로딩 성능과 런타임 성능

웹 성능은 크게 로딩 성능과 런타임 성능으로 나누어 생각할 수 있다.

로딩 성능

사용자가 페이지에 처음 진입했을 때 콘텐츠가 얼마나 빠르게 나타나는지 확인한다.

예를 들면 다음과 같다.

FCP
LCP
CLS
이미지 로딩 시간
폰트 로딩 시간
JavaScript 번들 크기
네트워크 요청 지연

이미지 크기를 줄이거나, 폰트를 최적화하고, 코드 스플리팅을 적용하는 작업은 주로 로딩 성능과 관련이 있다.

런타임 성능

페이지가 이미 로딩된 이후 사용자 동작에 얼마나 빠르고 부드럽게 반응하는지 확인한다.

예를 들면 다음과 같다.

스크롤할 때 화면이 버벅이는가?
애니메이션이 끊겨 보이는가?
검색어를 입력할 때 타이핑이 밀리는가?
모달을 열 때 화면이 순간적으로 멈추는가?
상태 변경 시 불필요한 컴포넌트까지 다시 렌더링되는가?

런타임 성능 최적화는 화면이 실제로 그려지는 흐름과 밀접하게 연결되어 있다.


브라우저가 화면을 그리는 과정

사용자가 버튼을 클릭하거나 스크롤하면 JavaScript가 실행되고, 상태나 DOM이 변경될 수 있다.

그 이후 브라우저는 화면을 다시 그리기 위해 다음 과정을 거친다.

JavaScript 실행
      ↓
Style Calculation
      ↓
Layout
      ↓
Paint
      ↓
Composite

FPS와 Frame rendering stats

Frame이란?

브라우저는 움직이는 화면을 한 번에 만드는 것이 아니다.
짧은 간격으로 정지 화면을 계속 새로 만들어 보여준다.

이때 화면 한 장을 Frame이라고 한다.

1번째 Frame: 요소가 x = 0에 위치
2번째 Frame: 요소가 x = 5에 위치
3번째 Frame: 요소가 x = 10에 위치
4번째 Frame: 요소가 x = 15에 위치

이 정지 화면들이 빠르게 교체되면 사용자는 요소가 자연스럽게 움직이는 것처럼 느낀다.

FPS란?

FPS(Frames Per Second)는 1초 동안 표시되는 Frame의 수다.

일반적인 60Hz 모니터를 기준으로 브라우저는 1초 동안 약 60개의 Frame을 보여줄 수 있다.

1000ms ÷ 60 ≈ 16.67ms

즉, 브라우저는 약 16.67ms 안에 하나의 Frame을 준비해야 한다.

Frame 예산을 초과하면?

한 Frame 안에서 처리해야 하는 작업이 너무 많다고 가정해 보자.

JavaScript 실행       8ms
Style Calculation     3ms
Layout                6ms
Paint                 5ms
-------------------------
총합                 22ms

총 작업 시간이 16.67ms를 초과했다.

이 경우 브라우저는 다음 Frame을 제시간에 보여주지 못한다. 일부 Frame이 누락되면서 화면이 끊겨 보일 수 있다.

이를 흔히 Frame Drop 또는 Jank라고 한다.

Long Task와 Frame Drop의 관계

Chrome Performance 패널에서 다음과 같은 작업을 확인했다고 가정해 보자.

긴 작업 소요 시간: 62.93ms

60FPS 기준으로 한 Frame 예산은 약 16.67ms다.

62.93ms ÷ 16.67ms ≈ 3.8

하나의 긴 작업 때문에 약 3~4개의 Frame을 제시간에 처리하지 못할 가능성이 있다.

따라서 Performance 패널에서 긴 작업을 분석할 때는 다음 질문을 함께 생각해야 한다.

이 작업 때문에 Frame 예산을 초과하고 있지는 않은가?

Frame rendering stats란 무엇인가?

Frame rendering stats는 현재 페이지의 FPS와 Frame 상태를 실시간으로 확인할 수 있는 도구다.

Chrome에서 다음 순서로 확인할 수 있다.

개발자 도구 열기
→ Ctrl + Shift + P
→ Rendering 검색
→ Show Rendering 선택
→ Frame rendering stats 체크

정리하면 다음과 같다.

FPS
→ 1초 동안 표시되는 Frame 수

Frame rendering stats
→ Chrome DevTools에서 FPS 상태를 확인하는 도구

requestAnimationFrame

requestAnimationFrame이란?

requestAnimationFrame()은 브라우저에게 다음과 같이 요청하는 API다.

다음 화면을 다시 그리기 직전에 이 함수를 실행해 줘.

일반적으로 줄여서 rAF라고 부른다.

requestAnimationFrame(() => {
  console.log("다음 Frame 직전에 한 번 실행");
});

이 코드는 콜백을 한 번만 실행한다.

반복 실행하기

Frame마다 반복해서 실행하려면 콜백 내부에서 다시 requestAnimationFrame()을 호출해야 한다.

const animate = () => {
  console.log("Frame마다 실행");

  requestAnimationFrame(animate);
};

requestAnimationFrame(animate);

흐름은 다음과 같다.

첫 번째 Frame 직전
→ animate 실행
→ 다음 animate 실행 예약

두 번째 Frame 직전
→ animate 실행
→ 다음 animate 실행 예약

세 번째 Frame 직전
→ animate 실행
→ 다음 animate 실행 예약

60Hz 모니터라면 보통 약 16.67ms 간격으로 실행된다.

하지만 정확히 항상 같은 간격으로 실행되는 것은 아니다.

60Hz 모니터   → 약 16.67ms 간격
120Hz 모니터  → 약 8.33ms 간격
144Hz 모니터  → 약 6.94ms 간격

메인 스레드가 바쁘거나 브라우저 탭이 백그라운드에 있다면 실행 간격이 달라질 수도 있다.

실제 실행 간격 확인하기

requestAnimationFrame()은 콜백에 현재 시간을 전달한다.

let previousTime: number | null = null;

const animate = (currentTime: number) => {
  if (previousTime !== null) {
    console.log(currentTime - previousTime);
  }

  previousTime = currentTime;

  requestAnimationFrame(animate);
};

requestAnimationFrame(animate);

60Hz 환경에서는 대략 다음과 같은 값이 출력된다.

16.67
16.66
16.72
16.64

setInterval과의 차이

애니메이션을 다음처럼 작성할 수도 있다.

setInterval(() => {
  position += 5;
  box.style.transform = `translateX(${position}px)`;
}, 16);

하지만 setInterval()은 브라우저가 실제로 화면을 그리는 시점을 고려하지 않는다.

반면 requestAnimationFrame()은 다음 repaint 직전에 실행되기 때문에 화면 갱신 타이밍에 맞춰 작업을 처리하기 쉽다.

setInterval
→ 개발자가 지정한 시간마다 실행을 시도
→ 화면 갱신 시점과 어긋날 수 있음

requestAnimationFrame
→ 다음 repaint 직전에 실행
→ 애니메이션 타이밍을 맞추기 쉬움

rAF가 무조건 성능을 개선하는 것은 아니다

requestAnimationFrame()은 무거운 코드를 자동으로 가볍게 만들어 주는 기능이 아니다.

const animate = () => {
  for (let index = 0; index < 100_000_000; index += 1) {
    // 무거운 연산
  }

  requestAnimationFrame(animate);
};

한 Frame마다 무거운 작업을 실행하면 여전히 화면은 끊긴다.

핵심은 다음과 같다.

requestAnimationFrame
→ 무거운 작업을 자동으로 최적화하는 기능 X
→ 화면을 그리는 타이밍에 맞춰 작업을 예약하는 기능 O

이동 거리는 시간 기준으로 계산하기

다음 코드처럼 Frame마다 고정된 값을 더하면 모니터 주사율에 따라 이동 속도가 달라질 수 있다.

position += 5;

60Hz 환경에서는 1초에 약 300px 이동한다.

5px × 60 = 300px

120Hz 환경에서는 1초에 약 600px 이동한다.

5px × 120 = 600px

따라서 지난 Frame 이후 흐른 시간을 기준으로 이동 거리를 계산하는 편이 좋다.

let previousTime: number | null = null;
let position = 0;

const animate = (currentTime: number) => {
  if (previousTime !== null) {
    const deltaTime = currentTime - previousTime;

    position += 0.3 * deltaTime;

    box.style.transform = `translateX(${position}px)`;
  }

  previousTime = currentTime;

  requestAnimationFrame(animate);
};

requestAnimationFrame(animate);

deltaTime은 지난 Frame 이후 흐른 시간이다.

이 방식을 사용하면 모니터 주사율이 달라도 이동 속도를 일정하게 유지하기 쉽다.

React에서 항상 rAF를 사용해야 할까?

그렇지 않다.

버튼 클릭, 좋아요 상태 변경, 모달 열기처럼 일반적인 UI 상태 변경은 React에 맡기면 된다.

const [isOpen, setIsOpen] = useState(false);

<button onClick={() => setIsOpen(true)}>
  모달 열기
</button>

requestAnimationFrame()은 주로 화면이 지속적으로 바뀌는 작업에 사용한다.

직접 구현한 애니메이션
드래그 앤 드롭
스크롤 위치 기반 UI
Canvas 애니메이션
게임 화면
실시간 그래프
마우스 이동을 따라가는 요소

React Profiler

React Profiler란?

React Profiler는 React 컴포넌트의 렌더링 성능을 측정하는 도구다.

다음과 같은 질문에 답하기 위해 사용한다.

버튼을 하나 눌렀는데 왜 화면이 버벅이는가?
검색창에 글자를 입력했는데 왜 카드 목록 전체가 다시 렌더링되는가?
어떤 컴포넌트의 렌더링 비용이 가장 큰가?
memo, useMemo, useCallback을 적용한 효과가 실제로 있는가?

React 렌더링과 브라우저 렌더링

React 렌더링과 브라우저 렌더링은 다르다.

React 렌더링

React가 컴포넌트 함수를 실행하여 어떤 UI가 필요한지 계산하는 과정이다.

state 또는 props 변경
        ↓
컴포넌트 함수 다시 실행
        ↓
이전 JSX와 새로운 JSX 비교
        ↓
필요한 변경 사항만 실제 DOM에 반영

브라우저 렌더링

DOM과 CSS를 기반으로 화면을 실제로 그리는 과정이다.

Style Calculation
→ Layout
→ Paint
→ Composite

React 컴포넌트가 다시 실행되었다고 해서 실제 DOM이 반드시 변경되는 것은 아니다.

하지만 컴포넌트 함수가 불필요하게 많이 실행되고 내부 연산까지 반복된다면 React 자체의 계산 비용이 커질 수 있다.

React DevTools Profiler 사용 방법

React Developer Tools를 설치하면 Chrome 개발자 도구에서 Profiler 탭을 사용할 수 있다.

개발자 도구 열기
→ Profiler 탭
→ Record 버튼 클릭
→ 분석할 동작 하나 수행
→ 기록 종료

분석할 동작은 하나씩 나누어 확인하는 것이 좋다.

검색창에 글자 한 개 입력
좋아요 버튼 클릭
수량 증가 버튼 클릭
모달 열기
페이지 이동

Commit

Profiler 화면 상단에는 여러 개의 막대가 표시된다.

각 막대는 하나의 Commit을 의미한다.

Render Phase
→ 무엇이 바뀌어야 하는지 계산

Commit Phase
→ 계산된 변경 사항을 실제 DOM에 반영

React가 계산한 결과를 실제 화면에 반영한 한 번의 작업이 하나의 Commit이다.

막대가 높을수록 렌더링 비용이 큰 Commit일 가능성이 있다.

Flamegraph

특정 Commit을 클릭하면 컴포넌트 구조를 확인할 수 있다.

App
└── PostListPage
    ├── SearchInput
    ├── PostItem
    ├── PostItem
    └── PostItem

검색어를 한 글자 입력했는데 모든 PostItem이 다시 렌더링되었다면 다음 내용을 확인해야 한다.

모든 카드가 다시 렌더링되어야 하는가?
카드에 전달되는 props가 실제로 변경되었는가?
카드 내부에 무거운 계산이 있는가?

Ranked Chart

Ranked Chart에서는 렌더링 비용이 큰 컴포넌트를 순서대로 확인할 수 있다.

1. PostListPage     8.3ms
2. PostItem         4.1ms
3. SearchInput      0.8ms
4. Header           0.3ms

우선 시간이 오래 걸리는 컴포넌트부터 확인한다.

다시 렌더링된 이유 확인하기

Profiler 설정에서 다음 옵션을 활성화하면 각 컴포넌트가 렌더링된 원인을 확인할 수 있다.

Record why each component rendered while profiling

대표적인 원인은 다음과 같다.

props 변경
state 변경
Hook 변경
부모 컴포넌트 렌더링

불필요한 리렌더링 예시

const Parent = () => {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    console.log("카드 클릭");
  };

  return (
    <>
      <button onClick={() => setCount((prev) => prev + 1)}>
        {count}
      </button>

      <PostItem onClick={handleClick} />
    </>
  );
};

count가 바뀔 때마다 Parent가 다시 실행된다.

그 과정에서 handleClick 함수도 다시 만들어진다.

이전 렌더링의 handleClick
!==
다음 렌더링의 handleClick

PostItemmemo()로 감싸더라도 함수 props의 참조가 달라졌기 때문에 다시 렌더링될 수 있다.

const PostItem = memo(({ onClick }: PostItemProps) => {
  return <button onClick={onClick}>게시글</button>;
});

필요하다면 useCallback()을 고려할 수 있다.

const handleClick = useCallback(() => {
  console.log("카드 클릭");
}, []);

하지만 모든 코드에 무조건 useCallback()을 추가하면 안 된다.

Profiler로 불필요한 렌더링 확인
→ 원인 확인
→ 필요한 경우에만 최적화 적용
→ Profiler로 개선 효과 검증

자주 확인하는 문제

부모 상태 변경으로 자식 전체가 렌더링되는 경우

모달 상태 변경
→ Page 렌더링
→ PostList 렌더링
→ PostItem 수백 개 렌더링

매번 새로운 객체를 props로 전달하는 경우

<PostItem style={{ marginTop: 12 }} />

객체는 내용이 같아도 렌더링할 때마다 새로 생성된다.

{ marginTop: 12 } !== { marginTop: 12 }

무거운 계산을 반복하는 경우

const filteredProducts = products
  .filter((product) => product.name.includes(keyword))
  .sort((a, b) => b.price - a.price);

데이터가 많고 계산 비용이 실제로 크다면 useMemo()를 고려할 수 있다.

const filteredProducts = useMemo(() => {
  return products
    .filter((product) => product.name.includes(keyword))
    .sort((a, b) => b.price - a.price);
}, [products, keyword]);

Context나 전역 상태를 너무 넓게 구독하는 경우

Zustand 같은 전역 상태 관리 도구를 사용할 때는 필요한 상태만 선택하여 구독하는 것이 좋다.

const isModalOpen = useModalStore(
  (state) => state.isModalOpen,
);

코드에서 Profiler 사용하기

React의 <Profiler> 컴포넌트를 직접 사용할 수도 있다.

import {
  Profiler,
  type ProfilerOnRenderCallback,
} from "react";

const handleRender: ProfilerOnRenderCallback = (
  id,
  phase,
  actualDuration,
  baseDuration,
) => {
  console.table({
    영역: id,
    렌더링종류: phase,
    실제렌더링시간: `${actualDuration.toFixed(2)}ms`,
    기준렌더링시간: `${baseDuration.toFixed(2)}ms`,
  });
};

const PostPage = () => {
  return (
    <Profiler id="PostList" onRender={handleRender}>
      <PostList />
    </Profiler>
  );
};

주요 값은 다음과 같다.

의미
id측정 영역 이름
phase최초 렌더링인지 업데이트인지 구분
actualDuration이번 렌더링에서 실제로 걸린 시간
baseDuration최적화가 없다고 가정했을 때 예상되는 기준 비용

React Profiler 사용 원칙

렌더링이 발생했다고 해서 무조건 문제가 있는 것은 아니다.

React에서 상태가 변경되면 다시 렌더링되는 것이 자연스럽다.

다음과 같은 경우에 최적화를 고민하면 된다.

사용자가 체감할 정도로 느림
렌더링 시간이 실제로 큼
불필요한 컴포넌트가 대량으로 반복 렌더링됨
동일한 무거운 계산이 자주 반복됨

memo, useMemo, useCallback도 공짜가 아니다.

값을 비교하고 메모리에 저장하는 비용이 추가되므로, Profiler로 실제 병목을 확인한 뒤 필요한 경우에만 적용하는 것이 좋다.


Performance Timeline API

Performance Timeline API란?

Performance Timeline API는 브라우저가 수집한 성능 기록을 시간순으로 확인할 수 있게 해 주는 API 체계다.

브라우저 내부에 성능 기록용 타임라인이 존재한다고 생각하면 쉽다.

페이지 진입
  ↓
HTML 문서 요청
  ↓
CSS 파일 다운로드
  ↓
이미지 다운로드
  ↓
첫 콘텐츠 표시
  ↓
사용자 정의 작업 시작
  ↓
사용자 정의 작업 종료

이 타임라인에 여러 종류의 기록이 쌓이고, JavaScript 코드에서 기록을 조회하거나 관찰할 수 있다.

DevTools Performance 탭과의 차이

Chrome DevTools Performance 탭은 개발자가 직접 화면을 열고 녹화하여 분석한다.

개발자 도구 열기
→ Performance 탭
→ Record
→ 동작 수행
→ 기록 종료
→ 결과 분석

Performance Timeline API는 코드에서 성능 데이터를 조회하거나 수집한다.

const entries = performance.getEntries();

console.log(entries);
방식특징
Chrome Performance 패널사람이 직접 상세 흐름을 시각적으로 분석
Performance Timeline API코드에서 성능 기록을 조회하거나 수집
React ProfilerReact 컴포넌트 렌더링 비용에 집중

PerformanceEntry

타임라인에 저장되는 성능 기록 하나를 PerformanceEntry라고 한다.

{
  name: "https://example.com/image.webp",
  entryType: "resource",
  startTime: 324.5,
  duration: 81.2
}
의미
name기록 이름
entryType기록 종류
startTime측정 시작 시점
duration걸린 시간

기록 조회하기

모든 기록을 조회할 수 있다.

const entries = performance.getEntries();

console.log(entries);

특정 종류의 기록만 조회할 수도 있다.

const resources =
  performance.getEntriesByType("resource");

console.log(resources);

특정 이름을 가진 기록만 조회할 수도 있다.

const results =
  performance.getEntriesByName("filter-posts");

console.log(results);

대표적인 entryType

entryType의미
"navigation"페이지 진입과 HTML 문서 로딩
"resource"이미지, CSS, JavaScript 등의 리소스 로딩
"mark"개발자가 직접 표시한 특정 시점
"measure"개발자가 직접 측정한 구간
"paint"콘텐츠가 화면에 그려진 시점
"longtask"메인 스레드를 오래 점유한 작업

performance.mark()

performance.mark()는 타임라인에 특정 시점을 기록한다.

performance.mark("filter-start");

스톱워치의 시작 지점을 표시하는 것과 비슷하다.

performance.measure()

performance.measure()는 두 시점 사이의 시간을 계산하여 타임라인에 기록한다.

performance.mark("filter-start");

// 측정할 작업

performance.mark("filter-end");

performance.measure(
  "filter-posts",
  "filter-start",
  "filter-end",
);

게시글 필터링 시간 측정하기

performance.mark("filter-start");

const filteredPosts = postList.filter((post) =>
  post.title.includes(keyword),
);

performance.mark("filter-end");

performance.measure(
  "filter-posts",
  "filter-start",
  "filter-end",
);

const results =
  performance.getEntriesByName("filter-posts");

console.log(results);

결과는 대략 다음과 같다.

[
  {
    name: "filter-posts",
    entryType: "measure",
    startTime: 824.4,
    duration: 1.72,
  },
]

duration: 1.72라면 필터링에 약 1.72ms가 걸렸다는 의미다.

performance.now()와의 차이

간단한 측정에는 performance.now()를 사용할 수도 있다.

const startTime = performance.now();

const filteredPosts = postList.filter((post) =>
  post.title.includes(keyword),
);

const endTime = performance.now();

console.log(endTime - startTime);

차이는 다음과 같다.

방식특징
performance.now()간단하게 시간 차이를 확인하기 좋음
performance.mark() + performance.measure()의미 있는 이름을 붙여 타임라인에 기록하기 좋음

기록 초기화하기

같은 이름으로 측정하면 결과가 계속 누적된다.

performance.clearMarks();
performance.clearMeasures();

특정 이름의 기록만 지울 수도 있다.

performance.clearMarks("filter-start");
performance.clearMarks("filter-end");
performance.clearMeasures("filter-posts");

PerformanceObserver

새로운 성능 기록이 생길 때마다 자동으로 감지하려면 PerformanceObserver를 사용할 수 있다.

const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();

  entries.forEach((entry) => {
    console.log(entry.name, entry.duration);
  });
});

observer.observe({
  entryTypes: ["measure"],
});

이후 새로운 measure 기록을 만들면 Observer가 자동으로 감지한다.

performance.mark("filter-start");

// 측정할 작업

performance.mark("filter-end");

performance.measure(
  "filter-posts",
  "filter-start",
  "filter-end",
);

긴 작업 감지하기

일부 환경에서는 "longtask" 타입을 관찰하여 메인 스레드를 오래 막은 작업을 감지할 수 있다.

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log("긴 작업 감지:", entry.duration);
  });
});

observer.observe({
  entryTypes: ["longtask"],
});

Long Task는 메인 스레드를 50ms 이상 점유한 작업이다.

긴 작업 감지: 72.4

이 경우 메인 스레드가 약 72.4ms 동안 막혀 있었다는 의미다.

60FPS 기준 한 Frame 예산은 약 16.67ms이므로 화면이 순간적으로 끊길 가능성이 있다.


도구별 역할 비교

각 도구는 서로 대체하는 관계가 아니다.
문제 원인을 좁히기 위해 함께 사용한다.

도구중심 관점확인할 내용
Frame rendering stats사용자 체감FPS가 실제로 떨어지는가?
requestAnimationFrame()렌더링 타이밍화면 갱신 타이밍에 맞춰 작업하고 있는가?
React ProfilerReact 컴포넌트불필요한 리렌더링이 발생하는가?
Performance Timeline API코드 기반 측정특정 로직에 몇 ms가 걸리는가?
Chrome Performance 패널브라우저 전체 흐름JS, Layout, Paint 중 무엇이 병목인가?

런타임 성능 최적화 흐름

실제 화면에서 버벅임을 발견했다면 다음 순서로 접근할 수 있다.

사용자가 화면 버벅임을 느낌
        ↓
Frame rendering stats로 FPS 저하 확인
        ↓
React Profiler로 불필요한 리렌더링 확인
        ↓
Performance Timeline API로 특정 로직 시간 측정
        ↓
Chrome Performance 패널로
JavaScript, Layout, Paint 흐름 분석
        ↓
원인에 맞춰 개선
        ↓
같은 조건에서 다시 측정

예를 들어 검색 기능이 느리다면 다음과 같이 원인을 구분할 수 있다.

검색 요청 시간이 긴가?
데이터 가공 시간이 긴가?
React 렌더링 시간이 긴가?
Layout 또는 Paint 비용이 큰가?

막연히 화면이 느리다고 판단하는 것이 아니라, 구간별로 시간을 측정하여 실제 병목을 찾아야 한다.


정리

FPS
→ 1초 동안 표시되는 Frame 수

Frame rendering stats
→ Chrome DevTools에서 FPS 상태를 확인하는 도구

requestAnimationFrame
→ 다음 repaint 직전에 작업을 실행하도록 예약하는 API

React Profiler
→ React 컴포넌트의 렌더링 비용과 불필요한 리렌더링을 확인하는 도구

Performance Timeline API
→ 브라우저 성능 기록을 조회하고 특정 작업 시간을 코드로 측정하는 API 체계

런타임 성능 최적화의 핵심도 로딩 성능 최적화와 마찬가지로

측정
→ 병목 확인
→ 필요한 부분만 개선
→ 다시 측정

이 과정을 반복하여 실제 사용자 경험을 개선해야 한다.

1개의 댓글

comment-user-thumbnail
2026년 6월 8일

항상 실제 병목을 먼저 확인한 뒤에 비용 대비 효율이 있을 때만 최적화를 적용하는 게 중요한 것 같아요. 최적화 과정을 흐름대로 설명해주신 거 인상깊게 봤습니다. 각 원인에 맞는 방법을 사용하는 게 중요한 것 같네요!!

답글 달기