본문 바로가기

카테고리 없음

[React] React 애플리케이션의 성능 최적화 워크플로우

반응형

 

React 애플리케이션의 성능 최적화는 사용자 경험을 향상시키고 리소스 사용을 효율화하는 중요한 과정입니다. 이 블로그에서는 React 애플리케이션의 성능을 최적화하기 위한 체계적인 워크플로우를 알아보겠습니다.

1. 성능 문제 식별하기

성능 최적화의 첫 단계는 현재 애플리케이션의 성능 문제를 정확히 파악하는 것입니다.

1.1 React DevTools 사용하기

React DevTools의 Profiler 탭을 사용하여 컴포넌트 렌더링 시간을 측정할 수 있습니다.

  1. Chrome 개발자 도구를 열고 React DevTools의 Profiler 탭으로 이동합니다.
  2. 녹화 버튼을 클릭하고 애플리케이션을 사용합니다.
  3. 녹화를 멈추고 결과를 분석합니다.
// 성능 문제가 있는 컴포넌트 예시
const SlowComponent = ({ data }) => {
  // 무거운 계산
  const processedData = data.map(item => expensiveOperation(item));

  return (
    <div>
      {processedData.map(item => (
        <div key={item.id}>{item.value}</div>
      ))}
    </div>
  );
};

이 컴포넌트는 매 렌더링마다 무거운 계산을 수행하므로 성능 문제가 발생할 수 있습니다.

1.2 Lighthouse 사용하기

Lighthouse를 사용하여 전반적인 웹 성능을 측정할 수 있습니다.

  1. Chrome 개발자 도구의 Lighthouse 탭으로 이동합니다.
  2. 분석하고자 하는 카테고리를 선택하고 "Generate report" 버튼을 클릭합니다.
  3. 결과를 검토하고 개선이 필요한 영역을 파악합니다.

2. 코드 분할 (Code Splitting) 적용하기

대규모 React 애플리케이션의 경우, 코드 분할을 통해 초기 로딩 시간을 줄일 수 있습니다.

2.1 React.lazy와 Suspense 사용하기

import React, { Suspense, lazy } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <HeavyComponent />
      </Suspense>
    </div>
  );
}

이 방식을 사용하면 HeavyComponent는 필요한 시점에만 로드됩니다.

3. 메모이제이션 활용하기

불필요한 리렌더링을 방지하기 위해 React의 메모이제이션 기능을 활용할 수 있습니다.

3.1 React.memo 사용하기

const MyComponent = React.memo(({ data }) => {
  // 렌더링 로직
});

React.memo는 props가 변경되지 않았다면 컴포넌트의 리렌더링을 방지합니다.

3.2 useMemo와 useCallback 사용하기

const MyComponent = ({ data }) => {
  const expensiveResult = useMemo(() => {
    return expensiveComputation(data);
  }, [data]);

  const handleClick = useCallback(() => {
    console.log(data);
  }, [data]);

  return (
    <div>
      <p>{expensiveResult}</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
};

useMemo는 계산 비용이 높은 값을, useCallback은 함수를 메모이제이션합니다.

4. 가상화 (Virtualization) 적용하기

대량의 데이터를 렌더링 할 때는 가상화 기술을 사용하여 성능을 개선할 수 있습니다.

4.1 react-window 사용하기

import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const MyList = () => (
  <List
    height={400}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);

이 방식을 사용하면 화면에 보이는 항목만 렌더링 되어 메모리 사용량과 렌더링 시간이 크게 줄어듭니다.

5. 상태 관리 최적화하기

효율적인 상태 관리는 React 애플리케이션의 성능에 큰 영향을 미칩니다.

5.1 상태 정규화하기

// Before
const state = {
  users: [
    { id: 1, name: 'John', posts: [{ id: 1, title: 'Hello' }] }
  ]
};

// After
const state = {
  users: {
    1: { id: 1, name: 'John', postIds: [1] }
  },
  posts: {
    1: { id: 1, title: 'Hello' }
  }
};

상태를 정규화하면 데이터 업데이트가 더 효율적으로 이루어집니다.

5.2 불변성 유지하기

// Bad
const updateUser = (users, id, updates) => {
  users[id] = { ...users[id], ...updates };
  return users;
};

// Good
const updateUser = (users, id, updates) => {
  return {
    ...users,
    [id]: { ...users[id], ...updates }
  };
};

불변성을 유지하면 React가 상태 변화를 더 쉽게 감지할 수 있습니다.

6. 서버 사이드 렌더링 (SSR) 고려하기

초기 로딩 성능을 개선하기 위해 서버 사이드 렌더링을 고려할 수 있습니다.

6.1 Next.js 사용하기

Next.js는 React 애플리케이션의 SSR을 쉽게 구현할 수 있게 해 줍니다.

// pages/index.js
export async function getServerSideProps() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return { props: { data } };
}

function HomePage({ data }) {
  return <div>{data.title}</div>;
}

export default HomePage;

이 방식을 사용하면 서버에서 초기 데이터를 가져와 렌더링 한 HTML을 클라이언트에 전송합니다.

7. 번들 크기 최적화하기

애플리케이션의 번들 크기를 줄이면 초기 로딩 시간을 단축할 수 있습니다.

7.1 Tree Shaking 활용하기

webpack과 같은 번들러의 Tree Shaking 기능을 활용하여 사용하지 않는 코드를 제거할 수 있습니다.

// webpack.config.js
module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true,
  },
};

7.2 Code Splitting 사용하기

import()를 사용하여 동적으로 모듈을 import 할 수 있습니다.

const MyComponent = () => {
  const [Module, setModule] = useState(null);

  useEffect(() => {
    import('./HeavyModule').then(module => setModule(module.default));
  }, []);

  return Module ? <Module /> : <div>Loading...</div>;
};

8. 이미지 최적화하기

이미지는 웹 성능에 큰 영향을 미치는 요소 중 하나입니다.

8.1 이미지 지연 로딩

import { LazyLoadImage } from 'react-lazy-load-image-component';

const MyImage = ({ src, alt }) => (
  <LazyLoadImage
    src={src}
    alt={alt}
    effect="blur"
  />
);

이 방식을 사용하면 이미지가 뷰포트에 들어올 때만 로드됩니다.

8.2 적절한 이미지 포맷 사용하기

WebP와 같은 현대적인 이미지 포맷을 사용하여 이미지 크기를 줄일 수 있습니다.

const MyImage = ({ src, alt }) => (
  <picture>
    <source srcSet={`${src}.webp`} type="image/webp" />
    <img src={`${src}.jpg`} alt={alt} />
  </picture>
);

9. 성능 모니터링 및 지속적인 최적화

성능 최적화는 일회성 작업이 아닌 지속적인 과정입니다.

9.1 성능 모니터링 도구 사용하기

  • Google Analytics
  • New Relic
  • Sentry

이러한 도구들을 사용하여 실제 사용자의 성능 지표를 지속적으로 모니터링할 수 있습니다.

9.2 성능 예산 설정하기

성능 예산을 설정하고 이를 CI/CD 파이프라인에 통합하여 성능 저하를 사전에 방지할 수 있습니다.

// lighthouserc.js
module.exports = {
  ci: {
    assert: {
      assertions: {
        'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
        'interactive': ['error', { maxNumericValue: 5000 }],
      },
    },
  },
};

React 애플리케이션의 성능 최적화는 복잡하지만 체계적인 접근을 통해 효과적으로 수행할 수 있습니다. 이 워크플로우를 따라가면서 다음 단계들을 수행하세요:

  1. 성능 문제를 정확히 식별합니다.
  2. 코드 분할을 적용하여 초기 로딩 시간을 줄입니다.
  3. 메모이제이션을 활용하여 불필요한 리렌더링을 방지합니다.
  4. 대량의 데이터 렌더링 시 가상화를 적용합니다.
  5. 상태 관리를 최적화하여 효율적인 데이터 흐름을 만듭니다.
  6. 필요한 경우 서버 사이드 렌더링을 고려합니다.
  7. 번들 크기를 최적화하여 로딩 시간을 단축합니다.
  8. 이미지를 최적화하여 리소스 사용을 효율화합니다.
  9. 지속적인 성능 모니터링과 최적화를 수행합니다.

이러한 단계들을 꾸준히 실천하면 React 애플리케이션의 성능을 크게 향상시킬 수 있습니다. 성능 최적화는 사용자 경험을 개선하고 비즈니스 목표 달성에 도움을 주는 중요한 과정임을 기억하세요.

반응형