React.useEffect ์ dependencies Array
๐ ์๋ก
React๋ฅผ ์ฌ์ฉํ๋ค ๋ณด๋ฉด ํน์ ์ปดํฌ๋ํธ๊ฐ ํ๋ฉด์ ๊ทธ๋ ค์ง๋ API๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋ฑ ํน์ํ ๋ก์ง์ ์ํํด์ผํ ๋๊ฐ ์์ต๋๋ค. ์ด๋ด๋ ์ฐ๋ฆฌ๋ ์ผ๋ฐ์ ์ผ๋ก useEffect๋ฅผ ๋ง์ด ํ์ฉํ๊ฒ ๋ฉ๋๋ค.
์ด๋ ๋ฏ useEffect๋ ํน์ ํ ์ปดํฌ๋ํธ๊ฐ ๋๋๋ ๋(๊ทธ๋ ค์ง ๋) ํน์ํ ๋ก์ง์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ React์ ๊ณต์ hook์ค ํ๋์ ๋๋ค. useEffect๋ ์ฒซ ๋๋์์๋ง ํน์ ๋ก์ง์ด ๋์ํ๊ฒ ํ ์๋ ์์ง๋ง dependencies array ๋ฅผ ํตํด ํน์ ๊ฐ๋ค์ด ๋ณํ๋์์๋๋ง ํน์ ๋ก์ง์ด ๋์ํ๋๋ก ํ ์๋ ์์ต๋๋ค.
const [reportId, setReportId] = useEffect<number>(1)
useEffect(()=> {
// reportId๊ฐ ๋ฌ๋ผ์ง๋๋ง๋ค ํธ์ถ ๋จ
axios.get(`/reports/${reportId}`)
},[axios, reportId])
์ด์ฒ๋ผ ์๋์น ์์ ์ํฉ์ ๋ณํ๋ฅผ ๋ง๋๋ ํจ์๋ฅผ Side effect function ์ด๋ผ๊ณ ํ๋๋ฐ์, ๊ณต์์ ์ผ๋ก ์ ๊ณต๋๋ hook๋ค ์ค์๋ useEffect ๋ง๊ณ ๋ useMemo, useCallback ๋ฑ์ด ์์ต๋๋ค.
์ฌ๊ธฐ์ ์ ๊ฐ ์ง์ค์์ ์ด์ผ๊ธฐ๋ฅผ ํด๋ณด๊ณ ์ถ์ ๋ด์ฉ์ dependencies array ๋ฅผ ํตํ useEffect ๋ด๋ถ ๋ก์ง์ํ ๊ณผ์ ์ ๋๋ค.
dependencies array ๊ฐ ๋ณํ ํ์๋๋ง ๋์ํ๋ค๋ ์กฐ๊ฑด์ ํด๋น ํจ์๋ค ๋ด๋ถ์์ dependencies array ์ ๋ด์ด์ค ๊ฐ๋ค์ ๊ธฐ์ตํด๋๊ณ ๋งค ๋๋์๋ง๋ค ํด๋น ๋๋ ์์ ์ ๊ฐ๊ณผ ๋น๊ตํ๋ค๋๊ฒ์ ์๋ฏธํฉ๋๋ค.
์์ ์ฝ๋ ์์์์๋ ์ฐ๋ฆฌ๊ฐ dependencies array ์ Type ์ด ์ซ์ํ์ธ reportId๋ฅผ ๋ด์์ค๊ฒ์ ์๋ก๋ค ์ ์์ต๋๋ค.
์ด๋ ์ฐ๋ฆฌ๊ฐ ์ค์ํ๊ฒ ๋ด์ผํ๋ ์ ์ dependencies array ์ ๋ด์์ฃผ๋ ๋ณ์๋ค์ ํ์ ์ ๋๋ค. ์์ ์์ ์ reportId์ฒ๋ผ ๋ฌธ์ํ, ์ซ์ํ ๊ณผ ๊ฐ์ ๊ฐ๋ค์ ๋ํด์๋ React ๊ฐ ์ ํ๋ฅผ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ๋ ์์ ์๋ง ๋ด๋ถ ๋ก์ง์ ์ํํด์ค๋๋ค.
ํ์ง๋ง, Object, Array ์ ๊ฐ์ ๋ฐ์ดํฐ ํ๋ค์ React๊ฐ ๋น๊ต๋ฅผ ๋ชปํ๊ณ ๊ฐ์ ๊ฐ์์๋ ๋ณ๊ฒฝ ๋์๋ค๊ณ ์ธ์ํฉ๋๋ค.
const [reportId, setReportId] = useEffect<number>(1)
const [apiProps, setApiProps] = useEffect<{name: string}>({name: ''})
useEffect(()=> {
// reportId๊ฐ ๋ฌ๋ผ์ง๋๋ง๋ค ํธ์ถ ๋จ
// apiProps ๋ณ๊ฒฝ ์ ๋ฌด ์๊ด์์ด ๋งค ๋๋์๋ง๋ค ํธ์ถ ๋จ
axios.put(`/reports/${reportId}`, apiProps)
},[axios, reportId, apiProps]
์์ ์ฝ๋์์๋ apiProps ์ ๋ณ๊ฒฝ์ ๋ฌด๋ฅผ react๊ฐ ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋งค ๋๋์๋ง๋ค axios.put(
/reports/${reportId}
, apiProps) ํจ์๋ฅผ ํธ์ถํ๊ฒ ๋ฉ๋๋ค.
์ด์งธ์ Object ํํ์ ๋ฐ์ดํฐ๋ ๋น๊ต๋ฅผ ์ ๋ชปํ๋๊ฒ ์ผ๊น์?
์์ธ์ ์๊ฐ๋ณด๋ค ๊ฐ๋จํฉ๋๋ค. React ์์๋ Shallow compare ๋ง ์ง์ํ๊ธฐ ๋๋ฌธ์ธ๋ฐ์, ๋ค์๋งํด deep compare ์ ํ์๋ก ํ๋ Object, Array ๋ฑ์ ์ง์ํ์ง ์๋๋ค๋๋ง์ ์๋ฏธํฉ๋๋ค. ์ด๋ฌํ ์ด์ ๋๋ฌธ์ dependencies array ์ Object๋ Array๋ฅผ ๋ด์์ฃผ๋ฉด ๋งค ๋๋์๋ง๋ค ์ ํ๊ฒฐ๊ณผ๊ฐ ๋ค๋ฅด๋ค๊ณ ํ๋จํ์ฌ ๋ด๋ถ ๋ก์ง์ ์ํํ๊ฒ ๋๋๊ฒ์ด์ฃ .
๐ค ๋ณธ๋ก
์ฌ์ค React๋ฅผ ๊ฐ๋ฐํ๋ค๋ณด๋ฉด ๋ณ์๋ฅผ Object ํํ๋ก ๊ด๋ฆฌํ๋ ์ํฉ๋ค์ด ๋ง์ด ๋ฐ์ํฉ๋๋ค. ์๋ฅผ๋ค์ด api ํธ์ถ์ ํตํด ๋ฐ์์จ Metric ํํ ๋ฐ์ดํฐ๋, ์ ์ ์ ๋ณด ๋ฑ์ด ์์์ ์๊ณ ๋ api ํธ์ถ์ ์ํด ๋ด์์ค์ผ ํ๋ props ๋ฑ์ด ์์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ฌํ ์ํฉ๋ค์์ useEffect๋ฅผ ์จ์ผ ํ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น์?
๊ฐ์ฅ ๋จ์ํ๋ฉด์ ๋์ด์คํ ๋ฐฉ๋ฒ์ dependencies array ์ Shallow compare ๊ฐ ๊ฐ๋ฅํ Type์ ๊ฐ๋ค๋ง ๋ด์์ฃผ๋๊ฒ์ ๋๋ค.
๋ํ์ ์ธ ์์๋ก Object ํํ์ ์ํ๊ฐ์ ๋ ์๊ฒ ์ชผ๊ฐ์ด ์ซ์์ ๋ฌธ์ํํ๋ก ๋ถ๋ฆฌํ๊ฑฐ๋, ๋ฌธ์์ด ๋ฐฐ์ด๋ฑ์ ๋น๊ตฌ์กฐ ํ ๋นํ์ฌ dependencies array ์ ๋ด์์ฃผ๋ ๋ฐฉ๋ฒ๋ฑ ์ฌ๋ฌ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
const [reportId, setReportId] = useEffect<number>(1)
const [apiProps, setApiProps] = useEffect<{name: string}>({name: ''})
useEffect(()=> {
// reportId๊ฐ ๋ฌ๋ผ์ง๋๋ง๋ค ํธ์ถ ๋จ
// apiProps ๋ด๋ถ ๊ฐ๋ค์ด ๊ฐ๊ฐ ๋ฌ๋ผ์ง๋๋ง๋ค ํธ์ถ ๋จ
axios.put(`/reports/${reportId}`, apiProps)
},[axios, reportId, ...Object.values(apiProps)]
๊ทธ๋ ๋ค๊ณ ํด์ ์์๊ฐ์ ๋ฐฉ๋ฒ์ ์ฉ ๋ง์๋๋ ๋ฐฉ๋ฒ์ ์๋๊ฒ๋๋ค. api props ๊ฒฝ์ฐ์๋ ๋ด๋ถ property ๊ฐ ๋ง์ง ์์ง๋ง api๋ฅผ ํตํด ๋ฐ์์จ Metric data๋ ๋ช๋ฐฑ๊ฐ์ Row data ๊ฐ๋ ์๋ ์๊ณ , ๊ฐ Row data ์ ํํ๊ฐ Object๊ฐ ์๋๋ผ๊ณ ๋ณด์ฅํ ์ ์์ํ ๋๊น์.
์ ์ผ ์ข์ ๋ฐฉ๋ฒ์ useEffect๋ฅผ ์ฌ์ฉํ์ง ์๋ ๋ฐฉ์์ผ๋ก ๋ก์ง์ ๊ฐ์ ํ๋ ๊ฒ์ด๊ฒ ์ง๋ง, ๋ ๊ทธ๋ ๋ฏ ๋ค์ํ ์ธ๋ถํ๊ฒฝ์ ์์์ ์ํด useEffect๋ฅผ ์จ์ผ๋ง ํ๋ ๋ถ๊ฐํผํ ์ํฉ๋ค์ด ์๊ฒจ๋ ๊ฒ๋๋ค. ์ด๋ด๋๋ ์๋์ ๋ฐฉ๋ฒ์ด ๋์์ด ์ฃ ์ ์์ต๋๋ค.
(1) JSON.stringify()
JSON.stringify() ๋ฉ์๋๋ Object ํ ๋ฐ์ดํฐ๋ฅผ ๋ฌธ์์ด๋ก ๋ฐ๊ฟ์ฃผ๋ ๊ธฐ๋ฅ์ ์ํํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ณํ๋ ๋ฌธ์์ด์ dependencies array ์ ๋ด์์ฃผ๊ฒ๋๋ฉด ๋ฆฌ์กํธ๊ฐ ๊ทธ ๋ณํ๋ฅผ ๊ฐ์งํ ์ ์๊ฒ ๋ฉ๋๋ค.
const [reportId, setReportId] = useEffect<number>(1)
const [apiProps, setApiProps] = useEffect<{name: string}>({name: ''})
useEffect(()=> {
// reportId๊ฐ ๋ฌ๋ผ์ง๋๋ง๋ค ํธ์ถ ๋จ
// apiProps ๋ด๋ถ ๊ฐ๋ค์ด ๊ฐ๊ฐ ๋ฌ๋ผ์ง๋๋ง๋ค ํธ์ถ ๋จ
axios.put(`/reports/${reportId}`, apiProps)
},[axios, reportId, JSON.stringify(apiProps)]
ํ์ง๋ง, ๋๋ฌด ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ฌธ์์ดํ ํ ๊ฒฝ์ฐ์๋ ์ฑ๋ฅ์ ๋ฌธ์ ๊ฐ ๋ ์ ์์์ผ๋ก ์ฃผ์ํ์ ์ผ ํฉ๋๋ค.
(2) useDeepCompareMemoize
useDeepCompareEffect ๋ ์ด์ ๊ฐ์ useRef๋ก ๋ฉ๋ชจ์ด์ ์ด์ ํ์ฌ ์ ์ฅํด๋๊ณ ๋งค ๋๋์๋ง๋ค ํด๋น์์ ์ ๊ฐ๊ณผ ์ ์ฅํด ๋ ๊ฐ์ ๊น์ ๋น๊ต๋ฅผํ์ฌ ๋ฌ๋ผ์ก์๋๋ง ๊ฐ์ด ์ ๋ฐ์ดํธ ๋๋๋ก ํ๋ ํ ์ ๋๋ค.
import { isEqual } from 'lodash'
function useDeepCompareMemoize<Type>(value: Type): Type {
const ref = useRef(value)
const signalRef = useRef(0)
if (!isEqual(value, ref.current)) {
ref.current = value
signalRef.current += 1
}
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(() => ref.current, [signalRef.current])
}
ํด๋น ํ ์ ๋ฉ๋ชจ์ด์ ์ด์ ์ด๋ผ๋ ์ต์ ํ ํ ์ ์ฌ์ฉํฉ๋๋ค. ํด๋น ํ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์น์ด๋๊ณ ๊ธฐ์ตํด์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ธ๋ฐ์, ๋ฉ๋ชจ๋ฆฌ์ ์น์ด๋๊ณ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๊ณผ๋ํ ์ฌ์ฉ์ ๋ฉ๋ชจ๋ฆฌ๋ฆฌ ๊ณต๊ฐ์ ๋ง์ด ์ฐจ์งํ์ฌ ์ฑ๋ฅ์ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค.
์ ์ฝ๋์์ useMemo ๋ฅผ ๋ณด๋ฉด dependecies array ๊ฐ ์๋๊ฒ์ ํ์ธ์ด ๊ฐ๋ฅํ๋ฐ์, ์ด๋ useMemo ๋ํ ๋ด๋ถ์ํ๋ฅผ ๋น๊ตํ์ฌ ์ต์ ํ ํ๊ณ ์์์ ์ ์ ์์ต๋๋ค.
์ด๋ฌํ ๋น๊ต ์์ ๋ํ ์ฑ๋ฅ์ ์ํฅ์ ์ฃผ๋๋ฐ์, ๋๋ก๋ ๋๋๋ง ์ต์ ํ๋ฅผ ์ํ ๋น์ฉ์ด ์ฌ๋๋๋ก ๋ฐ์ํ ๋น์ฉ๋ณด๋ค ์ปค์ง ์ ์์ผ๋ ํด๋น ํ ์ ์ฌ์ฉ์ ์ฃผ์ํ์ ์ผ ํฉ๋๋ค.
๐ก ๊ฒฐ๋ก
React ์์๋ ๋๋๋ง ์ต์ ํ๋ฅผ ์ํด useMemo, useCallback ๋ฑ์ ์ง์ํฉ๋๋ค. ํ์ง๋ง ์ด ํ ๋ค๋ useEffect์ ๋ง์ฐฌ๊ฐ์ง๋ก deep equal(deep compare) ๋ฅผ ์ง์ํ์ง ์์ต๋๋ค. ๋๋ฌธ์ ์ด๋ฌํ ์ํฉ์์ useDeepCompareEffect ์ฌ์ฉ์ด ๋์์ด ๋ ์๋ ์์ต๋๋ค.
ํ์ง๋ง, useMemo ๋ useCallback๋ ๋๋๋ง ์ต์ ํ ๊ณผ์ ์์ ๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ์ด ์๋๊ฒ์ฒ๋ผ useDeepCompareEffect ๋ฅผ ์ฌ์ฉํ๋๊ฒ๋ ๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ์ฑ ์ ์๋๋๋ค. ๋๋ฌธ์ ๋ก์ง์ ๊ฐ์ ํ์ฌ useDeepCompareEffect๋ฅผ ์ฌ์ฉํด์ผํ๋ ์ํฉ์ ๋ง๋ค์ง ์๋๊ฒ์ด ์ต์ ์ฑ ์ด๋ผ ๋ณผ์ ์์ต๋๋ค.
์ด๋ฌํ ํ ๋ค์ ์์๋ฐฉํธ ๋ํ๊ตฌ๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ํธ๋ฆฌํด์ ์๊พธ ์์ด ๊ฐ๊ฒ ๋ฉ๋๋ค. ํ์ง๋ง, useDeepCompareEffect๋ง ๋ณด๋ฉด ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํด์ผํ๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ์๊ธธ ์ ์๊ณ , ๋ด๋ถ์์ ๋งค ๋๋์๋ง๋ค ๋น๊ต ๋ก์ง์ด ์ํ๋๊ธฐ ๋๋ฌธ์ ๋ฌด๋ถ๋ณํ ์ฌ์ฉ์ ์ฌ์ดํธ์ ์ฑ๋ฅ์ ์ ํด์ํฌ ์ ์๋ค๋ ์น๋ช ์ ์ธ ๋จ์ ์ด ์์ต๋๋ค. ๋๋ฌธ์ ์ํฉ์ ์ ํ๋จํ์ฌ์ ์ฌ์ฉํ๋๊ฒ์ ๊ถ์ฅ๋๋ฆฌ๋ฉฐ ๊ธ์ ๋ง์นฉ๋๋ค : )