본문 바로가기
JS/React 강의

[React] 20. useEffect와 useCallback에서 의존성 배열을 사용하는 이유 (Dependencies Array 설명)

by 박기린 2024. 1. 4.

안녕하세요. 박기린입니다.

 

useEffect와 useCallback에는 의존성배열 (dependencies array)를 인수로 받는 영역이 있습니다.

useEffect 설명글 : https://arnopark.tistory.com/770

useCallback 설명글 : https://arnopark.tistory.com/845

(위 두 글을 읽고, 본 글을 읽는 것을 적극 추천드립니다.)

 

 

 

지금부터 dependencies array를 사용하는 이유에 대해 좀 더 깊이 알아보겠습니다.

 

 

 


종속성이 필요한 경우

useCallback()을 사용한 예시를 통해 설명을 드리겠습니다.

 

 

// App.js

function App() {
  const [showParagraph, setShowParagraph] = useState(false);
  const [allowToggle, setAllowToggle] = useState(false);

  const toggleParagraphHandler = useCallback(() => {
    if (allowToggle) {
      setShowParagraph((prevShowParagraph) => !prevShowParagraph);
    }
  }, []);
  

  const allowToggleHandler = () => {
    setAllowToggle(true);
  }


  return (
    <div className="app">
      <h1>Hi there!</h1>
      <DemoOutput show={showParagraph} />
      <Button onClick={toggleParagraphHandler}>Toggle Paragraph!</Button>
      <Button onClick={allowToggleHandler}>Allow Toggle</Button>
    </div>
  );
}

 

위 코드를 살펴보면

state : allowToggle, showParagraph
핸들러함수 : toggleParagraphHandler, allowToggleHandler
<버튼> :Toggle Paragraph!, Allow Toggle!

이 요소들을 발견할 수 있습니다.

 

allowToggleHandler 함수를 통해 allowToggle state가 변경되면, 이에 맞춰서 toggleParagraphHandler 함수의 if 문이 활성화/비활성화되는 구조입니다.

이해가 힘들다면, 밑의 실제 실행화면을 참고해봅시다.

 

 

 

 

 

위 코드를 실행하면 이런 페이지가 뜹니다.

 

이 페이지에서 기대하는 작업은 이러합니다.

1. Allow Toggling 버튼을 누르면, Toggle Paragraph! 버튼이 활성화됩니다.
2. 그리고 Toggle Paragraph! 버튼을 누르면, 버튼 위에 'this is new!'라는 문구가 표시됩니다.

이제 실제로 버튼을 차례대로 눌러보겠습니다.

 

 

 

 

 

 

막상 눌렀더니, 아무런 변화가 없습니다. 오류가 발생했습니다.

 

 

 

 

오류가 발생한 이유는?

useCallback()을 잘못 사용한 것입니다.

자바스크립트 함수는  {클로저}로 구성되어 있습니다. 함수가 선언되면, 함수 내부 변수들은 클로저 내에서만 사용가능한 지역변수로 설정됩니다. 그리고 외부에서 선언한 변수를 가져오는 경우에는, 함수가 초기에 선언되는 시점에서 그 변수가 저장된 값을 가져옵니다.

 

 

 

원할한 이해를 위해 예시를 들어보겠습니다.

let a = 1;
const example = () => {
 let b = 2;
 console.log(a, b)
}
example() // 1, 2
console.log(b); // error

실행결과

a는 함수 외부에서 선언된 변수이고, b는 내부에서 선언된 변수입니다.

example 함수가 선언된 시점에서 a에는 1이 담겨 있습니다. 그래서 그 1을 그대로 가져와서 console.log(a, b)에 1을 넣어 출력합니다.

 

 

 

만약 a값이 처음과는 다르게 변했다면? (오른쪽 사진처럼 a에 3을 대입한 경우)

-> example 함수가 재실행될 때, a의 값을 최신 버전으로 갱신합니다.

 

 

 

 

이제 다시 useCallback()을 살펴보자

이제 다시 원래 React 코드로 돌아오겠습니다.

// App.js 중
  const toggleParagraphHandler = useCallback(() => {
    if (allowToggle) {
      setShowParagraph((prevShowParagraph) => !prevShowParagraph);
    }
  }, []);

toggleParagraphHandler 함수는 <Toggle Paragraph!> 버튼의 핸들러 함수입니다.

코드를 잘 보면, 'allowToggle' state가 함수 외부에서 가져와서 사용하는 값입니다.

사진의  <Toggle Paragraph!> 버튼 위에 있는 <Allow Toggling> 버튼을 누르면, allowToggle state가 변경됩니다.

 

 

toggleParagraphHandler함수는 allowToggle state의 값이 변경될 때마다 재실행/재평가를 통해 갱신을 해줘야 합니다. allowToggle state가 함수 외부의 값이기 때문입니다. 그런데 useCallback()으로 재실행/재평가를 막아버려서 allowToggle의 상태가 초기에 선언됐던 당시의 값 그대로 유지되고 있습니다. 즉, 변경된 값을 갱신하지 못합니다.

 

 

const [allowToggle, setAllowToggle] = useState(false);

allowToggle은 초기에 false로 선언됐었습니다.

그래서 toggleParagraphHandler 함수 내부의 if문에 걸려, 제대로 된 실행이 안 됐던 것입니다.

 

 

 

해결책 -> 종속성 배열

이 문제에 대한 해결책은 간단합니다.

// App.js 중
  const toggleParagraphHandler = useCallback(() => {
    if (allowToggle) {
      setShowParagraph((prevShowParagraph) => !prevShowParagraph);
    }
  }, [allowParagraph]);

useCallback()의 두 번째 인수인 종속성 배열(dependencies array)'함수 외부에서 가져온 값 - allowParagraph'를 추가해줍니다. 이러면, allowParagraph state가 변경될 때마다 이에 맞춰서 React 엔진이 함수를 재실행/재평가 합니다.

 

 

 

 

이제 프로그램이 정상적으로 작동됩니다.

 

 

 

 

 

반응형