React 앱의 성능 개선 및 최적화 방법

게시 됨: 2022-03-10
요약 요약 ↬ React가 도입된 이후로 프론트엔드 개발자가 웹 애플리케이션을 구축하는 방식을 변화시켰으며, 가상 DOM은 구성 요소를 효과적으로 렌더링하는 것으로 유명합니다. 이 튜토리얼에서는 React 애플리케이션에서 성능을 최적화하는 다양한 방법과 성능 향상에 사용할 수 있는 React의 기능에 대해 설명합니다.

React를 사용하면 웹 애플리케이션이 UI(사용자 인터페이스)를 빠르게 업데이트할 수 있지만 그렇다고 해서 중간 또는 대규모 React 애플리케이션이 효율적으로 수행되는 것은 아닙니다. 그 성능은 빌드할 때 React를 사용하는 방법과 React가 작동하는 방식과 구성 요소가 수명 주기의 다양한 단계를 거치는 프로세스에 대한 이해에 따라 달라집니다. React는 웹 앱에 많은 성능 향상을 제공하며 다양한 기술, 기능 및 도구를 통해 이러한 향상을 달성할 수 있습니다.

이 튜토리얼에서는 React 애플리케이션에서 성능을 최적화하는 다양한 방법과 성능 향상에 사용할 수 있는 React의 기능에 대해 설명합니다.

React 애플리케이션에서 성능 최적화를 어디서 시작해야 할까요?

최적화 시기와 위치를 정확히 알지 못하면 앱 최적화를 시작할 수 없습니다. "어디부터 시작할까요?"라고 물을 수 있습니다.

초기 렌더링 프로세스 동안 React는 구성 요소의 DOM 트리를 빌드합니다. 따라서 DOM 트리에서 데이터가 변경되면 React가 변경의 영향을 받은 구성 요소만 다시 렌더링하고 영향을 받지 않은 트리의 다른 구성 요소는 건너뛰기를 원합니다.

그러나 React는 DOM 트리의 모든 구성 요소가 영향을 받는 것은 아니지만 결국 모든 구성 요소를 다시 렌더링할 수 있습니다. 이로 인해 로딩 시간이 길어지고 시간이 낭비되며 CPU 리소스도 낭비됩니다. 이런 일이 일어나지 않도록 해야 합니다. 그래서 여기에서 최적화 노력을 집중할 것입니다.

이 상황에서 우리는 리소스와 시간 낭비를 피하기 위해 필요할 때만 렌더링하거나 비교하도록 모든 구성 요소를 구성할 수 있습니다.

점프 후 더! 아래에서 계속 읽기 ↓

성능 측정

느낌에 따라 React 애플리케이션의 최적화 프로세스를 시작하지 마십시오. 대신, 사용 가능한 측정 도구를 사용하여 React 앱의 성능을 분석하고 속도를 늦추는 원인에 대한 자세한 보고서를 얻으십시오.

Chrome의 성능 탭으로 React 구성 요소 분석

React의 문서에 따르면 아직 개발 모드에 있는 동안 Chrome 브라우저의 "성능" 탭을 사용하여 React 구성 요소가 어떻게 마운트, 업데이트 및 마운트 해제되는지 시각화할 수 있습니다. 예를 들어 아래 이미지는 개발 모드에서 내 블로그를 프로파일링하고 분석하는 Chrome의 "성능" 탭을 보여줍니다.

성능 프로파일러 요약
성능 프로파일러 요약(큰 미리보기)

이렇게 하려면 다음 단계를 따르세요.

  1. 모든 확장, 특히 React Developer Tools를 일시적으로 비활성화하십시오. 분석 결과를 망칠 수 있기 때문입니다. 브라우저를 시크릿 모드로 실행하여 확장 프로그램을 쉽게 비활성화할 수 있습니다.
  2. 애플리케이션이 개발 모드에서 실행 중인지 확인하십시오. 즉, 애플리케이션이 로컬 호스트에서 실행 중이어야 합니다.
  3. Chrome의 개발자 도구를 열고 "성능" 탭을 클릭한 다음 "기록" 버튼을 클릭합니다.
  4. 프로파일링하려는 작업을 수행합니다. 20초 이상 녹화하지 마십시오. 그렇지 않으면 Chrome이 멈출 수 있습니다.
  5. 녹음을 중지합니다.
  6. 반응 이벤트는 "사용자 타이밍" 레이블 아래에 그룹화됩니다.

프로파일러의 숫자는 상대적입니다. 대부분의 시간과 구성 요소는 프로덕션에서 더 빠르게 렌더링됩니다. 그럼에도 불구하고 이것은 UI가 실수로 업데이트되는 시기와 UI 업데이트가 얼마나 깊이 있고 얼마나 자주 발생하는지 파악하는 데 도움이 됩니다.

React 개발자 도구 프로파일러

React 문서에 따르면 react-dom 16.5+ 및 react-native 0.57+에서는 React Developer Tools Profiler를 사용하여 개발자 모드에서 향상된 프로파일링 기능을 사용할 수 있습니다. 프로파일러는 React 애플리케이션의 성능 병목 현상을 식별하기 위해 React의 실험적인 Profiler API를 사용하여 렌더링되는 각 구성 요소에 대한 타이밍 정보를 수집합니다.

브라우저용 React 개발자 도구를 다운로드하기만 하면 함께 제공되는 프로파일러 도구를 사용할 수 있습니다. 프로파일러는 개발 모드 또는 React v16.5+의 프로덕션 프로파일링 빌드에서만 사용할 수 있습니다. 아래 이미지는 React Developer Tools Profiler를 사용하여 개발 모드에 있는 내 블로그의 프로파일러 요약입니다.

React 개발자 도구 프로파일러 화염 그래프
React 개발자 도구 프로파일러 화염 그래프(큰 미리보기)

이를 달성하려면 다음 단계를 따르십시오.

  1. React 개발자 도구를 다운로드합니다.
  2. React 애플리케이션이 개발 모드 또는 React v16.5+의 프로덕션 프로파일링 빌드에 있는지 확인하세요.
  3. Chrome의 '개발자 도구' 탭을 엽니다. React Developer Tools에서 제공하는 "Profiler"라는 새 탭을 사용할 수 있습니다.
  4. "기록" 버튼을 클릭하고 프로파일링하려는 작업을 수행합니다. 이상적으로는 프로파일링하려는 작업을 수행한 후 기록을 중지하십시오.
  5. 그래프(flamegraph라고 함)가 React 앱의 모든 이벤트 핸들러 및 구성 요소와 함께 나타납니다.

참고 : 자세한 내용은 설명서를 참조하십시오.

React.memo React.memo() 로 메모 작성하기

React v16은 추가 API, React.memo() 라는 고차 구성 요소와 함께 출시되었습니다. 문서에 따르면 이것은 성능 최적화 로만 존재합니다.

메모( memo )라는 이름은 메모이제이션(memoization)에서 따온 것으로, 기본적으로 비싼 함수 호출의 결과를 저장하고 같은 비싼 함수 가 다시 호출될 때마다 저장된 결과를 반환하여 코드 속도를 높이는 데 주로 사용되는 최적화 형태입니다.

메모이제이션은 일반적으로 순수 함수를 한 번 실행한 다음 그 결과를 메모리에 저장하는 기술입니다. 이전과 동일한 인수를 사용하여 해당 함수를 다시 실행하려고 하면 함수를 다시 실행하지 않고 첫 번째 함수 실행에서 이전에 저장된 결과만 반환합니다.

위의 설명을 React 생태계에 매핑하면 언급된 기능은 React 구성 요소이고 인수는 props입니다.

React.memo() 를 사용하여 선언된 구성 요소의 기본 동작은 구성 요소의 props가 변경된 경우에만 렌더링된다는 것입니다. 이를 확인하기 위해 props의 얕은 비교를 수행하지만 이를 재정의하는 옵션을 사용할 수 있습니다.

React.memo() 는 props가 변경되지 않았거나 다시 렌더링이 필요하지 않은 구성 요소를 다시 렌더링하지 않도록 하여 React 앱의 성능을 향상시킵니다.

아래 코드는 React.memo() 의 기본 구문입니다.

 const MemoizedComponent = React.memo((props) => { // Component code goes in here })

React.memo() 를 사용하는 경우

  • 순수 기능성 성분
    구성 요소가 작동하고 동일한 소품이 제공되며 항상 동일한 출력을 렌더링하는 경우 React.memo() 를 사용할 수 있습니다. React 후크가 있는 비순수 기능 구성 요소에서 React.memo() 를 사용할 수도 있습니다.
  • 구성 요소는 자주 렌더링됩니다.
    React.memo() 를 사용하여 자주 렌더링되는 구성 요소를 래핑할 수 있습니다.
  • 구성 요소는 동일한 소품으로 다시 렌더링됩니다.
    React.memo() 를 사용하여 re-rendering 동안 동일한 props와 함께 일반적으로 제공되는 구성 요소를 래핑합니다.
  • 중간에서 높은 요소
    props가 동일한지 확인하기 위해 중간에서 많은 수의 UI 요소를 포함하는 구성 요소에 사용합니다.

참고 : props를 콜백으로 사용하는 구성 요소를 메모할 때 주의하십시오. 렌더링 간에 동일한 콜백 함수 인스턴스를 사용해야 합니다. 이는 부모 구성 요소가 렌더링할 때마다 콜백 함수의 다른 인스턴스를 제공할 수 있기 때문에 메모 작성 프로세스가 중단될 수 있습니다. 이 문제를 해결하려면 메모된 구성 요소가 항상 동일한 콜백 인스턴스를 수신하는지 확인하세요.

실제 상황에서 메모이제이션을 사용하는 방법을 살펴보겠습니다. "Photo"라고 하는 아래의 기능 구성 요소는 React.memo() 를 사용하여 재렌더링을 방지합니다.

 export function Photo({ title, views }) { return ( <div> <div>Photo title: {title}</div> <div>Location: {location}</div> </div> ); } // memoize the component export const MemoizedPhoto = React.memo(Photo);

위의 코드는 사진 제목과 사진에서 피사체의 위치가 포함된 div를 표시하는 기능적 구성 요소로 구성됩니다. 우리는 또한 새로운 함수를 만들고 그것을 MemoizedPhoto 라고 불러서 컴포넌트를 메모화하고 있습니다. 사진 구성 요소를 메모하면 props, titlelocation 가 후속 렌더링에서 동일한 한 구성 요소가 다시 렌더링되는 것을 방지할 수 있습니다.

 // On first render, React calls MemoizedPhoto function. <MemoizedPhoto title="Effiel Tower" location="Paris" /> // On next render, React does not call MemoizedPhoto function, // preventing rendering <MemoizedPhoto title="Effiel Tower" location="Paris" />

여기서 React는 메모이징된 함수를 한 번만 호출합니다. 소품이 동일하게 유지되는 한 다음 호출에서 구성 요소를 렌더링하지 않습니다.

번들링 및 축소

React 단일 페이지 애플리케이션에서는 모든 JavaScript 코드를 단일 파일로 묶고 축소할 수 있습니다. 우리의 응용 프로그램이 상대적으로 작은 한 괜찮습니다.

React 애플리케이션이 성장함에 따라 모든 JavaScript 코드를 단일 파일로 묶고 축소하는 것은 문제가 되고 이해하기 어렵고 지루해집니다. 큰 JavaScript 파일을 브라우저로 보내기 때문에 React 앱의 성능과 로딩 시간에도 영향을 미칩니다. 따라서 코드 기반을 다양한 파일로 분할하고 필요에 따라 간격을 두고 브라우저에 전달하는 데 도움이 되는 프로세스가 필요합니다.

이와 같은 상황에서 Webpack과 같은 일종의 자산 번들러를 사용한 다음 코드 분할 기능을 활용하여 애플리케이션을 여러 파일로 분할할 수 있습니다.

코드 분할은 애플리케이션의 로딩 시간을 개선하기 위한 수단으로 Webpack의 문서에 제안되어 있습니다. 또한 성능을 극적으로 향상시킬 수 있는 지연 로딩(현재 사용자가 필요로 하는 것만 제공)에 대한 React의 문서에서 제안합니다.

Webpack은 코드 분할에 대한 세 가지 일반적인 접근 방식을 제안합니다.

  • 진입점
    항목 구성을 사용하여 수동으로 코드를 분할합니다.
  • 중복방지
    중복을 제거하고 청크를 분할하려면 SplitChunksPlugin 을 사용하십시오.
  • 동적 가져오기
    모듈 내에서 인라인 함수 호출을 통해 코드를 분할합니다.

코드 분할의 이점

  • 코드 분할은 브라우저의 캐시 리소스와 자주 변경되지 않는 코드를 지원합니다.
  • 또한 브라우저가 리소스를 병렬로 다운로드하는 데 도움이 되므로 애플리케이션의 전체 로딩 시간이 단축됩니다.
  • 이를 통해 요청 시 또는 애플리케이션에서 필요에 따라 로드될 청크로 코드를 분할할 수 있습니다.
  • 첫 번째 렌더링에서 리소스의 초기 다운로드를 비교적 작게 유지하여 앱의 로딩 시간을 줄입니다.
번들링 및 축소 프로세스
번들링 및 축소 프로세스(큰 미리보기)

불변 데이터 구조

React의 문서에서는 데이터를 변경하지 않는 것의 힘에 대해 설명합니다. 변경할 수 없는 데이터는 변경할 수 없습니다. 불변성은 React 프로그래머가 이해해야 하는 개념입니다.

변경할 수 없는 값이나 개체는 변경할 수 없습니다. 따라서 업데이트가 있을 때 메모리에 새 값이 생성되고 이전 값은 그대로 유지됩니다.

불변 데이터 구조와 React.PureComponent 를 사용하여 복잡한 상태 변경을 자동으로 확인할 수 있습니다. 예를 들어 애플리케이션의 상태를 변경할 수 없는 경우 Redux와 같은 상태 관리 라이브러리를 사용하여 모든 상태 개체를 단일 저장소에 실제로 저장할 수 있으므로 실행 취소 및 다시 실행 기능을 쉽게 구현할 수 있습니다.

변경할 수 없는 데이터는 일단 생성되면 변경할 수 없음을 잊지 마십시오.

불변 데이터 구조의 이점

  • 부작용이 없습니다.
  • 불변 데이터 객체는 생성, 테스트 및 사용이 쉽습니다.
  • 데이터를 반복해서 확인할 필요 없이 상태의 업데이트를 빠르게 확인하는 데 사용할 수 있는 논리를 작성하는 데 도움이 됩니다.
  • 시간적 결합(코드가 실행 순서에 따라 달라지는 결합 유형)을 방지하는 데 도움이 됩니다.

다음 라이브러리는 변경할 수 없는 데이터 구조 세트를 제공하는 데 도움이 됩니다.

  • 불변성 도우미
    소스를 변경하지 않고 데이터 복사본을 변경합니다.
  • 불변.js
    JavaScript용 불변 영구 데이터 컬렉션은 효율성과 단순성을 높입니다.
  • 이음매 없는 불변
    JavaScript의 불변 데이터 구조는 일반 JavaScript 배열 및 객체와 역호환됩니다.
  • 반응 복사 쓰기
    이것은 변경 가능한 API로 변경할 수 없는 상태를 제공합니다.

기타 성능 개선 방법

배포 전에 프로덕션 빌드 사용

React의 문서에서는 앱을 배포할 때 축소된 프로덕션 빌드를 사용할 것을 제안합니다.

React 개발자 도구의 "프로덕션 빌드" 경고
React 개발자 도구의 "프로덕션 빌드" 경고(큰 미리보기)

익명 기능 피하기

익명 함수에는 식별자가 할당되지 않기 때문에( const/let/var 를 통해) 구성 요소가 불가피하게 다시 렌더링될 때마다 영구적이지 않습니다. 이로 인해 JavaScript는 명명된 함수를 사용할 때처럼 단일 메모리를 한 번만 할당하는 대신 이 구성 요소를 다시 렌더링할 때마다 새 메모리를 할당합니다.

 import React from 'react'; // Don't do this. class Dont extends Component { render() { return ( <button onClick={() => console.log('Do not do this')}> Don't </button> ); } } // The better way class Do extends Component { handleClick = () => { console.log('This is OK'); } render() { return ( <button onClick={this.handleClick}> Do </button> ); } }

위의 코드는 버튼이 클릭 시 작업을 수행하도록 하는 두 가지 방법을 보여줍니다. 첫 번째 코드 블록은 onClick() 소품의 익명 함수를 사용하며 이는 성능에 영향을 미칩니다. 두 번째 코드 블록은 이 시나리오에서 올바른 방법인 onClick() 함수에서 명명된 함수를 사용합니다.

구성 요소를 장착 및 분리하는 데 비용이 많이 드는 경우가 많습니다.

컴포넌트를 사라지게 하기 위해(즉, 마운트 해제하기 위해) 조건부 또는 테너리를 사용하는 것은 바람직하지 않습니다. 사라지게 만든 컴포넌트는 브라우저가 다시 칠하고 리플로우하게 하기 때문입니다. 문서에 있는 HTML 요소의 위치와 기하학을 다시 계산해야 하기 때문에 이것은 비용이 많이 드는 프로세스입니다. 대신 CSS의 opacityvisibility 속성을 사용하여 구성 요소를 숨길 수 있습니다. 이런 식으로 구성 요소는 여전히 DOM에 있지만 성능 비용 없이 보이지 않습니다.

긴 목록 가상화

설명서에 따르면 많은 양의 데이터가 포함된 목록을 렌더링하는 경우 표시되는 뷰포트 내에서 한 번에 목록의 데이터 중 작은 부분을 렌더링해야 합니다. 그런 다음 목록을 스크롤할 때 더 많은 데이터를 렌더링할 수 있습니다. 따라서 데이터는 뷰포트에 있을 때만 표시됩니다. 이 프로세스를 "윈도우잉"이라고 합니다. 윈도우에서 행의 작은 부분집합은 주어진 시간에 렌더링됩니다. 이 작업을 수행하는 데 널리 사용되는 라이브러리가 있으며 그 중 두 가지는 Brian Vaughn이 유지 관리합니다.

  • 반응 창
  • 반응 가상화

결론

React 애플리케이션의 성능을 향상시키는 몇 가지 다른 방법이 있습니다. 이 기사에서는 성능 최적화의 가장 중요하고 효과적인 방법에 대해 논의했습니다.

이 튜토리얼을 재미있게 읽으셨기를 바랍니다. 아래 나열된 리소스를 통해 자세히 알아볼 수 있습니다. 질문이 있는 경우 아래 댓글 섹션에 남겨주세요. 하나하나 답변해 드리겠습니다.

참조 및 관련 리소스

  • "성능 최적화", React Docs
  • "React.memo를 현명하게 사용하십시오", Dmitri Pavlutin
  • "React의 성능 최적화 기술", Niteesh Yadav
  • "React의 불변성: 개체를 변경하는 데 아무런 문제가 없습니다", Esteban Herrera
  • "React 앱의 성능을 최적화하는 10가지 방법", Chidume Nnamdi
  • "React 앱의 성능을 향상시키는 5가지 팁", William Le