Korean_hamster
지현의 개발자 성장과정
Korean_hamster
전체 방문자
오늘
어제
  • 분류 전체보기 (122)
    • Front-End Developer (79)
      • Project (12)
      • HTML (8)
      • CSS (17)
      • Computer Science (9)
      • JavaScript (20)
      • React (13)
    • 이런저런 생각 (7)
    • 주간 성장회고 (24)
    • English (0)
    • 리뷰 (2)
    • Books (5)

블로그 메뉴

  • 방명록

공지사항

인기 글

태그

  • 비전공자개발자
  • fetch
  • CS
  • 면접관님
  • 라우터
  • 반응형웹
  • 프론트앤드스쿨
  • HTML
  • 비전공개발자
  • flex
  • 멋사
  • 멋쟁이사자처럼
  • sass
  • 깃
  • js
  • CSS
  • 리액트
  • 프론트앤드
  • 깃헙
  • AtomicHabits

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Korean_hamster

지현의 개발자 성장과정

Front-End Developer/React

useCallback. [의존성 배열] 난 너만 바라봐

2022. 6. 15. 02:04

결론:  [의존성 배열] 난 너만 바라봐.

 

useCallback

useCallback(() => {
	return value;
}, [의존성배열])
const calculate = useCallback((num) => {
	return num + 1;
}, [의존성배열]

이렇게 함수를 useCallback으로 감싸주면 이제 이 calculate라는 변수는 메모이제이션된 함수를 갖고있게된다. 이는 의존성 배열 안에있는 값이 변경될 때 에만 초기화된다.

이 useCallback이 필요한 이유를 알고 이해하기 위해 상황을 설정해보자.

 

 

상황설정

간단한다. 인풋하나와 버튼하나가 있다.

  • 인풋값은 number만 들어간다. 그리고 useState를 사용하여 변경값을 input value에 넣어준다.
  • 버튼의 변화를 감지하여 콘솔에 '변경감지'를 출력하는 useEffect함수가 있다.
  • 버튼에 달린 변수는 콘솔로그에 number를 찍는다.
import { useState, useEffect } from 'react';

function UseCallback() {
  const [number, setNumber] = useState(0);

  const someFunction = () => {
    console.log(`someFunc: number: ${number}`);
    return;
  };
  
  //버튼의 변화를 감지하여 콘솔에 '변경감지'를 출력하는 useEffect함수가 있다.
  useEffect(() => {
    console.log('변경감지');
  }, [someFunction]);

  return (
    <div>
    //인풋값은 number만 들어간다. 그리고 useState를 사용하여 변경값을 input value에 넣어준다.
      <input
        type='number'
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />
      <br />
      //버튼에 달린 변수는 콘솔로그에 number를 찍는다.
      <button onClick={someFunction}>Call someFunc</button>
    </div>
  );
}

export default UseCallback;

 

예상하는 동작

: someFunction이 브라우저에 mount될 때, 그리고 someFunction에 변화가 있을 때 콘솔에 ‘변경감지' 가 출력

 

 

 

실제 결과

 

하지만 결과는 예상과는 다르게 someFunction에 아무런 변화가 없었음에도 콘솔에는 처음 mount 되었을 때 + setNumber함수가 실행되었을때에도 콘솔에 ‘변경감지'가 찍히는 것을 알 수 있다.

 

 

 

왜 그럴까?

두가지를 이해해야 한다.

 

  1. 리액트는 함수형 컴포넌트이다. 다시말해 랜더링이 일어나면 함수 안의 변수들은 초기화된다.
  2. 자바스크립트의 오브젝트는 reference data type이다.

 

리액트는 함수형 컴포넌트

 

지금 이 코드에서 input값에 변화를 줄 때마다 재랜더링이 된다. useState를 사용하고있기 때문이다.

useState는 값이 변화될 때 마다 랜더링을 일으켜 그 값을 업데이트한다. 그리고 랜더링은 useState가 적용되는 값만 렌더링되는게 아니라, 그 함수가 포함된 컴포넌트를 전체 랜더링시키고 그때마다 모든 변수들은 초기화된다.

 

근데 변수가 초기화되었다고 한들, someFunction에 변한게 없는데 왜 ‘변경감지'를 출력하냐고?

 

 

자바스크립트의 오브젝트는 reference data type이다.

그 말은 오브젝트가 어떤 변수에 할당이 될 때에는, 오브젝트 자체가 아니라 오브젝트는 어디 딴데에 가서 저장되어있고 그 저장된 ‘주소값'이 할당된다는 뜻이다.

 

위와 같이 똑같은 객체가 들어있음에도 불구하고 test1과 test2에는 각각 다른 주소값이 들어있기 때문에 비교시 false를 반환한다.

const someFunction = () => {
    console.log(`someFunc: number: ${number}`);
    return;
  };

useEffect(() => {
    console.log('변경감지');
  }, [someFunction]);

이제 알겠는가? 즉 someFunction 안에는 객체가 들어있기 때문에, 브라우저가 재랜더링 되어 someFunction값이 초기화되면 ;그 전과는 다른 주소값’을 가지게 된다는 소리다!

당연히 useEffect의 입장에서는, someFunction의 주소값이 바뀌었으니 변화라고 인식하고 콘솔로그를 출력하는 것이다.

 

 

이를해결하기 위한 useCallback함수

// useCallback import
import { useState, useEffect, useCallback } from 'react';

function UseCallback() {
  const [number, setNumber] = useState(0);

// 콜백함수 자체를 useCallback함수에 넣어준다.
  const someFunction = useCallback(() => {
    console.log(`someFunc: number: ${number}`);
    return;
  }, []);
//의존성 배열에 아무것도 넣어주지 않았으니 이 함수는 맨 처음 렌더링 될 때 만들어져서 
//메모이제이션이 된다. someFunction안에는 메모이제이션 된 주소가 들어있다.

  useEffect(() => {
    console.log('변경감지');
  }, [someFunction]);

  return (
    <div>
      <input
        type='number'
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />
      <br />
      <button onClick={someFunction}>Call someFunc</button>
    </div>
  );
}

export default UseCallback;

useCallback은 인자로 전달한 콜백함수 자체를 메모이제이션 해준다. 그 다음 랜더링부터는 변수가 재할당되지 않고 계속 처음에 메모이제이션 했던 값의 주소값을 사용한다. 의존성배열이 []이기 때문이다. 

 

의존성 배열 안에 다른값을 넣으면 그 값이 변화될 때에 someFunction을 포함하여 랜더링된다. 여기서 넌 나만 바라봐가 나온것이다. 이름부터 의존성 배열이니까. 여친(남친)에게 의존하며 걔만 바라보고 있는거다. 걔가 변화되었을때만! 반응을 한다. 

 

 

그런데 또 문제점이?

number를 변화시켜준 후 Call someFunc버튼을 눌러도 콘솔로그에 number는 0이 찍히는데, 이는 처음 렌더링에서 메모이제이션해줬을 당시에 number가 0이었기 때문에 그 값 그대로 메모이제이션 된 것이다.

 

이 경우 바로 전에 살펴봤던 것처럼 number가 변화될 때마다 someFunction 안의 함수를 갱신해주고싶으니 의존성 배열에 [number]를 넣으면 해결이다.

const someFunction = useCallback(() => {
    console.log(`someFunc: number: ${number}`);
    return;
  }, [number]);

 

 

 

콘솔 파헤치기

 

  1. 처음 mount될 때 변경감지
  2. number가 바뀔때마다 useEffect가 someFunction의 변화를 감지하여 '변경감지'출력
  3. number가 바뀔때마다 useCallback이 실행되어 someFunction이 다시 초기화-> 콘솔에 number출력
저작자표시 (새창열림)

'Front-End Developer > React' 카테고리의 다른 글

리액트 유치원이 개원했습니다. (유투브 채널개설)  (0) 2022.06.20
useState Lazy initialization - 게으른 초기화가 대체 뭔소리야? (+유투브 영상 업로드)  (4) 2022.06.16
Router v6 헷갈려서 공부해봤다. 기초정리  (0) 2022.06.14
React를 한다면 무조건 알아야 할 - useRef 1편  (0) 2022.06.13
영혼없이 썼던 import React from 'react' - 안써도 된다는데?  (0) 2022.06.13
    Korean_hamster
    Korean_hamster
    Keep pushing myself to the limits

    티스토리툴바