2024-03-16.md

๐Ÿก

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

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


DIL-week2-5_2024-03-16

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

์˜ค๋Š˜ ์ฝ์€ ๋‚ด์šฉ์„ markdown์œผ๋กœ ๊ฐ„๋‹จํžˆ ๋ฉ”๋ชจ
์ฝ์€ ์‹œ๊ฐ„: 9~21์‹œ๋ฐ˜ (์ค‘๊ฐ„์ค‘๊ฐ„ ํœด์‹)
ํ† ์š”์ผ ๋‹ค๋ฅธ ์ผ์ •x, ์ฑ… ์ฝ๊ธฐ

reference

  • What is tearing? https://github.com/reactwg/react-18/discussions/69

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

useImperativeHandle

  • ์‹ค๋ฌด์—์„œ ์ž์ฃผ ๋ณผ ์ˆ˜ ์—†์Œ
  • ์ผ๋ถ€ ์‚ฌ๋ก€์—์„œ ์œ ์šฉ

React.forwardRef

  • ref๋Š” useRef์—์„œ ๋ฐ˜ํ™˜ํ•œ ๊ฐ์ฒด, ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์˜ props์ธ ref์— ๋„ฃ์–ด HTMLElement์— ์ ‘๊ทผํ•˜๋Š” ์šฉ๋„๋กœ ํ”ํžˆ ์‚ฌ์šฉ
  • ref๋Š” ๋ฆฌ์•กํŠธ์—์„œ ์ปดํฌ๋„ŒํŠธ์˜ props๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์•ฝ์–ด ex) key
  • ref๋ฅผ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด?

    Error: ref is not a prop. Trying to access it will result in undefined being returned. If you need to access the same value within the child component, you should pass it as a different prop.

  • ๋‹ค๋ฅธ props๋กœ ๋ฐ›์œผ๋ฉด ์ž‘๋™
    • ๊ฐ™์€ ์ž‘์—…์„ ํ•˜๋Š” ๋ฆฌ์•กํŠธ API > forwardRef
    • ์ผ๊ด€์„ฑ

useImperativeHandle

  • ๋ถ€๋ชจ์—๊ฒŒ์„œ ๋„˜๊ฒจ๋ฐ›์€ ref๋ฅผ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ํ›…
const Input = forwardRef((props, ref) => {
  // useImperativeHandle: ref์˜ ๋™์ž‘์„ ์ถ”๊ฐ€๋กœ ์ •์˜
  useImperativeHandle(
    ref, // ref
    () => ({
      // ref.current๋ฅผ ๋ฎ์–ด์”€
      alert: () => alert(props.value), // ์›ํ•˜๋Š” ๊ฐ’์ด๋‚˜ ๋™์ž‘
      foo: "bar",
      // ...ref, // ref๋Š” undefined
    }),
    [props.value] // ์˜์กด์„ฑ ๋ฐฐ์—ด
  );

  return <input ref={ref} {...props} />;
});
  • ref ํ™•์ธ
function handleClick() {
  inputRef.current?.alert(); // ์ƒˆ๋กญ๊ฒŒ ์ •์˜ํ•œ ๋™์ž‘์„ ์‹คํ–‰ํ–ˆ์Œ
  console.log(inputRef.current); // {foo: 'bar', alert: ฦ’}

  console.log(inputRef.current.value); // undefined
  inputRef.current?.ref; // ์ƒˆ๋กญ๊ฒŒ ์ •์˜ํ•œ ref์— ๊ธฐ์กด ref๋ฅผ ๋„ฃ์œผ๋ ค ํ–ˆ์œผ๋‚˜ ์—ญ์‹œ undefined
}

useLayoutEffect

  • ๋ชจ๋“  DOM์˜ ๋ณ€๊ฒฝ(๋ฆฌ์•กํŠธ ๋žœ๋”๋ง) ํ›„, ๋™๊ธฐ์ ์œผ๋กœ ๋ฐœ์ƒํ•œ๋‹ค
  1. ๋ฆฌ์•กํŠธ๊ฐ€ DOM์„ ์—…๋ฐ์ดํŠธ
  2. useLayoutEffect
  3. ๋ธŒ๋ผ์šฐ์ €์— ๋ฐ˜์˜
  4. useEffect
  • useLayoutEffect์€ ๋ธŒ๋ผ์šฐ์ €์— ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋˜๊ธฐ ์ „์— ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰
    • ์‹คํ–‰์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ๋‹ค์Œ ํ™”๋ฉด์„ ๊ทธ๋ฆฐ๋‹ค
    • ์ปดํฌ๋„ŒํŠธ ์ผ์‹œ ์ค‘์ง€: ์„ฑ๋Šฅ ๋ฌธ์ œ
  • DOM์€ ๊ณ„์‚ฐ๋์ง€๋งŒ ์ด๊ฒƒ์ด ํ™”๋ฉด์— ๋ฐ˜์˜๋˜๊ธฐ ์ „์— ํ•˜๊ณ  ์‹ถ์€ ์ž‘์—…์ด ์žˆ์„ ๋•Œ/๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•  ๋•Œ ex) ์• ๋‹ˆ๋ฉ”์ด์…˜, ์Šคํฌ๋กค ์œ„์น˜ ์ œ์–ด

useDebugValue

  • ์ผ๋ฐ˜์ ์œผ๋กœ ํ”„๋กœ๋•์…˜x
  • ๋””๋ฒ„๊น… ์ •๋ณด -> ๋ฆฌ์•กํŠธ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ
    • ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜: ๊ฐ’
    • ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜: ํฌ๋งคํŒ… ํ•จ์ˆ˜ (์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜์˜ ๊ฐ’์ด ๊ฐ™์œผ๋ฉด ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ)
  • ๋‹ค๋ฅธ ํ›… ๋‚ด๋ถ€์—์„œ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ
function useDate() {
  const date = new Date();
  useDebugValue(date, (date) => `ํ˜„์žฌ ์‹œ๊ฐ„: ${date.toUTCString()}`);
  return date;
}

Rules of Hooks

rules-of-hooks
https://legacy.reactjs.org/docs/hooks-rules.html

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

  • ํ›…์— ๋Œ€ํ•œ ์ •๋ณด ์ €์žฅ => ๋ฆฌ์•กํŠธ ์–ด๋”˜๊ฐ€์— index์™€ ๊ฐ™์€ ํ‚ค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ (๋งํฌ๋“œ ๋ฆฌ์ŠคํŠธ) - ์ˆœ์„œ์— ์˜ํ–ฅ์„ ๋ฐ›์Œ
    • ํŒŒ์ด๋ฒ„ ๊ฐ์ฒด์˜ ๋งํฌ๋“œ ๋ฆฌ์ŠคํŠธ ํ˜ธ์ถœ ์ˆœ์„œ
const [count, setCount] = useState(0) // 1๏ธโƒฃ
const [required, setRequired] = useState(false) // 2๏ธโƒฃ

useEffect(()=>{ // 3๏ธโƒฃ
  // ...
},[count, required])

// Fiber
{  // 1๏ธโƒฃ setCount
  memoizedState: 0,
  baseState: 0,
  queue: {/* ... */},
  baseUpdate: null,
  next: { // 2๏ธโƒฃ setRequired
    memoizedState: false,
    baseState: false,
    queue: {/* ... */},
    baseUpdate: null,
    next: { // 3๏ธโƒฃ useEffect
      memoizedState: {
        tag: 192,
        create: ()=>{},
        destroy: undefined,
        deps: [0, false], // ์˜์กด์„ฑ ๋ฐฐ์—ด ๊ฐ’! (์ด์ „ ๊ฐ’๊ณผ ๋น„๊ต)
        next: {/* ... */}
      },
      baseState: false,
      queue: {/* ... */},
      baseUpdate: null,
    }
  }
}
  • ๊ณ ์ •๋œ ์ˆœ์„œ์— ์˜์กดํ•ด ํ›…๊ณผ ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ์ €์žฅํ•จ = ์ด์ „ ๊ฐ’์— ๋Œ€ํ•œ ๋น„๊ต์™€ ์‹คํ–‰
  • ํ›…์€ ์กฐ๊ฑด๋ฌธ/๋ฐ˜๋ณต๋ฌธ ๋“ฑ์— ์˜ํ•ด ๋ฆฌ์•กํŠธ์—์„œ ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ์ˆœ์„œ๋กœ ์‹คํ–‰๋˜๊ฒŒ ํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ๋ณด์žฅ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ ์ตœ์ƒ๋‹จ์— ์„ ์–ธ๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.

Custom Hook vs Higher order component

  • ์ปค์Šคํ…€ํ›…
    • ๋ฆฌ์•กํŠธ ํ›…์˜ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋ฉด์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ƒˆ๋กœ์šด ํ›… ๋งŒ๋“ฆ
      • ์™œ? ๋‚ด๋ถ€์—์„œ ํ›… ์‚ฌ์šฉํ•ด์•ผํ•จ,
    • ์œ ๋ช… ์ปค์Šคํ…€ํ›…: use-Hooks, react-use, ahooks
  • HOC๋Š” ์ปดํฌ๋„ŒํŠธ ๋กœ์ง ์žฌ์‚ฌ์šฉ ex) React.memo

HOC

  • ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ์‹ธ์„œ, ์ปค์Šคํ…€ ํ›…๋ณด๋‹ค ํฐ ์˜ํ–ฅ๋ ฅ (์ปดํฌ๋„ŒํŠธ ๊ฒฐ๊ณผ๋ฌผ์— ์˜ํ–ฅ์„ ๋ฏธ์นจ)
  • with ์ ‘๋‘์‚ฌ๋กœ ์‹œ์ž‘ํ•œ๋‹ค (์ผ์ข…์˜ ๊ฐœ๋ฐœ์ž ์ปจ๋ฒค์…˜)
  • ๋ถ€์ˆ˜ํšจ๊ณผ๋ฅผ ์ตœ์†Œํ™”ํ•œ๋‹ค.
    • ์ธ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์˜ props๋ฅผ ์ž„์˜๋กœ ์ˆ˜์ •, ์ถ”๊ฐ€, ์‚ญ์ œํ•˜์ง€ ์•Š๊ธฐ

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ, ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ, wrapper ์ปดํฌ๋„ŒํŠธ์˜ ์ฐจ์ด์ 

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ํฌํ•จํ•˜๊ณ , ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ props๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋‚˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๋“ฑ์„ ์ „๋‹ฌํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜, ์ž์‹ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์กฐ์ •ํ•˜๋Š” ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋Š” ์ผ๋ฐ˜์ ์ธ ์ปดํฌ๋„ŒํŠธ ๊ณ„์ธต์—์„œ์˜ ์ƒํ•˜ ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

function ParentComponent() {
  const message = "Hello from Parent!";

  return <ChildComponent message={message} />;
}

๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ (Higher-Order Components, HOC)

๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. HOC๋Š” ์ฃผ๋กœ ๋กœ์ง์˜ ์žฌ์‚ฌ์šฉ์„ ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์— ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณตํ†ต ๋กœ์ง์ด๋‚˜ ์ƒํƒœ ๊ด€๋ฆฌ, ์ƒ๋ช…์ฃผ๊ธฐ ๋ฉ”์†Œ๋“œ ๋“ฑ์„ HOC๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐ ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function withExtraProp(WrappedComponent) {
  return function (props) {
    return <WrappedComponent extraProp="Extra Prop" {...props} />;
  };
}

// ์‚ฌ์šฉ ์˜ˆ: const EnhancedComponent = withExtraProp(OriginalComponent);
// <EnhancedComponent />

๋ž˜ํผ (Wrapper) ์ปดํฌ๋„ŒํŠธ

๋ž˜ํผ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๊ฐ์‹ธ๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ๋ž˜ํผ ์ปดํฌ๋„ŒํŠธ๋Š” ์Šคํƒ€์ผ๋ง, ๋ ˆ์ด์•„์›ƒ ๊ตฌ์„ฑ, ์ƒํƒœ ๊ด€๋ฆฌ ๋“ฑ์˜ ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ž˜ํผ ์ปดํฌ๋„ŒํŠธ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ์ถ”๊ฐ€์ ์ธ ์†์„ฑ์„ ์ฃผ์ž…ํ•˜์ง€ ์•Š๊ณ , ๊ฐ์‹ธ๋Š” ์—ญํ• ์— ์ดˆ์ ์„ ๋งž์ถฅ๋‹ˆ๋‹ค.

function WrapperComponent({ children }) {
  return <div className="wrapper">{children}</div>;
}

/** ์‚ฌ์šฉ ์˜ˆ:
 * <WrapperComponent>
 *   <Component message={message} />
 * </WrapperComponent> */

๋น„๊ต

| ๊ตฌ๋ถ„ | ์ธ์ž | ๋ชฉ์  | ์‚ฌ์šฉ ์‚ฌ๋ก€ | ์žฌ์‚ฌ์šฉ์„ฑ | | ---------------- | -------------- | ----------------------------------------------------- | -------------------------------------------------- | ------------------------------------------ | | ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ | props | ์ƒ์œ„ ๊ณ„์ธต์—์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ | ์ปดํฌ๋„ŒํŠธ ๊ณ„์ธต ๊ตฌ์กฐ ๊ตฌ์„ฑ | ๋‚ฎ์Œ, ์ง์ ‘์ ์ธ ์ƒ์† ๊ด€๊ณ„ ํ•„์š” | | ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ | ์ปดํฌ๋„ŒํŠธ | ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑ, ์žฌ์‚ฌ์šฉ์„ฑ | ๋กœ์ง ์žฌ์‚ฌ์šฉ, prop ์ฃผ์ž…, ์กฐ๊ฑด๋ถ€ ๋žœ๋”๋ง, ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ | ๋†’์Œ, ๋‹ค์–‘ํ•œ ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉ ๊ฐ€๋Šฅ | | Wrapper ์ปดํฌ๋„ŒํŠธ | props.children | ์ถ”๊ฐ€์ ์ธ ์†์„ฑ์„ ์ฃผ์ž…ํ•˜์ง€ ์•Š๊ณ , ๊ฐ์‹ธ๋Š” ์—ญํ• ์— ์ดˆ์  | ์Šคํƒ€์ผ๋ง, ๋ ˆ์ด์•„์›ƒ ์กฐ์ •, ์กฐ๊ฑด๋ถ€ ๋ž˜ํผ | ์ค‘๊ฐ„, ๋น„์Šทํ•œ ๋ ˆ์ด์•„์›ƒ/์Šคํƒ€์ผ ์š”๊ตฌ์‹œ ์žฌ์‚ฌ์šฉ |

custom hook vs HOC

  • ๋ญ˜ ์“ฐ๋Š” ๊ฒŒ ์ข‹์„๊นŒ์˜ ๊ธฐ์ค€

| ๊ตฌ๋ถ„ | ์žฅ์  | ๋‹จ์  | ์‚ฌ์šฉ ์˜ˆ | | ----------- | ----------------------------------------------------------- | ---------------------------------------------------------------- | -------------- | | custom hook | ๋ถ€์ˆ˜ํšจ๊ณผ๊ฐ€ ์ œํ•œ์ , ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ์ตœ์†Œํ™” | ๋žœ๋”๋ง ๊ฒฐ๊ณผ๋ฌผ์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์—†์Œ, ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฐ”ํƒ•์œผ๋กœ ๋กœ์ง ์ž‘์„ฑ | ๋™์ผ ๋กœ์ง ๊ฒฉ๋ฆฌ | | HOC | ๋žœ๋”๋ง ๊ฒฐ๊ณผ๋ฌผ์— ์˜ํ–ฅ์„ ๋ฏธ์นจ, ๋กœ์ง์— ๋”ฐ๋ผ ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ๋…ธ์ถœ | ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง์ ‘ ๋ณด๊ฑฐ๋‚˜ ์‹คํ–‰ํ•  ๋•Œ๊นŒ์ง€, ๊ฒฐ๊ณผ๋ฌผ์„ ์•Œ ์ˆ˜ ์—†๋‹ค | ์—๋Ÿฌ ๋ฐ”์šด๋”๋ฆฌ |

๐Ÿ“Œ ๋ฆฌ์•กํŠธ์™€ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

  • ์ƒํƒœ
    • ์–ด๋– ํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„ ๊ฐ’
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋”ฐ๋ผ ์ง€์†์ ์œผ๋กœ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ’
  • UI, URL, form, API call

์—ญ์‚ฌ ๐Ÿ‘จโ€๐Ÿฆณ

flux ํŒจํ„ด

  • ๋ฆฌ๋•์Šค ์ด์ „์˜ ์ด์•ผ๊ธฐ....
  • 2014๋…„๊ฒฝ, ๋ฆฌ์•กํŠธ ๋“ฑ์žฅ๊ณผ ๋น„์Šทํ•œ ์‹œ๊ธฐ์— Flux ํŒจํ„ด, ๊ทธ๋ฆฌ๊ณ  Flux ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋‚˜ํƒ€๋‚ฌ๋”๋žฌ๋‹ค...
    • Flux, alt, RefluxJS, NuclearJS, Fuxible, Fluxxor
  • ๊ธฐ์กด MVC ํŒจํ„ด์— ๋ชจ๋ธ ๋ทฐ๊ฐ€ ๊ฒ๋‚˜ ๋งŽ์•„์ ธ์„œ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๋˜ ์ƒํ™ฉ
    • ์ƒํƒœ ๋ณ€ํ™” ์›์ธ๊ณผ ์œ„์น˜ ์ถ”์ ์ด ์–ด๋ ค์›€
  • (ํŽ˜์ด์Šค๋ถ) ์–‘๋ฐฉํ–ฅ์ด ๋ฌธ์ œ๋‹ค. -> ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ๋ณ€๊ฒฝ
    • Action -> Dispatcher -> Model -> View
    • ์•ˆ์ •์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ ์ž ๋…ธ๋ ฅ

| ๊ตฌ๋ถ„ | ์—ญํ•  | ์ž‘๋™ | | ---------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------ | | action | ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ ์•ก์…˜, ์•ก์…˜ ๋ฐœ์ƒ ์‹œ ํฌํ•จ์‹œํ‚ฌ ๋ฐ์ดํ„ฐ | ์•ก์…˜ ํƒ€์ž…๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ•ด ๋””์ŠคํŒจ์ณ๋กœ ๋ณด๋ƒ„ | | dispatcher | ์•ก์…˜์„ ์Šคํ† ์–ด์— ๋ณด๋ƒ„ | ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ํ˜•ํƒœ๋กœ, ์•ž์„œ ์•ก์…˜์ด ์ •์˜ํ•œ ํƒ€์ž…๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์Šคํ† ์–ด์— ๋ณด๋ƒ„ | | store | ์‹ค์ œ ์ƒํƒœ์— ๋”ฐ๋ฅธ ๊ฐ’๊ณผ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ | ์•ก์…˜ ํƒ€์ž…์— ๋”ฐ๋ผ, ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝํ•  ์ง€ ์ •์˜๋˜์–ด ์žˆ์Œ | | view | (๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์— ํ•ด๋‹น) ์Šคํ† ์–ด์—์„œ ๋งŒ๋“ค์–ด์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ํ™”๋ฉด์„ ๋žœ๋”๋ง | ์•ก์…˜์„ ํ˜ธ์ถœํ•ด์„œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Œ |

Redux์˜ ๋‘๋‘ฅ๋“ฑ์žฅ

  • ๋‹น์‹œ ์‹œ์žฅ์„ ์ง€๋ฐฐํ•จ
  • Flux ๊ตฌ์กฐ + Elm ์•„ํ‚คํ…์ฒ˜ ๋„์ž…: ์›นํŽ˜์ด์ง€๋ฅผ ์„ ์–ธ์ ์œผ๋กœ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ์–ธ์–ด
    • model(์ƒํƒœ), view(HTML), update(๋ชจ๋ธ ์ˆ˜์ • ๋ฐฉ์‹)
  • ๋‹จ์ 
    • ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์•ก์…˜ ํƒ€์ž… ์„ ์–ธ, creator ํ•จ์ˆ˜ ์ž‘์„ฑ, dispatcher์™€ selector
    • ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์งฑ ๋งŽ์Œ (์ง€๊ธˆ์€ ๋งŽ์ด ๊ฐ„์†Œํ™”๋จ)

Context API์™€ useContext

  • ๋ฆฌ์•กํŠธ 16.3 ์ด์ „ => getChildContext()๋ฅผ ์‚ฌ์šฉํ•ด context๋ฅผ ๋‹ค๋ฃธ
    • ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ ๋žœ๋”๋ง ์‹œ, getChildContext ํ˜ธ์ถœ -> ๋ฆฌ๋žœ๋”๋ง
    • context๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์•„์•ผํ•ด์„œ, ์ปดํฌ๋„ŒํŠธ์™€ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋†’์•„์ง
  • ๋ฆฌ์•กํŠธ 16.3 => Context API

Hook, React Query, SWR(Vercel)

  • ํ›… -> ์ด์ „์—๋Š” ๋ณผ ์ˆ˜ ์—†์—ˆ๋˜ ๋ฐฉ์‹์˜ ์ƒํƒœ ๊ด€๋ฆฌ ๋“ฑ์žฅ
  • React Query(Tanstack Query), SWR
    • fetch ๊ด€๋ฆฌ์— ํŠนํ™”, API ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌ
    • HTTP ์š”์ฒญ์— ํŠนํ™”๋œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

Recoil(ํŽ˜์ด์Šค๋ถ), Zustand, Jotai, Valtio

  • ๋ฒ”์šฉ์  ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    • ์ž‘์€ ํฌ๊ธฐ์˜ ์ƒํƒœ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์ถ”์„ธ
    • peerDependencies => react 16.8^

Local State: (1)useState, (2)useReducer

โ™ป๏ธ useReducer๋กœ useState ๋งŒ๋“ค๊ธฐ

  • (ํƒ€์ž…) Initializer ํƒ€์ž…์€ ์ œ๋„ค๋ฆญ ํƒ€์ž… T์— ๋Œ€ํ•˜์—ฌ T ๋˜๋Š” T์˜ ์ด์ „ ์ƒํƒœ๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ T ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ ํƒ€์ž…
    • ์ด๋Š” ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์‹œ ์ƒํƒœ ๊ฐ’ ๋˜๋Š” ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ action์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • ์‹ค์ œ๋กœ useState์€ useReducer๋กœ ๋งŒ๋“ฆ
type Initializer<T> = T extends any ? T | ((prev: T) => T) : never;

// `useStateWithUseReducer`๋Š” initialState `initState`๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ณ , ๋‚ด๋ถ€์ ์œผ๋กœ `useReducer` ํ›…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
function useStateWithUseReducer<T>(initState: T) {
  // `useReducer` ํ›…์€ ์ƒํƒœ(`state`)์™€ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜(`dispatch`)๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  // ์—ฌ๊ธฐ์„œ ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๋Š” ์ด์ „ ์ƒํƒœ(`prev`)์™€ ์•ก์…˜(`action`)์„ ๋ฐ›์•„ ์ƒˆ ์ƒํƒœ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  // ์•ก์…˜์ด ํ•จ์ˆ˜์ธ ๊ฒฝ์šฐ, ์ด์ „ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒํƒœ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  // ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์•ก์…˜ ์ž์ฒด๋ฅผ ์ƒˆ ์ƒํƒœ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  const [state, dispatch] = useReducer(
    (prev: T, action: Initializer<T>) =>
      typeof action === "function" ? action(prev) : action,
    initState
  );

  // ์ƒํƒœ์™€ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” `dispatch` ํ•จ์ˆ˜๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฌถ์–ด ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  // ์ด๋Š” `useState` ํ›…์˜ ๋ฐ˜ํ™˜ ๊ฐ’๊ณผ ์œ ์‚ฌํ•œ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.
  return [state, dispatch];
}

โ™ป๏ธ useState๋กœ useReducer ๋งŒ๋“ค๊ธฐ

import { useState, useCallback } from "react";

/**
 * `useState`์™€ `useCallback`์„ ์‚ฌ์šฉํ•˜์—ฌ `useReducer` ํ›…์˜ ๊ธฐ๋Šฅ์„ ๋ชจ๋ฐฉํ•˜๋Š” ์ปค์Šคํ…€ ํ›…์ž…๋‹ˆ๋‹ค.
 *
 * @param {Function} reducer - ์ด์ „ ์ƒํƒœ์™€ ์•ก์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜
 * @param {any} initialState - ์ดˆ๊ธฐ ์ƒํƒœ ๊ฐ’
 * @param {Function} [initializer] - (optional) ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์„ ํƒ์  ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜
 *                                   ์ œ๊ณต๋˜๋ฉด, ์ด ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐ ์ƒํƒœ ์„ค์ •
 * @returns {[any, Function]} ํ˜„์žฌ ์ƒํƒœ ๊ฐ’๊ณผ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” dispatch ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
 */
function useReducerWithUseState(reducer, initialState, initializer) {
  // ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. initializer๊ฐ€ ์ œ๊ณต๋˜๋ฉด, initializer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ initialState๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  // ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด, initialState๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  const [state, setState] = useState(
    initializer ? () => initializer(initialState) : initialState
  );

  // ์•ก์…˜์„ ๋ฐ›์•„ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” dispatch ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  // ์ด ํ•จ์ˆ˜๋Š” ํ›…์œผ๋กœ ์ „๋‹ฌ๋œ reducer ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
  // useCallback์œผ๋กœ ๋ž˜ํ•‘ํ•˜์—ฌ ํ•จ์ˆ˜์˜ ๋ถˆํ•„์š”ํ•œ ์žฌ์ƒ์„ฑ์„ ๋ฐฉ์ง€ํ•˜๊ณ , ๋ Œ๋”๋ง ๊ฐ„์— ์•ˆ์ •์ ์œผ๋กœ ์œ ์ง€๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  const dispatch = useCallback(
    (action) => setState((prev) => reducer(prev, action)),
    // reducer ํ•จ์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ dispatch ํ•จ์ˆ˜๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด useCallback์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ํฌํ•จ์‹œํ‚ต๋‹ˆ๋‹ค.
    // ์˜์กด์„ฑ์ด ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    [reducer]
  );

  // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒํƒœ๋ฅผ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ˜„์žฌ ์ƒํƒœ์™€ dispatch ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  // state๋Š” ํ˜„์žฌ์˜ ์ƒํƒœ, dispatch ํ•จ์ˆ˜๋Š” ์•ก์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
  return [state, dispatch];
}

ํ•จ์ˆ˜ ์™ธ๋ถ€์—์„œ ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚ฌ์„ ๋•Œ๋ฅผ ์ฐธ์กฐํ•ด ๋ฆฌ๋žœ๋”๋ง

  1. ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€ ์–ด๋”˜๊ฐ€์— ์ƒํƒœ๋ฅผ ๋‘๊ณ , ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ™์ด ์“ธ ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ
  2. ์ƒํƒœ์˜ ๋ณ€ํ™”๋ฅผ ์•Œ์•„์ฑŒ ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ณ , ์ƒํƒœ ๋ณ€ํ™” ์‹œ ๋ฆฌ๋žœ๋”๋ง์„ ์ผ์œผ์ผœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ์‹  ์ƒํƒœ๊ฐ’ ๊ธฐ์ค€์œผ๋กœ ๋žœ๋”๋งํ•ด์•ผ ํ•จ. (๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์—์„œ ๋™์ผ ์ž‘๋™)
  3. ์›์‹œ๊ฐ’ ์•„๋‹Œ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ, ๊ฐ์ง€ํ•˜์ง€ ์•Š๋Š” ๊ฐ’์ด ๋ณ€ํ•˜๋ฉด ๋ฆฌ๋žœ๋”๋ง x
    • obj.a๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ obj.b๋ฅผ ์˜์กดํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋žœ๋”๋ง๋˜๋ฉด x
  • store, callback, subscribe

๊ฐ„๋žตํ•˜๊ฒŒ ๋ณด๊ธฐ: Recoil, Jotai, Zustand

Recoil

  • 2020๋…„, ํŽ˜์ด์Šค๋ถ, ์ตœ์†Œ ์ƒํƒœ ๊ฐœ๋… Atom์„ ์ฒ˜์Œ ๋ฆฌ์•กํŠธ ์ƒํƒœ๊ณ„์—์„œ ์‹œ์ž‘
    • ์—…๋ฐ์ดํŠธ 1๋…„ ์ „, ์‹คํ—˜ ๋‹จ๊ณ„
  • RecoilRoot: Context store, ์ƒํƒœ ๊ฐ’์— ์ ‘๊ทผํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋“ค, ๊ฐ’ ๋ณ€๊ฒฝ => ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ์•Œ๋ฆผ
  • atom: ์ตœ์†Œ ์ƒํƒœ ๋‹จ์œ„, key, default

Jotai

  • atom(์ตœ์†Œ ์ƒํƒœ ๋‹จ์œ„)์œผ๋กœ ์ƒํƒœ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜, ํŒŒ์ƒ ์ƒํƒœ๋ฅผ ๋งŒ๋“ฆ
  • ์‰ฌ์›€ o
const [count, setCount] = useAtom(countAtom);

// countAtom
// atom์—๋Š” ๊ฐ’์ด ์ €์žฅ๋˜์–ด์žˆ์ง€ ์•Š๋‹ค.
{
  init: 0,
  read: (get) => get(config),
  write: (get, set, update) =>
    set(config, typeof update === "function" ? update(get(config)) : update),
};

Zustand

  • ๋ฆฌ๋•์Šค ๋Œ€๋น„ ์ ์€ ์ฝ”๋“œ ์ž‘์„ฑ / ์‰ฌ์›€ => ํ™•์ธ ํ•„์š”
  • ๋ฒˆ๋“ค ์ตœ์†Œํ™” o
  • ts ์ง€์› o
  • ๋ฏธ๋“ค์›จ์–ด o

image

=> TODO: ์˜ˆ์ œ ๋” ์‚ดํŽด๋ณด๊ธฐ 5.2.2~5.2.4


โœ๏ธ English
resilient /rษ™หˆzilฤ“ษ™nt/
de facto ์—…๊ณ„ ํ‘œ์ค€, De facto is Latin for "of fact", de jure
Tearing