앞으로 모든 리액트 글이 그렇겠지만,
이 글은 그렇게 체계적이지는 않을 것이다.
프론트를 체계적으로 공부해본 것도 아니고 딱히 프론트를 깊게 공부할 생각도 없기 때문이다...
이 글에서는 코딩하다가 생겼던 일에서 배운 점과 지금까지 관련 이슈에 대해 알아본 것을 가볍게 정리해보려고만 한다.
리액트 만져보신 분이라면 한 번쯤은 만나본 에러가 아닐까 싶다.
프론트 초짜인 필자는 저번 주에 처음 목도했다...
필자의 경우, rendering dependency가 엉켜있었기 때문에 발생했었다.
예시를 들자면 아래와 같았다.
import { doSomething } from './doSomething';
<ChildComponent
func1={() => doSomething()}
/>
// ChildComponent.js
const ChildComponent = ({ func1 = noop }) => {
useEffect(() => {
func1()
}, [func1])
}
대충 이런 형태의 코드였다...
리액트 좀 치시는 분들에겐 얼토당토 않는 코드지만
필자는 생각이 무지 얕기 때문에 저렇게 했다.
부모 컴포넌트 render
→ func1
render
→ useEffect
발동
→ side-effect로 부모 컴포넌트 render
...
필자가 실제로 작성한 코드는 저것보다 훨씬 복잡했다.
그래서 func1
때문에 부모 컴포넌트가 re-render 되었고
그 바람에 무한 렌더링에 빠져버렸다...
해결책은?
리액트 조금씩 공부하던 터라 몇가지 React hook은 알고 있었다.
그 중에 함수 관련된 hook인 useCallback
과 useMemo
를 떠올렸다.
useMemo
는 반환값을 정해두는 것이므로 아니겠고,
useCallback
이 re-rendering이겠구나! 하여 func1
을 부모 컴포넌트에서 wrap해줬다.
import { doSomething } from './doSomething';
const wrappedFunc1 = useCallback(() => doSomething(), [])
<ChildComponent
func1={() => doSomething()}
/>
// ChildComponent.js
const ChildComponent = ({ func1 = noop }) => {
useEffect(() => {
func1()
}, [func1])
}
부모 컴포넌트에서 props
로 넘겨주는 함수를 wrap 해주니,
무한 렌더링은 해결되었다.
여기서 질문이 생겼다.
모든 React function들은 useCallback
이나 useMemo
로 wrap해주면 되지 않나??
내 나름대로 '모든 function은 useCallback
, useMemo
로 wrap해버리자!' 라고 결정짓고 끝내려 했으나..
React 공식 문서에서는 useCallback
, useMemo
등의 hook들을
특정한 경우에만 필요한 것이라고 설명했다.
모든 상황에서 쓰면 안된다고 명시적으로 설명하진 않았지만,
특정한 경우라고 분명히 한정지었다.
이 때문에 구글링 좀 해봤지만,
아직 왜 모든 function을 wrap하면 안되는지에 대한 명확한 설명은 찾지 못했다.
단, 이런 재밌는 걸 찾았다.
Premature optimization is the root of all evil.
Donald Knuth라는 저명한 컴퓨터 과학자께서 남긴 명언이다.
그의 논문 Structured programming with go to statements 에서 처음 쓰인 문장이다.
논문은 1979년에 작성되었음에도, 아직까지 유용한 인용구로 보인다.
여기 짤막하게 설명한 블로그 글이 있다.
결론적으로는, useCallback
과 useMemo
를 남용하지 말자.
메모이제이션 또한 자원을 사용하고,
리액트 공식문서에서도 '고비용 함수'에서 사용한다고 설명되어 있다.
effect dependency가 복잡하다면 useReducer
를 사용하라고 권장한다.
간단한 effect dependency는 useState
를,
복잡한 effect dependenct는 useReducer
를 사용하라고 설명했는데,
얼마나 간단하고 복잡한지는 우리가 직접 판단을 내려야 하지 않을까 싶다.
필자는 이제 useReducer
를 공부해보러 이만...!!