- 정규 함수 외에 안에 상태를 설정할 수 있는 로직을 포함한 함수를 의미합니다.
- 이는 로직 재사용이 가능한 메커니즘을 만드는 것이며 여러 컴퍼넌트에 뿌려도 로직은 공유하지만 상태를 공유하지는 않습니다.
- 지금까지 어떤 액체를 얼릴 때마다 트레이를 새로 만들었습니다. 하지만 커스텀 훅을 만드는 것은 하나의 얼음 트레이를 만들어 한 컴퍼넌트에선 물을 얼리고 다른 컴퍼넌트에선 오렌지 주스를 얼리는 방식과 비슷합니다.

초당 숫자가 1씩 올라가는 컴퍼넌트와 초당 숫자가 1씩 내려가는 컴퍼넌트를 만들고 생각해 봅시다.
ForwardCounter.js
import { useState, useEffect } from "react";
const ForwardCounter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter((prevCounter) => prevCounter + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>{counter}</div>;
};
export default ForwardCounter;
BackwardCounter.js
import { useState, useEffect } from "react";
const BackwardCounter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter((prevCounter) => prevCounter - 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>{counter}</div>;
};
export default BackwardCounter;
이 둘은 덧셈과 뺄셈 외에는 정확히 동일한 코드이며 이렇게 코드가 중복될 때는 이 코드를 떼어내고 리팩토링을 해야 합니다.
그러기 위해선 공통되는 코드를 갖는 함수를 만들고 각 컴퍼넌트에 뿌려주면 됩니다.
커스텀 훅 만들기
함수 이름 시작에 use가 붙여 리액트에게 이 함수가 커스텀 훅임을 알려주며 이는 리액트가 해당 함수를 훅의 규칙에 따라 사용하겠다고 보장해 주는 것입니다.
즉, 이 커스텀 훅을 내장 훅과 같은 방식으로 쓰겠다는 것 알리는 것입니다.
또, 상태와 관련된 로직을 사용한다던가(useState) 다른 리액트 훅을 사용할 수 있으며(useEffect,...) 이를 통해 컴퍼넌트 간에 특정 로직을 공유할 수 있게 됩니다.
use-counter.js
import { useState, useEffect } from "react";
const useCounter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter((prevCounter) => prevCounter + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
// 커스텀 훅에서는 필요한 무엇이든간에 반환이 가능하다
// return 26, return [], return {}, ...
// 여기서는 counter가 갖고 있는 숫자를 반환한다
return counter;
};
export default useCounter;
이렇게 커스텀 훅을 구성하면
ForwardCounter.js
import useCounter from './use-counter';
const ForwardCounter = () => {
// useCounter를 통해 저장된 숫자를 counter에 저장해줌
const counter = useCounter();
return <div>{counter}</div>;
};
export default ForwardCounter;
숫자가 더해지는 컴퍼넌트의 코드가 이렇게 심플해질 수 있습니다.
하지만 위의 코드는 사실 정적인 틀이었습니다. 모든 얼음 트레이에 물만 부울 수 있는 것입니다.
많이 이상하죠...? 그럼 우리는 유연하게 훅을 구성하려면 어떻게 해야 할까요??
그건 바로 인자를 구성하고 활용해 주면 되는 겁니다.
use-counter.js
import { useState, useEffect } from "react";
const useCounter = (counterUpdateFn) => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter(counterUpdateFn());
}, 1000);
return () => clearInterval(interval);
}, []);
return counter;
};
export default useCounter;
이렇게 인자에 함수 자체를 받아와 연결할 수도 있습니다.
또, 위의 예시와 같은 상황을 해결해 보자면
use-counter.js
import { useState, useEffect } from "react";
const useCounter = (plus = true) => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
if (plus) {
setCounter((prevCounter) => prevCounter + 1);
} else {
setCounter((prevCounter) => prevCounter - 1);
}
}, 1000);
return () => clearInterval(interval);
}, [plus]); // 외부 상태에 의존성이 생겼으므로 추가합니다 !
return counter;
};
export default useCounter;
이렇게 인자를 조건으로 받아 세분화하는 방법도 있습니다.
지금까지는 커스텀 훅의 이해를 돕기 위한 너무나 비실용적인 간단한 예시였습니다.
좀 더 현실적인 상황에서의 커스텀 훅은 다음 블로그에 도전해 보겠습니다...
'REACT' 카테고리의 다른 글
| useContext 훅 (5) | 2023.03.03 |
|---|---|
| useReducer 훅 (5) | 2023.03.02 |
| 커스텀 훅으로 HTTP 요청 리팩토링 (8) | 2023.02.17 |
| 리액트 최적화 테크닉 (7) | 2023.02.14 |
| HTTP 요청 보내기 (8) | 2023.02.13 |