2024-03-14.md

๐Ÿก

DIL: ๋ชจ๋˜ ๋ฆฌ์•กํŠธ ๋”ฅ ๋‹ค์ด๋ธŒ, 2์ฃผ์ฐจ-4

์Šคํ„ฐ๋””: ์›”๊ฐ„ CS, https://github.com/monthly-cs/2024-03-modern-react-deep-dive
์˜ค๋Š˜ ์ง„ํ–‰: ๊ฐœ์ธ๊ณต๋ถ€


DIL-week2-4_2024-03-14

| DIL ์ฃผ์ฐจ | ๋ฒ”์œ„ | ๋‚ด์šฉ | ์˜ค๋Š˜์ฐจ ์ง„๋„ | | -------- | ------ | ------------------------------- | ----------- | | 2์ฃผ์ฐจ | 3, 5์žฅ | ๋ฆฌ์•กํŠธ ํ›…๊ณผ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ | 219p~228p |

์˜ค๋Š˜ ์ฝ์€ ๋‚ด์šฉ์„ markdown์œผ๋กœ ๊ฐ„๋‹จํžˆ ๋ฉ”๋ชจ
์ฝ์€ ์‹œ๊ฐ„: 11์‹œ~12์‹œ
๋ ˆํผ๋Ÿฐ์Šค

  • https://react.dev/reference/react/useReducer

React Hook ํŒŒํ—ค์น˜๊ธฐ

useContext

Context๋ž€?

  • props drilling์€? ๋ฒˆ๊ฑฐ๋กœ์›€

  • createContext ํ›„ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌ

    <Context.Provider value={{ foo: "bar " }}>
      <ChildComponent />
    </Context.Provider>
    
    ...
    export default function ChildComponent (){
      const value = useContext(Context);
      ...
    }
    

context๋ฅผ ๋ณ„๋„์˜ ์ฝ”๋“œ๋กœ ๊ฐ์‹ธ๊ธฐ

  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ฝ˜ํ…์ŠคํŠธ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์•„์„œ ์—๋Ÿฌ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ ์žˆ์Œ

  • ์ดˆ๊ธฐํ™” ์—ฌ๋ถ€ ํ™•์ธ

  • TS ํƒ€์ž… ์ถ”๋ก 

  • ์ƒ์œ„์— Provider๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์—๋„ ๋””๋ฒ„๊น…

    function useMyContext() {
      const context = useContext(MyContext);
      if (context === undefined) {
        throw new Error("Context Exception");
      }
      return context;
    }
    
    ...
    export default function ChildComponent (){
      const { foo } = useMyContext();
      ...
    }
    

useContext๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜์ 

  1. ์˜์กด์„ฑ: Provider์— ์˜์กดํ•˜์—ฌ ์žฌํ™œ์šฉ์ด ์–ด๋ ค์›Œ์ง„๋‹ค.
  2. ์ƒํƒœ๊ด€๋ฆฌ API๊ฐ€ ์•„๋‹˜
  • ์กฐ๊ฑด1: ์–ด๋– ํ•œ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค๋ฅธ ์ƒํƒœ๋ฅผ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค => x
  • ์กฐ๊ฑด2: ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์ตœ์ ํ™”ํ•œ๋‹ค => x. ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๊ฐ€ ๋ฆฌ๋žœ๋”๋ง๋˜๋Š” ๊ณ ์งˆ์ ์ธ ๋ฌธ์ œ(props ์ „๋‹ฌ์ด๊ธฐ ๋•Œ๋ฌธ, memo๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ. ๊ตณ์ด?)

useContext๋กœ ์ฃผ์ž…ํ•œ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•  ๋ฟ, ๊ทธ ์ž์ฒด๋กœ ๋žœ๋”๋ง ์ตœ์ ํ™”์— ์•„๋ฌด๋Ÿฐ ๋„์›€์ด ๋˜์ง€ ์•Š๋Š”๋‹ค.

useReducer

  • https://react.dev/reference/react/useReducer
    const [state, dispatch] = useReducer(reducer, initialArg, init?) // init์€ ๊ฒŒ์œผ๋ฅธ ์ดˆ๊ธฐํ™”
  • state์„ ์‚ฌ์ „์— ์ •์˜๋œ dispatcher๋กœ๋งŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
  • state์˜ ๊ฐ’์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ๋ฏธ๋ฆฌ ์ •์˜๋‘” dispatcher, ์ฆ‰ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ
  • state ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ œํ•œ / ๋ณ€๊ฒฝ์„ ๋น ๋ฅด๊ฒŒ ํ•จ
  • ์™œ ์“ฐ๋ƒ?
    • ์—ฌ๋Ÿฌ ๊ฐœ์˜ state์„ ๋ฌถ์–ด์„œ ๊ด€๋ฆฌ
    • state ์‚ฌ์šฉ ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ถ„๋ฆฌ
type State = {
  color: string;
};

type Action = { type: "day" | "night"; 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 "day":
      return { color: "#ffffff" };
    case "night":
      return { color: "#222222" };
    default:
      throw new Error("Unexpected action type");
  }
}

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

useReducer๋กœ userState ๋งŒ๋“ค๊ธฐ

function reducer(prevState, newState) {
  return typeof newState === "function" ? newState(prevState) : newState;
}

function init(intialArg) {
  return typeof intialArg === "function" ? intialArg() : intialArg;
}

function useState(intialArg) {
  return useReducer(reducer, intialArg, init);
}

userState๋กœ useReducer ๋งŒ๋“ค๊ธฐ

const useReducer = (reducer, initialArg, init) => {
  const [state, setState] = useState(
    init ? () => init(initialArg) : initialArg
  );

  const dispatch = useCallback(
    (action) => setState((prev) => reducer(prev, action)),
    [reducer]
  );

  return useMemo(() => [state, dispatch], [state, dispatch]);
};

| ๊ธฐ์ค€ | useState | useReducer | | ------------------ | ---------------------------------------------------------- | ---------------------------------------------------------------------------------- | | ์ •์˜ | ๋‹จ์ผ ์ƒํƒœ ๊ฐ’์— ์‚ฌ์šฉ | ์—ฌ๋Ÿฌ ํ•˜์œ„ ๊ฐ’ ์žˆ๋Š” ๋ณต์žกํ•œ ์ƒํƒœ ๋กœ์ง ๊ด€๋ฆฌ์— ์ ํ•ฉ | | ์‚ฌ์šฉ ์‚ฌ๋ก€ | ๊ฐ„๋‹จํ•œ ์ƒํƒœ ๋กœ์ง๊ณผ ๊ฐ’์— ์‚ฌ์šฉ | ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ์—ฌ๋Ÿฌ ์ƒํƒœ ์„œ๋กœ ์˜์กด์ ์ผ ๋•Œ ์‚ฌ์šฉ | | ์ƒํƒœ ๊ด€๋ฆฌ | ๋‹จ์ผ ๊ฐ’(๊ฐ์ฒด, ๋ฐฐ์—ด, ๊ธฐํƒ€) ๊ด€๋ฆฌ | ์•ก์…˜์„ ํ†ตํ•œ ์ƒํƒœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜ ์‚ฌ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ | | ์‚ฌ์šฉ ๋ฐฉ๋ฒ• | const [state, setState] = useState(initialState) | const [state, dispatch] = useReducer(reducer, initialState) | | ์žฅ์  | ๊ตฌํ˜„ ๊ฐ„๋‹จํ•˜๊ณ  ์ง๊ด€์  | ๋ณต์žกํ•œ ์ƒํƒœ ๋กœ์ง ์™ธ๋ถ€ํ™”, ํ…Œ์ŠคํŠธ ์‰ฌ์›€, ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€๋กœ ๋ถ„๋ฆฌ ๊ฐ€๋Šฅ | | ๋‹จ์  | ๋ณต์žกํ•œ ์ƒํƒœ ๋กœ์ง ๋‚ด๋ถ€ ํฌํ•จ์‹œํ‚ค๋ฉด ์ปดํฌ๋„ŒํŠธ ๋น„๋Œ€ํ•ด์งˆ ์ˆ˜ ์žˆ์Œ | ์‚ฌ์šฉ๋ฒ• useState๋ณด๋‹ค ๋ณต์žก, ์ดˆ๊ธฐ ์„ค์ • ๋งŽ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ | | ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง | ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์œ„ํ•ด ์ƒˆ๋กœ์šด ์ƒํƒœ ๊ฐ’ ์ง์ ‘ ์„ค์ • | ์•ก์…˜ ๊ฐ์ฒด ์ •์˜ํ•˜๊ณ , ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜ ํ†ตํ•ด ์ƒํƒœ ์—…๋ฐ์ดํŠธ | | ์ ํ•ฉํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€ | ๋‹จ์ˆœํ•œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ, ์˜ˆ: ์นด์šดํ„ฐ, ํ† ๊ธ€ ์Šค์œ„์น˜ | ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์™€ ์กฐ๊ฑด ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ, ์˜ˆ: ์‡ผํ•‘ ์นดํŠธ, ๊ฒŒ์ž„ ๋กœ์ง |


๋ฒˆ์™ธ

๊ฒŒ์œผ๋ฅธ ์ดˆ๊ธฐํ™” (Lazy Initialization)

๊ฒŒ์œผ๋ฅธ ์ดˆ๊ธฐํ™”๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๋ฅผ ํ•„์š”ํ•  ๋•Œ๊นŒ์ง€ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๋Š” ํŒจํ„ด์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์ ˆ์•ฝํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํฐ ๋ฐ์ดํ„ฐ ์„ธํŠธ๋‚˜ ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ฐ’์„ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•ด์•ผ ํ•  ๋•Œ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ•ด๋‹น ๊ฐ’์ด ์‹ค์ œ๋กœ ํ•„์š”ํ•  ๋•Œ ์ดˆ๊ธฐํ™”ํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

React์—์„œ๋Š” useState ํ›…์„ ์‚ฌ์šฉํ•  ๋•Œ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ํ•จ์ˆ˜๋กœ ์ „๋‹ฌํ•จ์œผ๋กœ์จ ๊ฒŒ์œผ๋ฅธ ์ดˆ๊ธฐํ™”๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋ Œ๋”๋ง๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๋ฉฐ, ๊ทธ ๊ฒฐ๊ณผ๊ฐ’์ด ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ์„ ์ตœ์ ํ™”ํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹œ์ž‘ ์‹œ๊ฐ„์„ ๋‹จ์ถ•์‹œํ‚ค๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

const [state, setState] = useState(() => {
  // ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋กœ์ง
  const initialState = performExpensiveCalculation();
  return initialState;
});

useReducer

useReducer๋Š” React์—์„œ ๋ณต์žกํ•œ ์ƒํƒœ ๋กœ์ง์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ํ›…์ž…๋‹ˆ๋‹ค.

useState๋ณด๋‹ค ๋” ์„ธ๋ฐ€ํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. useReducer๋Š” ํ˜„์žฌ ์ƒํƒœ์™€ ์•ก์…˜์„ ์ธ์ž๋กœ ๋ฐ›๋Š” ๋ฆฌ๋“€์„œ(reducer) ํ•จ์ˆ˜์™€, ์ด ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ ๋ฐœ์ƒํ•  ์•ก์…˜์„ ์ •์˜ํ•˜๋Š” ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
    </>
  );
}

useReducer๋Š” ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง์„ ์ปดํฌ๋„ŒํŠธ ๋ฐ”๊นฅ์œผ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋ฉฐ, ์•ก์…˜์„ ํ†ตํ•ด ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹์€ Redux์™€ ์œ ์‚ฌํ•œ ํŒจํ„ด์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useReducer์™€ ๊ฒŒ์œผ๋ฅธ ์ดˆ๊ธฐํ™”๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. useReducer์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•จ์œผ๋กœ์จ, ๋ณต์žกํ•œ ์ƒํƒœ ๋กœ์ง์„ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const [state, dispatch] = useReducer(reducer, initialArg, init);

์—ฌ๊ธฐ์„œ initialArg๋Š” init ํ•จ์ˆ˜์— ์ „๋‹ฌ๋˜๋ฉฐ, init ํ•จ์ˆ˜๋Š” ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹œ์ž‘ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.