react 의 useCallback 활용 꿀팁

Q&A 큐레이션

1. useCallback 으로 내부 컴포넌트를 선언하고 사용할 경우 얻는 장점이 무엇인가요?

아래 코드와 같이 내부에서 사용하는 컴포넌트를 useCallback 으로 선언해서 사용하는 코드가 있습니다. 저는 InnerComponent 를 새로운 컴포넌트로 만들고 props 만 넘겨주는게 더 효율적일 것 같아요. 이렇게 useCallback 을 사용해서 컴포넌트를 선언해서 얻는 장점이 있는 걸까요 ?


답변

안녕하세요 : ) 해당 컴포넌트만 봤을때에는 큰 효용이 없을것 같아요. 이유는 기본적으로 상태가 변경되면 해당 상태를 선언한 컴포넌트가 재랜더가 되기 때문인데요, useCallback 디펜던시 배열에 관련 상태를 넣었기 때문에 해당 함수 또한 재 호출이 이루어지기 때문입니다. 하지만, 첨부주신 코드에는 없지만, 부모 컴포넌트에서 특정 상태를 내려받아 활용하고 계시다면? useCallback이 조금 의미가 있을수도 있을것 같아요! 간혹, 부모의 랜더와 상관없이 자식 컴포넌트 상태를 유지하고 싶을때가 있는데 그럴때 useCallback, useMemo,를 많이 사용합니다 ㅎㅎ 함수형으로 선언된 리액트 컴포넌트도 함수가 맞아 useCallback을 적용이 가능하지만, 보통 컴포넌트는 React.Memo로 메모리에 올려 사용하는것 같습니다 ㅎㅎ 해당 메서드도 활용해보시면 좋을것 같아요!

외 1개 답변 보러 가기

2. 실무에서 useCallback useMemo 자주 사용하나요?

제곧내! 실무에서 useCallback , useMemo를 어느정도 사용하는지 궁금합니다. 모든 함수에 useCallback을 할 것 같지는 않아보여서요. 사용하게되는 경우라면 어떤 경우인지 궁금합니다!


답변

메모이제이션은 불필요한 반복적인 연산을 막을때 주로 사용합니다. 설명만 들었을때는 마법의 도구 같아 보일수도 있겠지만, 세상에는 공짜가 없듯이 값비싼 연산을 최적화 하주는 대신에 컴퓨터 메모리 공간을 지불해야합니다. 메모이제이션 할 대상 데이터 크기에 따라 달라지겠지만 너무 잦은 사용시 메모리를 많이 잡아먹게 됩니다. (이 또한 성능에 영향이 있을수 있어요) 대부분의 상황에서는 작은 데이터 혹은 짧은 함수에 사용되기에 메모리사용으로 인한 문제로 이어지진 않을거에요. 하지만 성능이란게 예방해서 나쁠게 없는것처럼 정말 필요한 상황에만 사용하는 습관을 갖는게 중요합니다. useEffect도 메모이제이션 함수들도 사용해야하는 상황을 잘 살펴보면 구조적으로 해결 가능한 문제인 경우가 꽤나 많거든요!

외 4개 답변 보러 가기

3. useCallback vs 컴포넌트 밖에 선언된 함수

React를 공부하다가 궁금한점이 생겨서 질문 남깁니다 useCallback은 의존성 배열의 값이 바뀌지 않는한 같은 함수를 반환하는 것으로 알고있습니다. react 컴포넌트 안에서 선언되는 함수는 useCallback의 의존성 배열에 추가해야되는 것을 이해하겠는데, react 컴포넌트 밖에서 선언되는 함수도 useCallback의 의존성 배열에 추가를 해야하나요? 혹시 성능 차이가 날까요?


답변

안녕하세요! 컴포넌트 밖에 선언되는 함수는 렌더 시 매번 새로 선언되지는 않으니 의존성 배열에 넣을 필요가 없지 않을까 싶습니다. (처음 파일을 읽을때 선언된 이후로 안 바뀔 것으로 예상합니다) 성능 차이는 코드마다 다르겠지만 그렇게 크지는 않을것 같아요. 관련해서 so나 테크 블로그 참고해보시면 좋을 것 같아요. - https://stackoverflow.com/questions/60660415/performance-difference-from-putting-function-outside-of-react-component - https://alexsidorenko.com/blog/react-render-usecallback/

외 1개 답변 보러 가기

4. Maximum update depth exceeded 에러 해결 방법

콘솔에 "Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn’t have a dependency array, or one of the dependencies changes on every render." 라는 에러가 표시되면서 해당 컴포넌트가 무한히 렌더링 되고 있습니다. 문제 원인과 해결 방법을 아시는 분이 계실까요? 코드 간단히 요약해서 첨부합니다.


답변

setordercart 할때 조건을 걸어줘야 할것같네요 의존성 배열에 들어가있는 값이 리렌더링이 발생 할때마다 변경되는것으로 인지해서 셋을 계속 호출 하는 상황입니다 아니면 의존성 배열에 해당하는 데이터를 usememo 나 usecallback 으로 감싸줘야 할것같네요

이 질문 바로 가기

5. React.memo로 감싼 컴포넌트가 계속 렌더링 됩니다.

부모 컴포넌트에서 map으로 자식 컴포넌트를 렌더링 하고 있습니다. 자식 컴포넌트가 받는 props에 변화가 없어서 React.memo로 감쌌는데 컴포넌트가 계속 렌더링 되고 있습니다. props도 확인했습니다.. props는 {userId:number, onClick: () => {history.push(...)}} 이런식으로 넘겨주고 있습니다. 해결하고 싶은데 도움주시면 감사하겠습니다!


답변

onClick으로 전달하는 () => history.push 함수로 인해 리렌더링 되는 것 같습니다. 참조 동일성의 개념을 아셔야하는데요, 리액트는 값을 비교하여 리렌더링 할 때 얕은 비교(Shallow Compare)를 합니다. 얕은 비교는 객체, 배열, 함수와 같은 참조 타입들을 실제 내부 값까지 비교하지 않고 동일한 메모리 값을 사용하는지(동일 참조인지)를 비교합니다. map으로 자식 컴포넌트가 생성될 때 새로운 메모리 값을 사용하는 함수가 onClick으로 전달되어 메모이제이션을 하지 못하고 리렌더링 되는 현상같아요. React.memo를 사용한다면, 함수를 props에서 바로 정의하지 말고, 부모 컴포넌트안에 정의한 뒤 useCallback으로 감싸서 전달하면 리렌더링이 발생하지 않을 것 같습니다. 코드로 간략하게 작성해봤습니다. const handleChildClick = useCallback(() => { history.push(...) }, []) <Child onClick={handleChildClick}/>

이 질문 바로 가기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직 개발자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

기술, 커리어 고민이 있다면

새로운 질문 올리기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!