본문 바로가기

오픈소스/노드

[Node] React 정리(12) - useCallback을 사용하여 성능 최적화 하기

 

리액트로 프로젝트를 생성하여 개발하는 과정 중 리액트의 필수 문법 및 기초적인 부분, 심화 과정 등을 정리한 문서입니다. 정리한 부분에는 제가 이해하는 관점에서만 정리를 하였기 때문에 초점은 보여주는 형식이 아닌 제가 필요할 때 사용하려는 목적이 담겨져 있습니다. 컴포넌트에서 useCallback를 이용하여 성능 최적화를 하는 예제를 담았습니다. 

 

 진부한 예제이지만 useCallback의 기초와 사용법 에 대해 배워보도록 합시다.

 

 

# Contents


  • 사용하는 이유
  • useCallback기초와 사용법

 

 

# 사용하는 이유


 useCallback 을 사용하는 이유를 먼저 설명하도록 하겠습니다. 괜히 복잡한 기능들만 배우면서 왜 사용하는지 모르면서 사용하는 것과 잘 알고 사용하는 것의 차이는 하늘과 땅이라고 생각합니다. 많은 함수들과 Hook 을 접해보면서 이러한 상황에서는 어떻게 최적화를 해야하는지 코드의 성능을 더 높이는 것과 재사용률을 높이는 것은 개발자의 역량이라고 생각합니다. 

 

 먼저 useCallback을 사용하기 전의 함수를 파악해보도록 하겠습니다.

 현재 안방과 주방, 화장실의 불을 켜는 간단하게 프로젝트를 생성해보았습니다. 

 

먼저 App.js 코드는 아래와 같습니다.

 

...생략...
    const [kitchenOn, setKitchen] = useState(false);
    const [livingOn, setLiving] = useState(false);
    const [restOn, setRest] = useState(false);

    return (
        <div>
            <CreateUser name={name} email={email} onChange={onChange} onClick={onClick} />
            <UserList users={users} onToggle={onToggle} onDelete={onDelete} />
            <div>활성화된 사용자 수 : {count}</div>

            <MyRoom room="키친" on={kitchenOn} onToggle={onToggles} />
            <MyRoom room="안방" on={livingOn} onToggle={onToggles} />
            <MyRoom room="화장실" on={restOn} onToggle={onToggles} />
        </div>
    );

    function onToggles(room, on) {
        var script = null;
        if (room == "키친") script = setKitchen;
        if (room == "안방") script = setLiving;
        if (room == "화장실") script = setRest;

        script(!on);
    }
...생략...

 

 서로 각각의 useState를 사용하여 버튼을 토글하게 되면 on/off 의 상태를 변화하도록 하였습니다. 

 

아래 코드는 MyRoom 소스 코드입니다.

 

function MyRoom({ room, on, onToggle }) {
    console.log({ room, on });
    return (
        <div>
            <h2>
                {room} 현재 상태 : {on.toString()}
            </h2>
            <button onClick={() => onToggle(room, on)}>{room} 버튼</button>
        </div>
    );
}

export default MyRoom;

 

 위 코드들을 이용하여 실행해본 결과 다음과 같이 하나를 토글하였을 때 연관된 값들의 함수들도 호출되게 됩니다.

 

아래는 결과 값 입니다.

 

 

 

이제부터는 이것의 해결방법인 useCallback을 소개할 것입니다.

 

 

# useCallback기초와 사용법


 useCallback를 사용하기 위해서는 다음과 같은 구문을 사용해야 합니다.

 

import react, { useCallback } from "react";

 

UseMemo는 다음과 같습니다.

  • useMemo와 비슷하다. --> useMemo 기반으로 만들어졌기 때문에
  • 첫번째 인수에 함수 를 두번째 인수에 상태가 props, state 에서 사용하는 배열을 넣는다.

 

구문은 다음과 같습니다.

 

useCallback(() => {실행할 함수}, props, state);

 

아래 코드를 통해 useCallback를 확인해보세요. App.js

 

import "./App.css";
import react, { useRef, useState, useMemo, useEffect, useCallback } from "react";
import UserList from "./UserList.js";
import CreateUser from "./CreateUser";
import MyRoom from "./MyRoom";

function App() {
    const [input, setInputs] = useState({
        name: "",
        email: "",
    });

    const { name, email } = input;

    const [users, setUsers] = useState([
        { id: 0, name: "John", email: "john@gmail.com", active: true },
        { id: 1, name: "Smith", email: "smith@gmail.com", active: false },
        { id: 2, name: "Park", email: "park@gmail.com", active: false },
        { id: 3, name: "Kim", email: "kim@gmail.com", active: false },
    ]);
    var nextId = useRef(5);

    var count = useMemo(() => countActiveUsers(users), users);

    /* test */
    const [masterOn, setMasterOn] = useState(false);
    const [kitchenOn, setKitchenOn] = useState(false);
    const [bathOn, setBathOn] = useState(false);

    const toggleMaster = useCallback(() => setMasterOn(!masterOn), [masterOn]);
    const toggleKitchen = useCallback(() => setKitchenOn(!kitchenOn), [kitchenOn]);
    const toggleBath = useCallback(() => setBathOn(!bathOn), [bathOn]);

    return (
        <div>
            <CreateUser name={name} email={email} onChange={onChange} onClick={onClick} />
            <UserList users={users} onToggle={onToggle} onDelete={onDelete} />
            <div>활성화된 사용자 수 : {count}</div>

            <MyRoom room="키친" on={masterOn} OnToggle={toggleMaster} />
            <MyRoom room="안방" on={kitchenOn} OnToggle={toggleKitchen} />
            <MyRoom room="화장실" on={bathOn} OnToggle={toggleBath} />
        </div>
    );

    function onChange(e) {
        var { name, value } = e.target;
        setInputs({ ...input, [name]: value });
    }

    function onClick() {
        console.log("onClick 이 호출되었습니다.");
        setUsers([...users, { id: nextId.current++, name, email, active: false }]);
    }

    function onToggle(userId) {
        setUsers(
            users.map((user) => {
                return userId === user.id ? { ...user, active: true } : user;
            })
        );
    }

    function onDelete(userId) {
        var newArr = [];
        users.map((user) => {
            return user.id !== userId ? newArr.push(user) : "";
        });

        setUsers([...newArr]);
    }

    function countActiveUsers(users) {
        console.log("활성 사용자 수를 세는중...");
        var s = users.filter((user) => {
            return user.active == true;
        });
        return s.length;
    }
}

export default App;

 

아래 코드는 React.memo 함수를 사용하였습니다. 이렇게 React 컴포넌트 함수를 React.memo() 함수로 감싸주면 해당 컴포넌트 함수는 props 값이 변경되지 않는 한 다시 호출되지 않습니다.

 

아래 코드를 통해 React.memo() 를 확인해보세요. MyRoom.js

 

import React from "react";

function MyRoom({ room, on, OnToggle }) {
    console.log({ room, on });
    return (
        <div>
            <h2>
                {room} 현재 상태 : {on.toString()}
            </h2>
            <button onClick={() => OnToggle()}>{room} 버튼</button>
        </div>
    );
}

export default React.memo(MyRoom);

 

이렇게 한 후 실행하여 컴포넌트의 버튼을 클릭하면 아래와 같이 props 가 수정된 컴포넌트의 함수만 호출되기 때문에 성능을 최적화할 수 있습니다.

 

 

 

 

출처 : https://www.daleseo.com/react-hooks-use-callback/

 

React Hooks: useCallback 사용법

Engineering Blog by Dale Seo

www.daleseo.com

https://react.vlpt.us/basic/18-useCallback.html