일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- JPA
- hook
- Chakra
- designpatterns
- css
- 오블완
- Gin
- 웹애플리케이션서버
- 티스토리챌린지
- springboot
- Redux
- component
- satisfiles
- tanstackquery
- react-hook-form
- Spring
- frontend
- test
- ReactHooks
- JavaSpring
- RTK
- go
- javascript
- java
- typescript
- backend
- storybook
- golang
- React
- Today
- Total
bkdragon's log
[React] 성능 최적화 본문
우선 React 성능 최적화를 하는 방법을 알기 전에 라이프 사이클과 리렌더링이 발생하는 타이밍에 대해 먼저 알아보자.
컴포넌트의 라이프 사이클
컴포넌트 라이프 사이클은 크게 3가지 단계로 구분할 수 있다. Mountion(생성) , Updating(업데이트), UmMounting(제거)
위의 이미지는 클래스형 컴포넌트에서 라이프 사이클 메서드들이 나와있는데 함수형 컴포넌트에선 훅이 그 역할을 대체한다.
분류 | 클래스형 컴포넌트 | 함수형 컴포넌트 |
---|---|---|
Mounting | constructor() | 함수형 컴포넌트 내부 |
Mounting | render() | return() |
Mounting | ComponenDidMount() | useEffect() |
Updating | componentDidUpdate() | useEffect() |
UnMounting | componentWillUnmount() | useEffect() |
- constructor : 생성자. 초기 state를 설정할 수 있다.
- getDerivedStateFromProps : props로 부터 파생된 state를 가져온다. props로 받아온 것을 state에 넣어주고 싶을 때 사용한다.
- componentDidMount : 컴포넌트의 첫번째 렌더링을 마친 후 실행된다.
- shouldComponentUpdate : 컴포넌트를 리렌더링 할지 말지를 결정한다. true, false를 반환한다.
- componentDidUpdate : 컴포넌트가 업데이트 된 후 발생한다. ( = 리렌더링 완료 후 실행된다.
- componentWillUnmount : 컴포넌트가 화면에서 사라지기 직전에 호출된다
컴포넌트 리렌더링
- 컴포넌트 내부의 상태가 변경될 때
- 상태를 변경하는 setState 함수는 비동기로 구동된다. 동기적인 동작을 보장하지 않는다.
- setState 실행 후 즉시 값이 변경되는 것이 아닌 pending된 형태로 동작한다.
- setState 실행 후 shouldComponentUpdate가 호출된다.
- 부모 컴포넌트가 리렌더링 될 때
- 자신이 전달 받은 Props가 변경될 때
forceUpdate
함수가 실행될 때
forceUpdate
state나 props가 아닌 다른 data를 리렌더링의 트리거로 삼을 수 있는 함수이다. 리액트 공식문서에서 사용하길 권하진 않는다.
라이프 사이클과 리렌더링이 되는 타이밍에 대해 알아보았다. 컴포넌의 성능은 리렌더링이 자주 일어나면 저하될 수 있다. 리렌더링이 될 때 컴포넌트 내부의 변수나 함수가 새롭게 그려진다. 예시 코드를 보자.
import React, { useState } from "react"
const containerPage = () => {
console.log("렌더링 됩니다.")
let countLet = 0
const [countState,setCountState] = useState(0)
const onClickCountLet = ()=>{
console.log(countLet+1)
countLet += 1
}
const onClickCountState = ()=>{
console.log(countState+1)
setCountState(countState+1)
}
return(
<div>
<div>================</div>
<h1>이것은 컨테이너 입니다.</h1>
<div> 카운트(let): {countLet} </div>
<button onClick={onClickCountLet}> 카운트(let) +1 올리기! </button>
<div> 카운트(state): {countLet} </div>
<button onClick={onClickCountState}> 카운트(state) +1 올리기! </button>
<div>================</div>
</div>
)
}
export default containerPage
처음에 이 페이지가 화면에 렌더링이 되면 ‘렌더링이 됩니다’ 메시지가 출력되고 변수와 함수가 새롭게 만들어질 것이다.
let으로 선언한 변수를 올리는 함수를 사용해도 컴포넌트의 리렌더링이 발생하진 않지만 값은 제대로 올라가고 출력되는 것을 확인할 수 있다.
이제 state를 올리는 함수를 사용하면 정상적으로 올라가고 let으로 선언한 변수는 0이 되고 ‘렌더링이 됩니다’ 메시지도 출력이 된다.
이 말은 즉슨 리렌더링이 발생하는 순간 state를 빼고 전부 초기화가 되었다는 것이다. 만약 변경될 일이 적고 복잡한 계산을 요하는 변수나 함수가 있었다면 초기화 되는 것 만으로도 성능의 저하를 야기할 수 있다.
리액트에서 불필요한 렌더링을 막을 수 있는 hook을 제공한다.
useMemo
useMemo 훅은 불필요한 리렌더링이나 재계산을 피하기 위해 비용이 많이 드는 계산이나 함수의 결과를 메모이제이션하는데 사용된다.
컴포넌트가 리렌더링 되더라도 종속석 배열의 값이 편하지 않는 이상 다시 계산되지 않고 캐시된 값을 유지한다.
useCallback
useMemo가 계산된 값을 메모이제이션 했다면 useCallback은 함수 자체를 메모이제이션한다.
React.memo
memo는 컴포넌트 자체를 메모이제이션하는 고차 컴포넌트이다. props가 변경되지 않는 경우 컴포넌트를 리렌더링하지 않는다.
사용예시
예를 들어 api 통신을 통해 받아온 데이터가 있다. 이 데이터를 별도의 가공 처리를 해서 화면에 보여줘야할 때 useMemo를 , 데이터를 가공하는 과정은 useCallback을 사용할 수 있을 것이다.
import React, { useState, useEffect, useMemo } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
// api 호출 함수
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
useEffect(() => {
fetchData().then(response => {
setData(response);
});
}, []);
const processData = useCallback(() => {
return data.map(item => ({
id : item.id,
name : item.name.toUpperCase()
}))
}, [data])
const processedData = useMemo(() => {
if (data) {
// 데이터에 별도의 프로세싱을 처리하는 과정
return processData(data);
}
return null;
}, [data]);
return (
<div>
{processedData && (
<ul>
{processedData.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)}
</div>
);
}
memo의 경우 부모컴포넌에서 여러개의 자식 컴포넌트를 사용할 때 효과적일 것이다. 예를 들어 이미지 데이터를 부모로 부터 받아 보여주는 컴포넌트가 있고 유저 정보를 부모로 부터 받아 보여주는 컴포넌트가 있다고 했을 때 자식 컴포넌트에 memo를 적용해주면 유저 정보가 바뀌었을 때 이미지 컴포넌트가 리렌더링이 되거나 이미지 정보가 바뀌었을 때 유저 컴포넌트가 리렌더링되는 것을 막을 수 있을 것이다.
useMemo, useCallback, memo는 성능 최적화에 도움을 주지만 남발하면 오히려 악영향을 미칠 수 있다. 메모이제이션은 메모리를 소비하기 때문이다. 의존성 배열에 넣을 인자가 많아지면 사용할지 고려해보아야한다.
'React' 카테고리의 다른 글
React 이벤트 위임 (0) | 2023.07.30 |
---|---|
클로저와 useState (0) | 2023.07.10 |
[React] Virtual DOM (0) | 2023.06.25 |
[React Query] Mutation 이후 캐시 데이터 직접 변경하기 (0) | 2023.06.24 |
[React-hook-form] register, handleSubmit 살짝 파헤치기 (0) | 2023.06.12 |