Frontend ๐Ÿ“š/React

[React] ๋ฆฌ์•กํŠธ ๊ธฐ์ˆ  ๋ฉด์ ‘ ํ•ธ๋“œ๋ถ IV - ์ปดํฌ๋„ŒํŠธ ๋ผ์ดํ”„ ์‚ฌ์ดํด, Hooks etc.

leejaejae 2024. 7. 4. 18:45

1. ์ปดํฌ๋„ŒํŠธ์˜ ๋ผ์ดํ”„ ์‚ฌ์ดํด ๋ฉ”์„œ๋“œ
2. Hooks์˜ ์ข…๋ฅ˜
3. useMemo vs. useCallBack
4. setState๋Š” ๋น„๋™๊ธฐ


1. ์ปดํฌ๋„ŒํŠธ์˜ ๋ผ์ดํ”„ ์‚ฌ์ดํด ๋ฉ”์„œ๋“œ โญ๏ธโญ๏ธ

- will~ ๋ฉ”์„œ๋“œ: ์–ด๋–ค ์ž‘์—…์„ ์ž‘๋™ํ•˜๊ธฐ ์ „์— ์‹คํ–‰๋˜๋Š” ๋ฉ”์„œ๋“œ
- Did~ ๋ฉ”์„œ๋“œ: ์–ด๋–ค ์ž‘์—…์„ ์ž‘๋™ํ•œ ํ›„์— ์‹คํ–‰๋˜๋Š” ๋ฉ”์„œ๋“œ

  • ๋ฉ”์„œ๋“œ๋Š” ์ปดํฌ๋„ŒํŠธ ํด๋ž˜์Šค์—์„œ ๋ฎ์–ด ์จ ์„ ์–ธํ•จ์œผ๋กœ์จ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
  • ๋ผ์ดํ”„์‚ฌ์ดํด์€ ์ด ์„ธ ๊ฐ€์ง€, ์ฆ‰ ๋งˆ์šดํŠธ, ์—…๋ฐ์ดํŠธ, ์–ธ๋งˆ์šดํŠธ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋‚˜๋ˆ”

  • ๋งˆ์šดํŠธ โญ๏ธ
    • DOM์ด ์ƒ์„ฑ๋˜๊ณ  ์›น ๋ธŒ๋ผ์šฐ์ €์ƒ์— ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์„ ๋งˆ์šดํŠธ๋ผ๊ณ  ํ•จ
    • componentDidMount: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์›น ๋ธŒ๋ผ์šฐ์ €์ƒ์— ๋‚˜ํƒ€๋‚œ ํ›„ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ
  • ์—…๋ฐ์ดํŠธ โญ๏ธ
    • ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋„ค ๊ฐ€์ง€ ๊ฒฝ์šฐ ์—…๋ฐ์ดํŠธ
      • Props๊ฐ€ ๋ฐ”๋€” ๋•Œ
      • state๊ฐ€ ๋ฐ”๋€” ๋•Œ
      • ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ
      • this.forceUpdate๋กœ ๊ฐ•์ œ๋กœ ๋ Œ๋”๋ง์„ ํŠธ๋ฆฌ๊ฑฐํ•  ๋•Œ
    • componentDidUpdate: ์ปดํฌ๋„ŒํŠธ์˜ ์—…๋ฐ์ดํŠธ(๋ฆฌ๋ Œ๋”๋ง) ์ž‘์—…์ด ๋๋‚œ ํ›„ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ
  • ์–ธ๋งˆ์šดํŠธ โญ๏ธ
    • ๋งˆ์šดํŠธ์˜ ๋ฐ˜๋Œ€ ๊ณผ์ •, ์ฆ‰ ์ปดํฌ๋„ŒํŠธ๋ฅผ DOM์—์„œ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ
    • componentWillUnmount: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ๋ผ์ง€๊ธฐ ์ „์— ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ

 

2. Hooks์˜ ์ข…๋ฅ˜ โญ๏ธโญ๏ธ

  • Hooks๋Š” ๋ฆฌ์•กํŠธ ๋ฒ„์ „ 16.8์— ์ƒˆ๋กœ ๋„์ž…๋œ ๊ธฐ๋Šฅ์œผ๋กœ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” useState ๋ Œ๋”๋ง ์งํ›„ ์ž‘์—…์„ ์„ค์ •ํ•˜๋Š” useEffect ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ ๊ธฐ์กด์˜ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•  ์ˆ˜ ์—†์—ˆ๋˜ ๋‹ค์–‘ํ•œ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ

  • useState
    • ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ Hook, ์ฒซ ๋ฒˆ์งธ ์›์†Œ๋Š” ์ƒํƒœ ๊ฐ’, ๋‘ ๋ฒˆ์งธ ์›์†Œ๋Š” ์ƒํƒœ๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜
    • ์ด ํ•จ์ˆ˜์— ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋„ฃ์–ด์„œ ํ˜ธ์ถœํ•˜๋ฉด ์ „๋‹ฌ๋ฐ›์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ’์ด ๋ฐ”๋€Œ๊ณ  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ฆฌ๋ Œ๋”๋ง ๋จ
import { useState } from 'react';

const [value, setValue] = useState(0);
/* useState ๋‚ด๋ถ€์˜ ๋ชจ์Šต์„ ๊ตฌํ˜„ํ•œ ๋ชจ์Šต */

const MyReact = (function () {
  const global = {};
  let index = 0;

  function useState(initialState) {
    if (!global.states) {
      // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์˜ states ๋ฐฐ์—ด์„ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
      // ์ตœ์ดˆ ์ ‘๊ทผ์ด๋ผ๋ฉด ๋นˆ ๋ฐฐ์—ด๋กœ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
      global.states = [];
    }

    // states ์ •๋ณด๋ฅผ ์กฐํšŒํ•ด์„œ ํ˜„์žฌ ์ƒํƒœ๊ฐ’์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์—†๋‹ค๋ฉด ์ดˆ๊นƒ๊ฐ’์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.
    const currentState = global.states[index] || initialState;
    // states์˜ ๊ฐ’์„ ์œ„์—์„œ ์กฐํšŒํ•œ ํ˜„์žฌ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.
    global.states[index] = currentState;

    // ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜๋กœ setter๋ฅผ ๋งŒ๋“ ๋‹ค.
    const setState = (function () {
      // ํ˜„์žฌ index๋ฅผ ํด๋กœ์ €๋กœ ๊ฐ€๋‘ฌ๋†”์„œ ์ดํ›„์—๋„ ๊ณ„์†ํ•ด์„œ ๋™์ธํ•œ index์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
      let currentIndex = index;
      return function (value) {
        global.states[currentIndex] = value;
        // ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค. ์‹ค์ œ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์ฝ”๋“œ๋Š” ์ƒ๋žตํ–ˆ๋‹ค.
      };
    })();

    /**
     * useState๋ฅผ ์“ธ ๋•Œ๋งˆ๋‹ค index๋ฅผ ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ด index๋Š” setState์—์„œ ์‚ฌ์šฉ๋œ๋‹ค.
     * ์ฆ‰, ํ•˜๋‚˜์˜ state๋งˆ๋‹ค index๊ฐ€ ํ• ๋‹น๋ผ ์žˆ์–ด ๊ทธ index๊ฐ€ ๋ฐฐ์—ด์˜ ๊ฐ’(global.states)์„ ๊ฐ€๋ฆฌํ‚ค๊ณ  ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ๊ทธ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ฒŒ ํ•œ๋‹ค.
     **/

    index = index + 1;

    return [currentState, setState];
  }

  function Component() {
    const [value, setValue] = useState(0);

    //...
    return <div>{value}</div>;
  }
})();

 

  • useEffect
    • ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” Hook
    • ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ์˜ componentDidMount์™€ componentDidUpdate๋ฅผ ํ•ฉ์นœ ํ˜•ํƒœ๋กœ ๋ณด์•„๋„ ๋ฌด๋ฐฉ
    • useEffect๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ Œ๋”๋ง๋˜๊ณ ๋‚œ ์งํ›„๋งˆ๋‹ค ์‹คํ–‰๋˜๋ฉฐ ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐฐ์—ด์— ๋ฌด์—‡์„ ๋„ฃ๋Š”์ง€์— ๋”ฐ๋ผ ์‹คํ–‰๋˜๋Š” ์กฐ๊ฑด์ด ๋‹ฌ๋ผ์ง
    • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ ์ „์ด๋‚˜ ์—…๋ฐ์ดํŠธ๋˜๊ธฐ ์ง์ „์— ์–ด๋– ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด useEffect์—์„œ ๋’ท์ •๋ฆฌ(cleanup)ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ด ์ฃผ์–ด์•ผ ํ•จ
/* ๋งˆ์šดํŠธ๋งŒ ์‹คํ–‰์‹œํ‚ค๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ: ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋น„์–ด์žˆ๋Š” ๋ฐฐ์—ด์„ ๋„ฃ์–ด์ฃผ๋ฉด ๋จ */
useEffect(() => {
  console.log('๋งˆ์šดํŠธ๋  ๋•Œ๋งŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.');
}, []);


/* ํŠน์ • ๊ฐ’์ด ์—…๋ฐ์ดํŠธ๋  ๋•Œ๋งŒ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ: ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋˜๋Š” ๋ฐฐ์—ด ์•ˆ์— ๊ฒ€์‚ฌํ•˜๊ณ  ์‹ถ์€ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋ฉด ๋จ */
useEffect(() => {
  console.log(name);
}, [name]);


/* ๋’ท์ •๋ฆฌ(cleanup)์ฝ”๋“œ */
useEffect(() => {
    console.log("๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!");
    console.log("none clean up: ", { name, nickname });
    return () => {
      console.log("clean up!");
      console.log("clean up name: ", { name });
    };
  }

 

  • useReducer
    • useState๋ณด๋‹ค ๋” ๋‹ค์–‘ํ•œ ์ปดํฌ๋„ŒํŠธ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ์ƒํƒœ๋ฅผ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•ด ์ฃผ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” Hook
    • ํ˜„์žฌ ์ƒํƒœ, ๊ทธ๋ฆฌ๊ณ  ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋‹ด์€ ์•ก์…˜ ๊ฐ’์„ ์ „๋‹ฌ๋ฐ›์•„ ์ƒˆ๋กœ์šด ์ƒํƒœ์— ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
    • ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜์—์„œ ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ๋ถˆ๋ณ€์„ฑ์„ ์ง€์ผœ์ค˜์•ผ ํ•จ

    • ์ฆ‰, ๋ณต์žกํ•œ ํ˜•ํƒœ์˜ state๋ฅผ ์‚ฌ์ „์— ์ •์˜๋œ dispatcher๋กœ๋งŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด ์คŒ์œผ๋กœ์จ state ๊ฐ’์— ๋Œ€ํ•œ ์ ‘๊ทผ์€ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ณ , ์ด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ƒ์„ธ ์ •์˜๋Š” ์ปดํฌ๋„ŒํŠธ ๋ฐ–์— ๋‘” ๋‹ค์Œ, state์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด ๋‘” dispatcher๋กœ๋งŒ ์ œํ•œํ•˜๋Š” ๊ฒƒ
    • state์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ œํ•œ์ ์œผ๋กœ ๋‘๊ณ  ์ด์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์„ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ !
type State = {
  count: number,
};

type Action = { type: 'up' | 'down' | 'reset', payload?: State };

function init(count: State): State {
  return count;
}

const initialState: State = { count: 0 };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'up':
      return { count: state.count + 1 };
    case 'down':
      return { count: state.count - 1 };
    case 'reset':
      return init(action.payload || { count: 0 });
    default:
      throw new Error(`Unexpected action type ${action.type}`);
  }
}

export default function App() {
  const [state, dispatcher] = useReducer(reducer, initialState, init);

  function handleUpButtonClick() {
    dispatcher({ type: 'up' });
  }

  function handleDownButtonClick() {
    dispatcher({ type: 'down' });
  }

  function handleResetButtonClick() {
    dispatcher({ type: 'reset', payload: { count: 1 } });
  }

  return (
    <div>
      <h1>{state.count}</h1>
      <button onClick={handleDownButtonClick}>+</button>
      <button onClick={handleDownButtonClick}>-</button>
      <button onClick={handleResetButtonClick}>reset</button>
    </div>
  );
}

 

  • useMemo
    • ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—ฐ์‚ฐ ์ตœ์ ํ™”
    • ๋ Œ๋”๋งํ•˜๋Š” ๊ณผ์ •์—์„œ ํŠน์ • ๊ฐ’์ด ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ๋งŒ ์—ฐ์‚ฐ์„ ์‹คํ–‰ํ•˜๊ณ , ์›ํ•˜๋Š” ๊ฐ’์ด ๋ฐ”๋€Œ์ง€ ์•Š์•˜๋”๋ฉด ์ด์ „์— ์—ฐ์‚ฐํ–ˆ๋˜ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹

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

const getAverage = (numbers) => {
  console.log('ํ‰๊ท ๊ฐ’ ๊ณ„์‚ฐ ์ค‘..');
  if (numbers.length === 0) return 0;
  const sum = numbers.reduce((a, b) => a + b);
  return sum / numbers.length;
};

const Average = () => {
  const [list, setList] = useState([]);
  const [number, setNumber] = useState('');
  const inputEl = useRef();

  const onChange = useCallback((e) => {
    setNumber(e.target.value);
  }, []); // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋ Œ๋”๋ง๋  ๋•Œ๋งŒ ํ•จ์ˆ˜ ์ƒ์„ฑ

  const onInsert = useCallback(
    (e) => {
      const nextList = list.concat(parseInt(number));
      setList(nextList);
      setNumber('');
      inputEl.current.focus();
    },
    [number, list] // number ํ˜น์€ list๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ๋งŒ ํ•จ์ˆ˜ ์ƒ์„ฑ
  );

  const avg = useMemo(() => getAverage(list), [list]);

  return (
    <div>
      <input value={number} onChange={onChange} ref={inputEl} />
      <button onClick={onInsert}>๋“ฑ๋ก</button>
      <ul>
        {list.map((value, index) => (
          <li key={index}>{value}</li>
        ))}
      </ul>
      <div>
        <b>ํ‰๊ท ๊ฐ’:</b> {avg}
      </div>
    </div>
  );
};

export default Average;

 

  • useCallback
    • useMemo์™€ ์ƒ๋‹นํžˆ ๋น„์Šทํ•œ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค์–ด๋†จ๋˜ ํ•จ์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
    • ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ์ƒ์„ฑํ•˜๊ณ  ์‹ถ์€ ํ•จ์ˆ˜๋ฅผ ๋„ฃ๊ณ , ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ๋ฐฐ์—ด์„ ๋„ฃ์Œ, ์ด ๋ฐฐ์—ด์—๋Š” ์–ด๋–ค ๊ฐ’์ด ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š”์ง€ ๋ช…์‹œํ•ด์•ผ ํ•จ
    • ์ฃผ๋กœ ๋ Œ๋”๋ง ์„ฑ๋Šฅ์„ ์ตœ์ ํ™” ํ•ด์•ผํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉ
const onChange = useCallback((e) => {
  setNumber(e.target.value);
}, []); // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋ Œ๋”๋ง๋  ๋•Œ๋งŒ ํ•จ์ˆ˜ ์ƒ์„ฑ


const onInsert = useCallback(
  (e) => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber("");
  },
  [number, list] // number ํ˜น์€ list๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ๋งŒ ํ•จ์ˆ˜ ์ƒ์„ฑ
  /* ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ƒํƒœ ๊ฐ’์— ์˜์กดํ•ด์•ผ ํ•  ๋•Œ๋Š” ๊ทธ ๊ฐ’์„ ๋ฐ˜๋“œ์‹œ ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ ์•ˆ์— ํฌํ•จ์‹œ์ผœ ์ฃผ์–ด์•ผ ํ•จ */
);


useMemo(() => {
  const fn = () => {
    console.log(‘hello world!‘);
  };
  return fn;
}, [])

 

  • useRef
    • ๋ฐ”๋‹๋ผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ DOM ์š”์†Œ๋ฅผ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด querySelector๋‚˜ getElementById ๋“ฑ์„ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด, ๋ฆฌ์•กํŠธ์—์„œ๋Š” useRef ํ›… ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•จ
    • .current ํ”„๋กœํผํ‹ฐ์— ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๊ฐ’์„ ๋‹ด๊ณ  ์žˆ๋Š” ๊ฐ์ฒด์ž„
    • .current ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋”๋ผ๋„ ๋ฆฌ๋ Œ๋”๋ง์„ ์œ ๋ฐœํ•˜์ง€ ์•Š์Œ. ref ๊ฐ์ฒด ์•ˆ์˜ ๊ฐ’์€ ๋ฆฌ์•กํŠธ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋…๋ฆฝ์ ์ด๊ธฐ ๋•Œ๋ฌธ
import React, { useRef, Component } from 'react';

class RefSample extends Component {
  input = useRef();

  handleFocus = () => {
    this.input.current.focus();
  };

  render() {
    return (
      <div>
        <input ref={input} />
      </div>
    );
  }
}

export default RefSample;
  • Q) ์–ธ์ œ useState ๋Œ€์‹  useRef๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‚˜์š”?
  • A) useRef๋Š” ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์žฌ๋ Œ๋”๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๋Š” ๊ฐ’์„ ์ €์žฅํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•ด์•ผ ํ•จ. ๋ฐ˜๋ฉด, useState๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ๋‹ค์‹œ ๋ Œ๋”๋ง์„ ํŠธ๋ฆฌ๊ฑฐํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์„ ์ €์žฅํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉ.

    ๋”ฐ๋ผ์„œ ํ™”๋ฉด์˜ ์ผ๋ถ€ ๋‚ด์šฉ์„ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ๋ Œ๋”๋ง์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์•™๋„ ๋˜๋Š” ๊ฒฝ์šฐ์— useRef๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ฆฌ๋ Œ๋”๋ง์„ ์กฐ๊ธˆ ๋” ํšจ์œจ์ ์œผ๋กœ ํ•ธ๋“ค๋ง ํ•  ์ˆ˜ ์žˆ์Œ.

 

3. useMemo vs. useCallBack โญ๏ธโญ๏ธ

  • useMemo ํ•จ์ˆ˜๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜
  • useCallBack ํ•จ์ˆ˜๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜
    • Memoization(๋ฉ”๋ชจ์ด์ œ์ด์…˜): ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์‹œ ์ด์ „์— ๊ณ„์‚ฐํ•œ ๊ฐ’์„ ์ €์žฅํ•จ. ์›ํ•˜์ง€ ๊ฐ’์ด ๋ฐ”๋€Œ์ง€ ์•Š์•˜๋‹ค๋ฉด ์ด์ „์— ์—ฐ์‚ฐํ–ˆ๋˜ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ์‚ฌ์šฉ 
  • ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ ๋ฆฌ๋ Œ๋”๋ง ๋˜๋Š”๋ฐ,
    1. props๊ฐ€ ๋ฐ”๋€” ๋•Œ
    2. state๊ฐ€ ๋ฐ”๋€” ๋•Œ
    3. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ
  • ์ด๋Ÿฐ ํŠน์ • ์ƒํ™ฉ(๊ฐ’, ๋˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ)์— ๋งž๊ฒŒ ๋ฆฌ๋ Œ๋”๋ง์ด ๋  ์ˆ˜ ์žˆ๋„๋ก useMemo์™€ useCallBack ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•จ

  • ์˜์กด์„ฑ ๋ฐฐ์—ด๋กœ ์–ด๋–ค ๊ฐ’์— ์˜ํ•ด ๋ฐ”๋€Œ๋Š” ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น ํ•จ์ˆ˜์˜ ๋‘ ๋ฒˆ์งธ ์ฝœ๋ฐฑ์ธ [] ๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ๊ฐ’์„ ๋ณด๋ฉด ๋จ
// useMemo
const avg = useMemo(() => getAverage(list), [list]); // list ๋ผ๋Š” ๊ฐ’์ด ๋ณ€๊ฒฐ๋  ๋•Œ'๋งŒ' getAverage ํ•จ์ˆ˜๋ฅผ ์žฌ ํ˜ธ์ถœํ•˜๋Š” ์šฉ๋„์ž…๋‹ˆ๋‹ค

// useCallback
const onInsert = useCallback(
  (e) => {
    const nextList = list.concat(parseInt(number));
    setList(nextList);
    setNumber('');
  },
  [number, list] // number ํ˜น์€ list๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ๋งŒ onInsert ํ•จ์ˆ˜ ์ƒ์„ฑ
);

// useMemo
<div>
  <b>ํ‰๊ท ๊ฐ’:</b> {avg}
</div>

// useCallback
<button onClick={onInsert}>๋“ฑ๋ก</button>
  • Q) useCallBack์„ ์‚ฌ์šฉํ•  ๋•Œ์™€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ๋Š” ์–ด๋–ค ์ฐจ์ด๊ฐ€ ์žˆ๋‚˜์š”?
  • A) useCallBack ํ›…ํ•จ์ˆ˜๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•˜๋Š” ๋ฆฌ์•กํŠธ์˜ ํ›…์ž„. ์ฆ‰, ์ข…์†์„ฑ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ์—๋งŒ ํ•จ์ˆ˜๊ฐ€ ๋‹ค์‹œ ์ƒ์„ฑ๋จ. ์ด ๊ธฐ๋Šฅ์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ž์ฃผ ๋ Œ๋”๋ง๋˜๊ณ  ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์žฌ์ƒ์„ฑํ•˜๋Š” ๋ฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ์ƒํ™ฉ์—์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”์— ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

    ์ด์™€ ๋ฐ˜๋Œ€๋กœ useCallBack์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•˜๋Š” ๊ฒฝ์šฐ ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์„ ์–ธํ•จ

    ํ•จ์ˆ˜ ์ดˆ๊ธฐํ™”(์„ ์–ธ, initialize) ๊ธฐ๋Šฅ๊ณผ ํ•จ๊ป˜ 'useCallBack' ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์—ฌ๋ถ€๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ์š”๊ตฌ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง

    ๊ธฐ๋Šฅ์ด ๋น„๊ต์  ๊ฐ„๋‹จํ•˜๊ณ  ์žฌํ˜„ ๋น„์šฉ์ด ๋งŽ์ด ๋“ค์ง€ ์•Š๋Š”๋‹ค๋ฉด 'useCallback'์„ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ์Œ
    ๊ทธ๋Ÿฌ๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ๋” ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ฒฝ์šฐ์—๋Š” ์ด ํ•จ์ˆ˜์™€ ํ•จ๊ป˜ 'useCallBack'์„ ์‚ฌ์šฉํ•˜๋ฉด ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Œ

 

4. ๋ฆฌ์•กํŠธ์—์„œ setState๋Š” ๋™๊ธฐ์ธ๊ฐ€ ๋น„๋™๊ธฐ์ธ๊ฐ€ โญ๏ธ

function Example() {
  const [count, setCount] = React.useState(0);

  useEffect(()=>{
    console.log('in useEffect:', count)
  },[count])

  const onClickButton = () => {
    console.log("before", count);

    setCount(count + 1);
    // setCount(prev => prev+1);

    console.log("after", count);
  };

  return <button onClick={onClickButton}>{count}</button>;
}

>>>
/*
  before 0
  after 0
  in useEffect 1
*/
  • state ๊ฐ’์„ ๊ฐฑ์‹ ํ•˜๋Š” ํ•จ์ˆ˜์ธ setState๋Š” ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•˜๋Š” ํ›… ํ•จ์ˆ˜์ž„
  • ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์˜ ๋ฐœ์ƒ์„ ๋Œ€๋น„ํ•˜๊ธฐ ์œ„ํ•ด ๋ฆฌ์•กํŠธ๋Š” setState๋ฅผ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋กœ ์ฒ˜๋ฆฌํ•ด ์ปดํฌ๋„ŒํŠธ ๋‚ด์˜ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝœ๋ฐฑํ๊ฐ€ ๋‹ค ๋น„์›Œ์ง€๋ฉด ๋ฆฌ๋ Œ๋”๋งํ•˜๋„๋ก ์„ค๊ณ„
  • ์ฆ‰, ํ•ด๋‹น ํ•จ์ˆ˜ ๋‚ด์—์„œ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ชจ๋‘ ์‹คํ–‰๋œ ๋’ค์— ๋งˆ์ง€๋ง‰์— setState๋ฅผ ์ฒ˜๋ฆฌ
const onClickButton = () => {
  console.log('before', count); // ----> ๋™๊ธฐํ•จ์ˆ˜

  setCount(count + 1); // ---->  ๋น„๋™๊ธฐํ•จ์ˆ˜

  console.log('after', count); // ---->  ๋™๊ธฐํ•จ์ˆ˜
};
  • Q) setState๊ฐ€ ๋น„๋™๊ธฐ ๋™์ž‘์„ ์ทจํ–ˆ์„ ๋•Œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ด์ ์€? โญ๏ธ
  • A) setState๊ฐ€ ๋น„๋™๊ธฐ ๋™์ž‘์„ ์ทจํ•จ์œผ๋กœ์จ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋‚ด์˜ ์‹คํ–‰์ปจํ…์ŠคํŠธ ์Šคํƒ์ด ๋Œ์•„๊ฐˆ ๋•Œ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜๋“ค์„ ๋ชจ๋‘ ๋™์ž‘ํ•œ ๋’ค์— ํ์— ์กด์žฌํ•˜๋Š” ๋น„๋™๊ธฐ์ ์ธ ํ•จ์ˆ˜๋“ค์„ ์ด๋ฒคํŠธ ๋ฃจํ”„์— ์˜ํ•ด ๊บผ๋‚ด์™€์„œ ์‹คํ–‰์‹œํ‚ด.

    setState๊ฐ€ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค๋ฉด, ํ•œ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ์กด์žฌํ•˜๋Š” ํ•จ์ˆ˜์— ์˜ํ•ด state ๊ฐ’์ด ์—ฐ์†์ ์œผ๋กœ ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ ๋ฆฌ์•กํŠธ์˜ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง ์กฐ๊ฑด์— ์˜ํ•ด ์ง€์†์ ์œผ๋กœ ๋ฆฌ๋ Œ๋”๋ง์ด ๋  ๊ฒƒ์ž„.