안녕하세요. J4J입니다.
이번 포스팅은 hooks의 useCallback에 대해 적어보는 시간을 가져보려고 합니다.
useCallback이란?
useCallback은 hooks에서 메모이제이션을 이용해 함수를 재사용함으로 써 최적화를 해주는 역할을 담당합니다.
예를 들어 어떤 상황에서든 항상 동일한 결과값을 제공해주는 함수(= 순수 함수)가 있다고 가정해보겠습니다.
리액트를 개발하다보면 빈번하게 리 렌더링의 상황이 발생되는데 이때 항상 동일한 결괏값을 제공해주는 함수들도 함께 리 렌더링이 이루어집니다.
이것은 다른 말로 데이터를 지웠다가 똑같은 상태의 함수를 다시 생성하는 불필요한 작업을 한다고도 말할 수 있습니다.
그리고 이런 작업들이 한 개, 두 개 점점 쌓여나가면 결국 서비스의 속도에 영향을 미칠 수 밖에 없게 됩니다.
이런 상황에서 만약 메모이제이션을 사용하여 함수를 저장해 두고 있다면 어떨까요?
리 렌더링이 발생될 때 제거했다가 다시 처음부터 만들어내는 것이 아니라 저장된 곳에서 이미 만들어져 있는 함수를 가져오기만 한다면 더 빠른 속도로 렌더링이 되는 것을 도울 수 있습니다.
함수 리렌더링에 있어 이런 역할을 해주는 녀석이 바로 useCallback입니다.
여기서 한 가지 알고 가셔야 될 점은 메모이제이션, 캐시 메모리 저장 등을 할 때에도 어느 정도의 작업시간이 소요가 됩니다.
그렇기 때문에 재사용성이 거의 발생하지 않는 곳에도 해당 기술들을 적용하게 된다면 오히려 서비스 속도를 더 늦춰버리는 결과를 초래할 수 있습니다.
하지만 반대로 재사용이 빈번하게 발생되는 곳에 사용하게 된다면 재사용의 횟수가 많아질수록 더욱 큰 장점으로 다가오게 됩니다.
useCallback의 사용방법은 매우 단순합니다.
우선 관련 메서드는 'react'패키지에서 제공해주기 때문에 리액트로 개발되는 곳 어디서든 가져다 사용할 수 있습니다.
vscode에서 제공해주는 useCallback에 대한 정보를 확인해보시면 다음과 같습니다.
function React.useCallback<() => string>(callback: () => string, deps: React.DependencyList): () => string
파라미터는 총 2개를 입력할 수 있습니다.
첫 번째 파라미터인 callback에는 메모이제이션을 적용할 함수를 넣어주시면 됩니다.
두 번째 파라미터인 deps에는 callback에 집어넣은 함수의 리 렌더링이 될 수 있는 의존관계가 될 변수들을 배열의 형태로 집어넣어 주시면 됩니다.
여기서 배열의 요소로 들어가는 데이터들에 변경점이 생길때마다 callback에 해당하는 함수가 리 렌더링이 되고 변경점이 생기지 않거나 또는 빈 배열을 입력하게 될 때는 리 렌더링이 발생되지 않습니다.
리턴 값으로는 문자열이 나옵니다.
해당 문자열을 변수에 저장하여 함수처럼 사용해주시면 됩니다.
useCallback과 관련된 간단한 예시 코드를 작성해보겠습니다.
예시 코드
다음과 같은 코드를 작성해보겠습니다.
- 이름과 나이에 해당하는 값들을 추가적인 문자열과 함께 출력될 수 있는 메서드들을 생성
- 이름과 나이를 각각 변경할 수 있는 텍스트를 생성
- 텍스트에 값을 입력하고 변경버튼을 클릭할 경우 화면에 출력되는 이름과 나잇값을 변경
- 이름을 출력하는 메서드에는 useCallback을 적용
간단하게 App.jsx는 다음과 같이 작성해보겠습니다.
import * as React from 'react';
import MyComponent from './myComponent';
const App = () => {
const [name, setName] = React.useState('');
const [age, setAge] = React.useState(0);
const [nameText, setNameText] = React.useState('');
const [ageText, setAgeText] = React.useState(0);
const onClickButton = () => {
setName(nameText);
setNameText('');
setAge(ageText);
setAgeText(0);
}
return (
<div>
<input type="text" value={nameText} onChange={(e) => setNameText(e.target.value)} />
<input type="number" value={ageText} onChange={(e) => setAgeText(e.target.value)} />
<button onClick={onClickButton}>변경</button>
<MyComponent name={name} age={age} />
</div>
)
}
export default App;
그리고 동일 위치에 myComponent.jsx파일을 생성하여 다음과 같이 작성해보겠습니다.
import * as React from 'react';
const myComponent = ({ name, age }) => {
const getName = React.useCallback(() => {
console.log('getName');
return `my name is ${name}`;
}, [])
const getAge = () => {
console.log('getAge');
return `my age is ${age}`;
}
return (
<div>
<h3>{getName()}</h3>
<h5>{getAge()}</h5>
</div>
)
}
export default myComponent;
위와 같이 작성하고 실행해보면 다음과 같은 결과가 나옵니다.
텍스트에 데이터를 입력하는 상황에 대해 설명해보면 다음과 같습니다.
- useState를 이용해 만들어놓은 nameText, ageText의 값이 변경될 때마다 관련된 useState의 값들이 모두 리 렌더링이 되기 때문에 name, age도 리 렌더링이 됩니다.
- name, age가 리렌더링이 되면 해당 데이터들을 파라미터로 사용하는 MyComponent도 리 렌더링이 됩니다.
- MyComponent가 리렌더링이 되면 getAge는 새롭게 만들어지지만 getName은 메모이제이션이 된 데이터를 가져와 사용합니다.
- MyComponent가 다시 그려질때마다 div태그 내부에서 getName과 getAge를 호출하기 때문에 결과적으로 텍스트가 입력될 때마다 콘솔 값이 출력됩니다.
버튼을 클릭하는 상황에 대해 설명해보면 다음과 같습니다.
- name, age의 값들이 변경되기 때문에 위에서 텍스트를 입력하는 상황과 동일한 상황이 발생됩니다.
- getAge함수는 MyComponent가 리 렌더링 될 때마다 다시 생성되기 때문에 변경된 age값을 리턴해줍니다.
- getName함수는 MyComponent가 리렌더링이 되더라도 useCallback의 deps파라미터에 빈 배열이 들어가 있기 때문에 메모이제이션 처리가 되어 다시 생성되지 않고 저장된 데이터를 가져와 사용합니다.
- 결국 getAge는 반응성을 나타내지만 getName은 초기에 만들어진 함수를 그대로 가져와 사용하기 때문에 name값이 변경되더라도 name이 항상 빈 문자열인 값으로 출력됩니다.
이상으로 hooks의 useCallback에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'SPA > React' 카테고리의 다른 글
[React] Hooks - useReducer (0) | 2021.09.27 |
---|---|
[React] Hooks - useMemo (0) | 2021.09.22 |
[React] Hooks - useRef (0) | 2021.09.13 |
[React] Hooks - useState (0) | 2021.09.05 |
[React] Hooks - useEffect (0) | 2021.09.02 |
댓글