ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React #3 리액트 Hooks - useMemo와 useCallback

    Library/React 2023. 8. 25. 10:54

    React Hooks

    hooks는 리액트 v16.8에 새로 도입된 기능으로 함수 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해준다. 리액트에서 기존에 사용하던 class를 이용한 코드를 작성할 필요 없이, 여러 리액트 기능을 사용할 수 있도록 만든 라이브러리이다.

     

    가장 기본적인 함수는 useState이며 리액트의 함수 컴포넌트에서 상태 관리를 할 수 있도록 해준다.

    import { useState } from 'react';
    
    const test = () => {
    	// testVal: 변수명
        // setTestVal: testVal 값 set
        // 0: 초기값
    	const [testVal, setTestVal] = useState(0);
        
        return (
        	<div>
            	<p>현재 값 : {value}</p>
                <button onClick={() => setTestValue(value + 1)}></button>
            </div>
        }
    }

    그 외에 많은 hook이 있지만 오늘 정리할 내용은 useMemo와 useCallback이다.

     

    react hooks

    useMemo와 useCallback

    useMemo와 useCallback 두 개를 사용하면서 약간 헷갈렸는데 그 이유는, 둘 다 특정 값이 바뀌는게 감지되면 연산을 한다고 생각했기 때문이다. 함수의 작성 방식은 아래와 같다.

     

    // useMemo
    const avg = useMemo(() => getAverage(list), [list]);
    
    // useCallback
    const avg = useCallback(() => {
    	const newList = list.concat(parseInt(number));
        setList(newList);
        setNumber('');
    }, [number, list])

    두 함수 모두 두 번째 인자(param)로 '어떤 값이 변경되었을 때 리렌더링할지 배열'을 받고 있으며, 렌더링 성능을 최적화해야 하는 상황에서 사용된다. 비슷해보이지만 쓰임새는 명확히 다른데 실제 코드에 적용해본 결과는 아래와 같다.

     

    useMemo

    // 스타일에 사용
    const gridStyle = useMemo(() => ({ height: '100%', width: '100%' }), []);
    
    // 연산에 사용
    const getRowId = useMemo(() => {
        return (params) => params.data.id;
    }, []);

    샘플을 보면 알 수 있듯이 useMemo는 연산의 결과를 저장하고 있으며, 배열로 전달한 의존성이 변경되었을 때만 '다시 연산'하는 것이다. 위의 소스는 백엔드에서 목록을 조회하고 그리드 형태로 화면에 그리는 것인데 전달받은 params의 data.id 값을 메모리제이션한다. 배열에 값을 전달했다면 그 값이 변경될 때에만 연산을 다시 할 것이지만 지금 이 코드에서는 전달하지 않았으니 매번 새로운 연산을 하게 된다.

     

     

    useCallback

    const onRowDataA = useCallback(() => {
        // Axios를 사용하여 데이터를 가져옵니다.
        axios
            .get('http://localhost:8080/hello')
            .then((response) => {
                const updatedRowData = rowData.concat({ id: 2, title: response.data });
                setRowData(updatedRowData);
            })
            .catch((error) => {
                console.error('Error fetching data:', error);
            });
    }, []);

    useCallback은 만들어놨던 함수를 재사용한다. 컴포넌트가 처음 렌더링될 때만 함수를 생성하고 그 이후에는 배열로 명시한 인자(param) 값이 변경될 때에 함수를 재생성한다. 때문에 함수 내부에서 상태 값에 의존해야 하는 경우에는 그 값을 반드시 두 번째 인자 안에 포함시켜야 한다. 

     

    하지만 리액트의 공식문서 중 아래와 같은 내용이 있다.

    useCallback(fn, deps)은 useMemo(() => fn, deps) 와 같습니다

     

    그 이유는 둘 다 'deps' 배열 내의 값이 변경되지 않는 한, 동일한 함수 인스턴스를 재사용하는 역할을 하기 때문이다.

     

     

    결론

    useMemo와 useCallback은 사용 방식도 유사하고, 재사용을 하므로써 렌더링을 최적화 하는 데에 목적이 있다. 하지만 가독성과 사용 의도를 명확하게 하기 위해 두 개를 구분해서 사용할 필요가 있다.

     

    • useMemo는 연산 결과를 메모리제이션 한다.
    • useCallback은 콜백 함수를 메모리제이션 한다.

     

     

Designed by Tistory.