React 알아가기 (9)

삔아·2023년 6월 9일
0
post-thumbnail

해당 내용은 https://www.udemy.com/course/best-react/ 강의를 들으며 정리하고 스스로 공부 한 내용을 기록 하였습니다.

지난 포스팅 (React.memo) 에서 말했듯이 props를 통한 객체나 배열 또는 함수를 가져오는 컴포넌트에는 어떻게 해결 할 지에 대해 알아보도록 하자.

About useCallback()

컴포넌트 실행 전반에 걸쳐 함수를 저장 할 수 있게 하는 훅

리액트에 함수를 저장하고 실행할 때 마다 함수를 재생성 할 필요가 없다는 것을 알려준다.

강의를 복습 하면서 useCallback 부분이 잘 이해가 가지않아 따로 검색해보았는데,

사실 useCallback 은 새로 생성되지 않아서 동일 참조 값을 사용하는 게 아니라 매 렌더마다 항상 함수의 생성까지는 되는데, 의존성이 바뀌지 않았다면 생성된 함수를 무시하고 기존 함수를 반환 한다고 한다.
참고: Hook 자주 묻는 질문 – React

let obj1 = {};
let obj2 = {};
obj1 === obj2 // false

obj1 = obj2;
obj1 === obj2 // true

우리가 선택한 함수를 리액트의 내부 저장 공간에 저장해서 함수 객체가 실행 될 때 마다 이를 재사용 할 수 있다.

사용법은 저장하려는 함수를 래핑 해주면 된다.

const toggleParagraphHandler = useCallback(() => {
    setShowParagraph((prev) => !prev);
  }, []);

해당 예시처럼 useCallback 훅을 사용하고, 함수를 첫 번째 인자로 전달하면 useCallback은 이 저장된 함수를 반환 해준다.

그리고 App 함수 가 다시 실행되면 useCallback이 리액트가 저장한 함수를 찾아서 같은 함수 객체를 재사용 한다.

useCallback은 어떤 함수가 절대 변경 되어서는 안될 때 자주 쓰인다.

또한 두 번째 인자가 필요한데, 두 번째 인자는 useCallback 호출에 대한 의존성 배열 이 들어와야 한다. (useEffect 와 같은 의미를 가지고 있다.)

내용이 없는 의존성 배열이 온 경우에는 리액트에게 이 콜백 함수는 절대 변경되지 않을 것이라고 알려주는 배열이다.
따라서 App 컴포넌트가 다시 렌더링 되어도 항상 같은 함수 객체가 사용된다.

useCallback 은 메모리 안에서 항상 같은 함수 객체 임을 보증한다.

useCallback 에서 의존성 배열은 늘 최신 값을 가져와 기존 값과 변경이 되었는지 비교 후 기존 함수를 반환 할지 새로운 함수 객체를 반환 할지 결정한다.

최신 값을 가져올 수 있는 것은 자바스크립트의 함수는 모두 클로저 함수 이기 때문이다. ( 참고 : https://ko.javascript.info/closure 해당 페이지에서 도움을 많이 받았다.)

이 의존성 배열의 필요성을 강의에서 들었던 내용의 코드를 가져와서 약간의 설명을 해보자면,

만약, 위 사진에서 의존성 배열에 들어가있는 allowToggle 이 없다고 가정해보자.

App함수에 있는 블록 안의 함수들이 정의가 될 때, 이 App함수 내부에서 사용 되는 모든 변수들을 잠그게 된다.

나는 이를 하나의 컨텍스트를 생성한다 라고 생각하며, 여기서 allowToggle 에 집중하여 보면 될 것 같다.

이는 변수나 상수가 될 것이며, App함수 내부의 함수안에서 사용하고 있기도 하다.

자바스크립트는 이 상수에 클로저를 만들고 함수를 정의할 때 사용하기 위해 상수를 저장하게 되는데, 이렇게 되면 이 toggleParagraphHandler 가 실행이 되면 이 저장된 상수를 그대로 사용할 수 있는 것이다.

즉, 이 변수의 값은 변수가 저장된 시점의 값을 사용하는 것 이다.

이 기능을 사용하면 함수 밖의 변수를 함수 안에서 쓸 수 있으며 우리가 원하는 시점에 함수를 호출 할 수 있다.

하지만 useCallback 을 사용하여 리액트에게 해당 함수를 메모리 어딘가에 저장하도록 지시하게 되는데, 이를 사용하면 allowToggle 의 값은 최신 값이 아닌 App 컴포넌트가 처음 실행된 시점의 값을 저장하고 있기 때문에 문제가 발생한다.

따라서 의존성 배열에 allowToggle종속형태로 추가하여 최신 값을 가져오도록 지시하여 새로운 값이 들어오면 새로 생성한 함수를 반환 하도록 도와주게 된다.

정리하자면,

리액트 컴포넌트에서는 상태와 props, 컨텍스트를 이용해 작업할 수 있으며 props와 컨텍스트는 결국 상태의 변경으로 이어지며, 변경된 상태가 있는 컴포넌트는 재평가, 즉 컴포넌트 함수가 재 실행 된다는 것을 알아보았다.

리액트는 단순히 최신 평가의 결과를 가져와서 직전 평가의 결과와 비교하며, 확인된 모든 변경 사항이나 차이점을 리액트 DOM에 전달한다.
이 리액트DOM 은 변경 사항을 브라우저의 실제 DOM에 적용 하고 변경 되지 않은 것들을 그대로 둔다.

리액트가 컴포넌트를 재평가 할 때 단순히 컴포넌트 재평가에서 그치지 않고 전체 함수를 재실행하고 이를 통해 코드를 전부 리빌드 한다.
이 과정에서 하위 컴포넌트의 불필요한 재실행을 막기 위해 React.memo 를 배웠으며, 이를 통해 리액트에게 props가 실제로 변경되었을 경우에만 컴포넌트 함수를 재실행하고 그것이 아니라면 함수를 재실행 하지 않도록 할 수 있다.

컴포넌트 재평가는 컴포넌트 함수 전체 재실행을 의미 한다는 점을 다시 한번 말하며, 함수 객체를 props를 통해 컴포넌트에 전달 할 때, 객체는 참조 값이고 React.memo 가 내부적으로 실행하는 등호를 통한 비교는 원시값에 대해서 통용되지 않기 때문에 React.memo 의 도움을 얻을 수 없다.
이를 해결하기 위해 useCallback 을 사용한다.

useCallback 을 통해 리액트에게 이 함수를 저장하고, 이를 둘러싼 함수가 재실행 되어도 특정 의존성이 변경 되는 것이 아니라면 기존의 함수를 반환하도록 지시 할 수 있다.

profile
Frontend 개발자 입니다, 피드백은 언제나 환영 입니다

0개의 댓글