본문 바로가기

블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기

[85일차]20210712 React hooks API useMemo useCallback

반응형

이번 주 배울 것 : 함수형 컴포넌트 hooks 5가지 (총 10가지 존재) 

useMemo

useCallback 

useReducer

useContext

useEffect

 

이전에 함수의 state를 선언할 때 쓰고있던 useState도 hooks API 중 하나였다.

 

 

 

이전 내용 복씁

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>REACT TIME</title>
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    
</head>

<body>

    <div id="root"></div>
    <script type="text/babel">

        class CounterClass extends React.Component{
            // constructor(props){
            //     super(props)
            //     this.state={
            //         number:0
            //     }
            // }           // babel 덕분에 위의 구문이 아래처럼 간단해진다 ! 

            state={
                number:0
            }

            render(){
                return(
                    <div>
                        <h1>{this.state.number}</h1>
                        <button>+1</button>
                        <button>-1</button>
                    </div>
                )
            }
        }

        ReactDOM.render(
            <CounterClass/>,
            document.querySelector('#root')
        )


    </script>
</body>

</html>

원래 위의 주석처리된 코드이지만 babel 덕분에 아래 stat={} 요렇게 간단히 가능했다. 

 

이제 버튼 클릭해서 number가 변하게 만들기 !! 

            state={
                number:0
            }

            handleClick1 = ()=>{
                this.setState({
                    number: this.state.number +=1
                })
            }

            handleClick2 =()=>{
                this.setState({
                    number: this.state.number -=1
                })
            }

            render(){
                return(
                    <div>
                        <h1>{this.state.number}</h1>
                        <button onClick={this.handleClick1}>+1</button>
                        <button onClick={this.handleClick2}>-1</button>
                    </div>
                )
            }
        }

 

 

위의 내용을 함수로 만들기 

        const CounterFn = ()=>{
            const [number, setNumber] = React.useState(0)

            const up = () =>{
                setNumber(number+1)
            }

            const down = () =>{
                setNumber(number-1)
            }

            return(
                <div>
                    <h1>{number}</h1>
                    <button onClick={up}>+1</button>
                    <button onClick={down}>-1</button>
                </div>
            )
        }



        ReactDOM.render(
            <CounterFn/>,
            document.querySelector('#root')
        )

userState(0)요 부분이 왜 이렇게 낯설지,,,,,,,,,,,,,

 

함수형은 setState가 바뀌면 전체가 다시 render 되고 클래스는 render() 부분만 render가 되어서 함수형은 많은 연산을 포함하게 되면 느릴수도 있다. (성능저하) ---> 요거를 커버하는 기술을 오늘 배울 예정 

 

클래스 렌더되는 시점 쳌

        class Counterclass extends React.Component{
            constructor(props){ // CounterFn 가 실행되면 constructor 가 실행됨 
                super(props)
                this.state={  // number : 0 세팅이 됨 
                    number:0
                }
                console.log('counterclass')
            }           // babel 덕분에 위의 구문이 아래처럼 간단해진다 ! 



            handleClick1 = ()=>{
                this.setState({
                    number: this.state.number +=1
                })
            }

            handleClick2 =()=>{
                this.setState({
                    number: this.state.number -=1
                })
            }

            render(){
                return(
                    <div>
                        {console.log('rendering')}
                        <h1>{this.state.number}</h1>
                        <button onClick={this.handleClick1}>+1</button>
                        <button onClick={this.handleClick2}>-1</button>
                    </div>
                )
            }
        }

        const CounterF

setState 가 되어 render될 때 render안의 구문만 다시 실행된다 ---> 'rendering' 만 계속 찍힘 

 

 

 

함수형 렌더되는 시점 쳌 

        const CounterFn = ()=>{
            const [number, setNumber] = React.useState(0)

            const up = () =>{
                setNumber(number+1)
            }

            const down = () =>{
                setNumber(number-1)
            }

            console.log('CounterFn')
            return(
                <div>
                    {console.log('rendering')}
                    <h1>{number}</h1>
                    <button onClick={up}>+1</button>
                    <button onClick={down}>-1</button>
                </div>
            )
        }

함수형은 setState될 때 CounterFn 안의 전체 코드를 render 한다 !  -> 'counterFn', 'rendering' 둘 다 찍힘 

 

 

함수형을 아래처럼 바꿔보기 

        const CounterFn = ()=>{
            const [number, setNumber] = React.useState(0)

            const up = () =>{
                setNumber((prevNumber)=>prevNumber+1)
            }

            const down = () =>{
                setNumber((prevNumber)=>prevNumber-1)
            }

            console.log('CounterFn')
            return(
                <div>
                    {console.log('rendering')}
                    <h1>{number}</h1>
                    <button onClick={up}>+1</button>
                    <button onClick={down}>-1</button>
                </div>
            )
        }

prevNumber 인자값은 아무거나 쓸 수 있다 - 이전에 대한 상태값을 인자값으로 받을 때의 코드 ! 

위의 코드와 똑같이 +1  -1 이 된다. 

 

 


 

 

Memoization

 

 

 

Momoization 메모이제이션
(memoization)은 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다. 동적 계획법의 핵심이 되는 기술이다.

메모이제이션을 이해해야 use를 알 수 있다!

 

 

 

 

피..피보나치 수열... ?

피보나치 순열 

첫 번째 , 두 번째 항은 무조건 1 ! -> 이후 더하기 

아래 설명을 보니 이해가 되었다. 

출처: 위키백과

 

 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Memoization</title>
    <script type="text/javascript">
        function fibo(n){
            if(n==1)return 1;
            if(n==2)return 1;
            
            return fibo(n-2) + fibo(n-1);
        }
    </script>
</head>

<body>

    <div id="root"></div>
    <script type="text/babel">

    </script>
</body>

</html>

---> 재귀함수로 사용한 피보나치 수열 

위처럼 계속 콜백 지옥..

문제점 : 수가 조금이라도 크면 (ex.50) 연산이 느어무 오래걸림 

n 이 4인 경우부터 계속 콜백함수로 불려와서 더해진다. 

 

 

 

 

 

--> 메모이제이션  : 한 번 연산했던 이전의 값을 저장해 두었다가 재사용 할 수 있도록 만듬 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Memoization</title>
    <script type="text/javascript">
        let memo = {}       // memo 라는 공간 설정

        function fibo(n){
            let result;

            if(n in memo){  // 계산한 적이 있다면 
                result = memo[n]
            }else{  // 아직 계산한 적이 없다면 
                if(n==1 || n==2){
                    result = 1
                }else{
                    result= fibo(n-2) + fibo(n-1)
                }
                // 새로 계산한 것을 memo에 넣기 
                memo[n] = result;
            }
            
            return result;
        }
    </script>
</head>
<body>
</body>
</html>

-> 큰 수도 연산이 잘 된다 ! 

 

 

 

 

 

 

 

 


 

 

 

 

Memoization 만들어보기  !

 

1. 웹팩 기본 설정하기 


https://blckchainetc.tistory.com/255

 

[79일차 복습] 웹팩 CRA 없이 React 개발 환경 구축 및 핫리로드 & CRA 사용버전

Webpack, 웹팩이란 ? -> https://blckchainetc.tistory.com/253 웹 애플리케이션의 빠른 로딩을 " data-og-host="blckchainetc.tistory.com" data-og-source-url="https://blckchainetc.tistory.com/253" data-og..

blckchainetc.tistory.com

 

 

 

2. memo 폴더 - memo.jsx 파일 생성  /  코드 작성  / App과 연결 

memo.jsx

import React, {useState} from 'react';

const Memo = () =>{
    // 두 개의 상태값 : 1. input 1. 내용 저장
    
    const [username,setUsername] = useState('')
    const [list,setList] = useState([])
    

    return(
        <>
            <h2>회원리스트(0)</h2>
            <form>
                <input type="text" name="username" />
                <button type="submit">
                    추가
                </button>
            </form>
            <ol>
                <li>emily1</li>
                <li>emily2</li>
                <li>emily3</li>
            </ol>
        </>
    )
}

export default Memo

App.jsx

import React from 'react';
import Memo from './memo/memo';


const App = () => {
    return(
        <>
            <Memo />
        </>
    )
}

export default App

 

 

3. 내용 쓰고 추가 누르면 리스트 추가 하도록 만들기

memo.jsx 

import React, {useState} from 'react';

const Memo = () =>{
    // 두 개의 상태값 : 1. input 1. 내용 저장
    
    const [username,setUsername] = useState('')
    const [list,setList] = useState([])
    

    const change = (e) =>{
        let {value} = {...e.target}
        setUsername(value)
    }

    return(
        <>
            <h2>회원리스트(0)</h2>
            <form>
                <input 
                    type="text" 
                    name="username" 
                    onChange={change}
                />
                <button type="submit">
                    추가
                </button>
            </form>
            <ol>
                <li>emily1</li>
                <li>emily2</li>
                <li>emily3</li>
            </ol>
        </>
    )
}

export default Memo

state 바뀌는 것 확인 ↓↓

4. submit 함수 추가 / 작동 확인 

memo.jsx 

import React, {useState} from 'react';

const Memo = () =>{
    // 두 개의 상태값 : 1. input 1. 내용 저장
    
    const [username,setUsername] = useState('')
    const [list,setList] = useState([])
    

    const change = (e) =>{
        let {value} = {...e.target}
        setUsername(value)
    }

    const submit = (e) =>{
        e.preventDefault()
        console.log('submit 잘 동작함니다.')
    }

    return(
        <>
            <h2>회원리스트(0)</h2>
            <form onSubmit={submit}>
                <input 
                    type="text" 
                    name="username" 
                    value={username}
                    onChange={change}
                />
                <button type="submit">
                    추가
                </button>
            </form>
            <ol>
                <li>emily1</li>
                <li>emily2</li>
                <li>emily3</li>
            </ol>
        </>
    )
}

export default Memo

 

 

얕은 복사하기 

slice() or [...list] 로 ! 

import React, {useState} from 'react';

const Memo = () =>{
    // 두 개의 상태값 : 1. input 1. 내용 저장
    
    const [username,setUsername] = useState('')
    const [list,setList] = useState([])
    

    const change = (e) =>{
        let {value} = {...e.target}
        setUsername(value)
    }

    const submit = (e) =>{
        e.preventDefault()
        let newList = [...list]    //==list.slice()
        newList.push(username)
        setList(newList)
        setUsername('')
    }

    return(
        <>
            <h2>회원리스트(0)</h2>
            <form onSubmit={submit}>
                <input 
                    type="text" 
                    name="username" 
                    value={username}
                    onChange={change}
                />
                <button type="submit">
                    추가
                </button>
            </form>
            <ol>
                <li>emily1</li>
                <li>emily2</li>
                <li>emily3</li>
            </ol>
        </>
    )
}

export default Memo

-> 브라우저에서 input box에 입력하고 '추가'누르면 input box clear 

 

 

 

 

*** 

오류 : key값 넣어달라고함 

 

** 배열에 담고만 있어도 list 형태로 jsx 가 알아서 return 해준다 ! 

 

=> 그럼 코드로 만들 때 값을 배열로 만들어서 return 만 해주면 됨

 

memo.jsx

import React, {useState} from 'react';

const Memo = () =>{
    // 두 개의 상태값 : 1. input 1. 내용 저장
    
    const [username,setUsername] = useState('')
    const [list,setList] = useState([])
    

    const change = (e) =>{
        let {value} = {...e.target}
        setUsername(value)
    }

    const submit = (e) =>{
        e.preventDefault()
        let newList = [...list]    //==list.slice()
        newList.push(username)
        setList(newList)
        setUsername('')
    }

    const renderList=()=>{
        let newArr = [];
        for (let i=0; i<list.length; i++){
            newArr.push(<li key={i}>{list[i]}</li>)
        }
        return newArr
    }

    return(
        <>
            <h2>회원리스트(0)</h2>
            <form onSubmit={submit}>
                <input 
                    type="text" 
                    name="username" 
                    value={username}
                    onChange={change}
                />
                <button type="submit">
                    추가
                </button>
            </form>
            <ol>
                {renderList()}
            </ol>
        </>
    )
}

export default Memo

 

내용 추가하면 바로바로 나온다 ! 

 

위의 코드

1. for문 

        let newArr = [];
        for (let i=0; i<list.length; i++){
            newArr.push(<li key={i}>{list[i]}</li>)
        }
        return newArr

2. map 

        let newArr = list.map((v,k)=>{
            return <li key={k}>{v}</li>
        })
        return newArr

3. map 바로 return 

        return (
            list.map((v, k) => {
                return <li key={k}>{v}</li>
            })
        )

 

여기까지 전체 코드 및 설명

import React, { useState } from 'react';

const Memo = () => {
    // 두 개의 상태값 : 1. input 1. 내용 저장

    const [username, setUsername] = useState('')
    const [list, setList] = useState([])

    const change = (e) => {
        let { value } = { ...e.target }  // 2. value 에 inputbox의 입력한 내용 담기
        setUsername(value)    // 3. state에 저장 
    }

    const submit = (e) => {
        e.preventDefault()
        let newList = [...list]    // (2) newList 에 state의 현재 list 값 담기 
        newList.push(username)     // (3) newList 에 state의 username 담기 (input box에서 입력한 값)
        setList(newList)           // (4) newList 수정하기 
        setUsername('')            // (5) state.username의 값 (inputbox에 담은값) reset하기 
    }

    const renderList = () => {
        let newArr = list.map((v,k)=>{   // 2222 list의 각 원소에 <li>요소, key값 추가한 새로운 배열 담기
            return <li key={k}>{v}</li>
        })
        return newArr                  //  333 해당 새로운 배열 newArr return 
    }

    return (
        <>
            <h2>회원리스트(0)</h2>
            <form onSubmit={submit}>   {/* (1) submit 클릭  */}
                <input
                    type="text"
                    name="username"
                    value={username}     // 4. state에 저장한 value으로 변경 
                    onChange={change}   // 1. inputbox에 회원명 입력 
                />
                <button type="submit">
                    추가
                </button>
            </form>
            <ol>
                {renderList()}  {/* 1111) renderList함수 실행  */}
            </ol>
        </>
    )
}

export default Memo

 

 

 

5. 회원리스트(0) -> 요 숫자 카운팅 함수 만들기 


    const count = () =>{
        return list.length
    }


    return (
        <>
            <h2>회원리스트({count})</h2>
            <form onSubmit={submit}>   {/* (1) submit 클릭  */}

 

 

 


 

 

 

use memo

6. 이제 memorization !

위의 코드 불필요한 부분  -> 모든 함수가 사용될 때 rerender되는 부분이 비효율적 

특히 change 부분 변화가 있을 때마다 inputbox에 글을 한 글자라도 쓸 때마다 render 됨 

 

memo.jx

import React, { useState, useMemo } from 'react';

 

    const count = () =>{
        return list.length       // 삭제
    }

    const userCount = useMemo(()=>{
        console.log(33)
        return list.length
    }, [list.length])


    return (
        <>
            <h2>회원리스트({userCount})</h2>
            <form onSubmit={submit}>  
                <input
                    type="text"

언제 console.log가 찍히는지 보기 ! 

-> 글을 쓰는 중에는 안찍히고 '추가' 버튼을 눌렀을 떄 console찍힘

 

위의 코드 설명 


    const userCount = useMemo(()=>{   //useMemo 첫번째 인자값 : 함수  , 두 번째 인자값 : 배열 (상태값) 
        console.log(33)
        return list.length  // 최종적으로 list.length를 userCount에 담기 
    }, [list.length])      // list의 길이가 변할 떄만 ! return 하도록 
    // 내가 userCount에 넣은 값과 상태값(두 번째 인자값인) list.length와 다를 경우에만 ~ 요 매서드를 실행하겠다. 


    return (
        <>
            <h2>회원리스트({userCount})</h2>
            <form onSubmit={submit}>

 

메모리에 저장하는 방법 ---> 메모리도 제한이 있기 떄문에 막 쓰면 안됨 !   ( = 자원의 교환 )

ex) 함수 컴포넌트 쓰니깐 다 useMemo 써서 저장해야징~ -> X

 

클래스의 경우 (pure Component  == 함수의 useMemo)

 

 

 


 

 

useCallback

 

 

 

useCallback 과 useMemo의 차이 : 

useCallback 함수를 memoiztion 할 때 

useMemo는 변수의 값을 memorzation 사용할 때  (return 값이 있는 것들) 

 

 

 

7. useCallback 가져오기 (useMemo처럼!) 

import React, { useState, useMemo, useCallback } from 'react';

 

8. chang 함수를 useCallback으로 바꿔보기

    const change = (e) => {
        let { value } = { ...e.target }  /
        setUsername(value)  
    }

    const change = useCallback((e)=>{    // Memo 함수 Component가 처음 실행될 때 heaq에 담아짐
        let {value} = {...e.target}
        setUsername(value)
    },[])

요 것도 너무 많이 쓰면 성능 저하가 올 수 있다. sytyle component를 만들 때 쓰는게 좋을 듯 ! style 바뀔 때 

[] 빈 배열 - 최초에 component를 실행할 때 change 함수를 생성하겠다. 라는 뜼 

 

 

submit 변경

    // const submit = (e) => {
        // e.preventDefault()
        // let newList = [...list]  
        // newList.push(username)   
        // setList(newList)      
        // setUsername('')         
    // }

    const submit = useCallback((e)=>{
        e.preventDefault()
        let newList = [...list]  
        newList.push(username)   
        setList(newList)      
        setUsername('')  
    },[list,userName])  // submit 함수는 list와 usename 두 가지를 변경 함

함수 submit에서 바꾸는 내용은 list, username이 있다. []안에 두개 다 쓰기 

list와 username 상태가 바뀔 때만 함수를 재생성한다 ---> 효율적 (남발하면 메모리에 무리가 감)

 

 

renderList

    const renderList = useCallback(()=>{
        let newArr = list.map((v,k)=>{
            return <li key = {k}>{v}</li>
        })
        return newArr
    }, [list])

 

 

useCallback 은 몰라도 상관없지만 최적화를 하기위한 도구이다 ! 필요한 공간이 있을 떄가 있을 때 ! 

100% 필요한 것은 아니라고 함,,,,

 

 

 

과제 : 위의 전체 완성 코드 안보고 쓰기 

반응형