일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- CSS
- 백준 1449 nodejs
- 백준 1449
- 백준 4796 자바스크립트
- JavaScript
- 백준 1449 javascript
- 백준 10448 javascript
- 백준 1018 typescript
- 백준 2503 javascript
- 백준 1018 자바스크립트
- 백준 4796 캠핑
- 백준 4796 타입스크립트
- 알고리즘
- 백준 2503 타입스크립트
- 백준 11047 typescript
- 백준 1449 자바스크립트
- 백준 11047 nodejs
- 백준 1018 javascript
- 백준 1449 타입스크립트
- 백준 2503 자바스크립트
- 백준 11047 javascript
- 백준 1018 타입스크립트
- 백준 4796 nodejs
- 백준 2503 nodejs
- 백준 2503 typescript
- 백준 4796 javascript
- 백준 1018 nodejs
- 백준 11047 자바스크립트
- 백준 11047 타입스크립트
- 백준 1449 노드
- Today
- Total
POTATO THAT WANT TO BE HUMAN
[React]useCallback 과 useMemo 사용하기 본문
리액트에서 렌더링 성능 최적화를 위해 사용되는 hook 중에는 useCallback
과 useMemo
가 있다.
이에 대해 자세히 알아보기 전에 우선 리액트의 렌더링과 메모이제이션에 대해 알아보자.
🔍 리액트의 렌더링
리액트에서 컴포넌트를 리렌더링하게 되는 조건에는 3가지가 있다.
- 자신의 state가 변경될 때
- 부모 컴포넌트로부터 전달받은 props가 변경될 때
- 부모 컴포넌트가 리렌더링 될 때
컴포넌트가 렌더링된다는 것은 누군가가 그 컴포넌트를 호출해 실행되는 것을 의미한다.
컴포넌트가 실행될 때마다 내부에 선언되어 있던 변수나 함수 등의 표현식도 매번 다시 선언되어 사용된다.
많이 사용되는 함수형 컴포넌트는 렌더링
→ Component 함수 호출
→ 모든 내부 변수 초기화
의 순서를 거친다. 컴포넌트가 렌더링 될 때마다 초기화되는 변수나 함수가 무거운 일을 하는 함수라면 굉장히 비효율적일 것이다. 이를 막기 위해 메모이제이션을 사용한다.
🔍 메모이제이션
메모이제이션이란 연산의 결괏값을 메모리에 저장해두고 이전 값과 결과가 동일할 때 재사용하는 것을 말한다.
리액트에서 제공하는 메모이제이션에는 React.memo(컴포넌트)
, useCallback(( ) => { ··· }, [ ])
, useMemo(( ) => { ··· }, [ ])
가 있다. 그렇다면 이 메소드들은 어떤 식으로 메모이제이션을 하고 있을까?
React.memo()
는 props의 값으로 변경을 확인하고
useCallback()
과 useMemo()
는 dependency 배열 내부의 값으로 변경사항을 확인한다.
그럼 이제 본격적으로 useCallback
과 useMemo
에 대해 살펴보자.
🔍 useCallback
useCallback
은 함수 자체를 메모이제이션 한다.
자바스크립트에서 함수는 객체이다. 때문에 리렌더링이 일어날 때마다 새로운 함수가 생성된다.
위에서 살펴본 컴포넌트의 리렌더링 조건 3가지 중 한 가지라도 발생한다면, 해당 컴포넌트는 리렌더링이 되고 컴포넌트 내부에 선언되어 있던 변수나 함수 또한 재선언된다. 이는 전체적인 성능에도 영향을 끼치기 때문에 useCallback
을 사용해 자식 컴포넌트의 불필요한 리렌더링을 막아줄 수 있다.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b])
useCallback
의 기본적인 형식이다.
첫 번째 인자로는 콜백 함수를 받는다.
두 번째 인자로는 dependencies
를 받는다. dependencies
는 의존성 배열로, 작성한 값이 변경되었을 때에만 함수가 실행된다. 다른말로, 이 의존성 배열의 값이 변경되지 않는다면 이전에 생성한 함수의 참조 값을 반환해준다.
주의할 점으로는 의존성 값의 배열이 콜백에 인자로 전달되지는 않는다는 점이다. 하지만 개념적으로 의존성 값의 배열은 콜백 함수가 무엇일지를 표현하는 방법이기 때문에, 콜백 안에서 참조되는 모든 값은 의존성 값의 배열에 나타나야 한다.
예시를 통해 살펴보자.
const [show, setShow] = useState(true);
const showHandler = useCallback( () => {
console.log(show);
setShow(!show);
}, [ ] )
위 함수는
🔍 useMemo
useMemo
는 함수의 리턴 값을 메모이제이션 한다.
useMemo
는 처음에 계산된 결괏값을 메모리에 저장하여 컴포넌트가 반복적으로 렌더링 되어도 계산될 함수를 호출하지 않고 이전에 계산된 결과 값을 메모리에서 꺼내와서 재사용할 수 있게 한다.
const memoizedValue = useMemo(() => computeExpensiveValue(a,b), [a, b]);
useMemo
의 기본적인 형식이다.
첫 번째 인자로는 콜백 함수를 받는다.
두 번째 인자로는 dependencies
를 받는다. 이 dependencies
에도 마찬가지로 의존성 배열이 들어간다. 의존성 배열의 값이 업데이트될 때만 콜백함수를 다시 호출하여 메모이제이션된 값을 업데이트하여 다시 메모이제이션을 해준다. 다른 말로, useCallback
과 마찬가지로 의존성 배열의 값이 변경되지 않는다면 메모이제이션 된 값을 사용한다.
(만약 빈 배열을 넘겨주면 맨 처음 컴포넌트가 마운트되었을 때만 값을 계산하고 이후에는 항상 메모이제이션된 값을 꺼내와 사용한다.)
🔍 언제 사용해야하는가?
렌더링 최적화를 시키기 위해서 무분별적으로 useCallback
과 useMemo
를 사용하는 것은 옳지않다.
모든 추상화 및 최적화 코드에는 비용이 들기 마련인데, 이때 발생하는 비용을 상쇄시킬만한 비용절약이 있지 않으면 오히려 비용이 증가하기 때문이다. 최적화 관점에서 useCallback
과 useMemo
를 사용하기 위해서는 전후 성능을 비교 후에 사용하는 것이 옳다.
간략하게 살펴보자면 다음과 같다.
- 리렌더링이 자주 일어나지 않는다면 굳이 사용할 필요가 없다. 오히려 메모리에 불필요하게 남아있을 뿐이다.
- props나 state가 변경되는 경우가 대부분일 경우, 굳이 비교작업이 계속될 필요가 없기 때문에 사용하지 않는다.
이에 대해 자세히 설명한 블로그 글의 주소이다.
https://www.zigae.com/react-memo/
리액트 useCallback, useMemo 언제 사용 할까?
본글은 useCallback, useMemo에 대해 설명하는 글이 아님을 알린다.
www.zigae.com
https://yceffort.kr/2022/04/best-practice-useCallback-useMemo
Home
yceffort
yceffort.kr
'FRONTEND > React' 카테고리의 다른 글
[React] 반복문에서 setState? (0) | 2023.03.09 |
---|---|
[React] LiftCycle과 useEffect (0) | 2022.11.04 |
[React] 리액트에서 쿼리스트링 값 가져오기 (useSearchParams) (0) | 2022.07.17 |
[React] child에서 parent의 함수로 event handler 등록하기 (0) | 2022.03.30 |
[React] 리스트와 key (0) | 2022.02.23 |