2024-04-13.md

๐Ÿก

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

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


DIL-week6-6_2024-04-13

| DIL ์ฃผ์ฐจ | ๋ฒ”์œ„ | ๋‚ด์šฉ | ์˜ค๋Š˜์ฐจ ์ง„๋„ | | -------- | ---------- | --------------------------------------------------------- | ----------- | | 6์ฃผ์ฐจ | 10์žฅ, 11์žฅ | ๋ฆฌ์•กํŠธ 17๊ณผ 18 ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์‚ดํŽด๋ณด๊ธฐ, Next.js 13๊ณผ ๋ฆฌ์•กํŠธ 18 | p~ |

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


React 18

react-dom/client

  • ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฆฌ์•กํŠธ ํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•˜๋Š” API ๋ณ€๊ฒฝ์ 
    • index.{t|j}sx ๋‚ด์šฉ ๋ณ€๊ฒฝ

createRoot

  • render => createRoot + render
const root = ReactDOM.createRoot(container);
root.render(<App />);

hydrateRoot

  • ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋žœ๋”๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•˜์ด๋“œ๋ ˆ์ด์…˜์„ ํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ
    • ํ”„๋ ˆ์ž„์›Œํฌ์— ์˜์กดํ•˜๊ณ  ์žˆ์„ ๊ฒƒ์ด๋ฏ€๋กœ ์ˆ˜์ •ํ•  ์ผ์ด ๊ฑฐ์˜ ์—†๋‹ค.
// before
import ReactDOM from "react-dom";
import App from "App";

const container = document.getElementById("root");

ReactDOM.hydrate(<App />, container);

// 18
const root = ReactDOM.hydrateRoot(container, <App />);

onRecoverableError

  • createRoot, hydrateRoot์˜ ์˜ต์…˜
    • ๊ณผ์ •์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜ => reportError, console.error ๋˜๋Š” ์›ํ•˜๋Š” ๋‚ด์šฉ ์ถ”๊ฐ€

react-dom/server

renderToPipeableStream

  • ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ HTML๋กœ ๋žœ๋”๋งํ•˜๋Š” ๋ฉ”์„œ๋“œ, ์ŠคํŠธ๋ฆผ์„ ์ง€์›
  • HTML์„ ์ ์ง„์ ์œผ๋กœ ๋žœ๋”๋ง, ํด๋ผ์ด์–ธํŠธ๋Š” ์ค‘๊ฐ„์— script๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์Œ
    • ์„œ๋ฒ„์—์„œ Suspence๋ฅผ ์‚ฌ์šฉํ•ด, ๋น ๋ฅด๊ฒŒ ๋žœ๋”๋ง ํ•„์š”ํ•œ ๋ถ€๋ถ„ ๋žœ๋”๋ง(๋น„์‹ผ ์—ฐ์‚ฐ ๋‚˜์ค‘์—)
  • ์ฒซ ๋ฒˆ์งธ ๋กœ๋”ฉ์„ ๋น ๋ฅด๊ฒŒ ์ˆ˜ํ–‰ํ•˜๊ธฐ
    • hydrateRoot๋ฅผ ํ˜ธ์ถœํ•ด ์„œ๋ฒ„์—์„œ๋Š” HTML ๋žœ๋”๋ง + ํด๋ผ์ด์–ธํŠธ์˜ ๋ฆฌ์•กํŠธ์—๋Š” ์ด๋ฒคํŠธ๋งŒ ์ถ”๊ฐ€
    • ๊ธฐ์กด renderToNodeStream ๋ฌธ์ œ์  -> ๋ฌด์กฐ๊ฑด ์ˆœ์„œ๋Œ€๋กœ, ์ˆœ์„œ์— ์˜์กด์  / ๋ธ”๋ฝํ‚น, ๋ณ‘๋ชฉ
  • ์™€ ๊ฐ™์€ ์ฝ”๋“œ ๋ถ„ํ• , ์ง€์—ฐ ๋žœ๋”๋ง์„ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ์‚ฌ์šฉ
  • ์‹ค์ œ๋กœ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹ˆ๋ผ renderToPipableStream์œผ๋กœ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋žœ๋”๋ง์„ ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ ๊ฑฐ์˜ ์—†์ง€๋งŒ -> ์‚ฌ์šฉํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ๋ฆฌ์•กํŠธ 18์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? ๋ฉ”์„œ๋“œ ์ง€์› ์—ฌ๋ถ€ ํ™•์ธ ํ•„์š”

renderToReadableStream

  • ์›น ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋˜ ์—ฃ์ง€ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ (์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์‚ฌ์šฉ x)
    • renderToPipeableStream: Node.js
    • renderToReadableStream: web stream

์ž๋™ ๋ฐฐ์น˜ Automatic Batching

  • ๋ฆฌ์•กํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ์ƒํƒœ๋ฅผ ํ•˜๋‚˜์˜ ๋ฆฌ๋žœ๋”๋ง์œผ๋กœ ๋ฌถ์–ด์„œ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•
    • ์‚ฌ์šฉ์ž ์•ก์…˜์ด ํ•œ ๋ฒˆ์— ๋‘ ๊ฐœ ์ด์ƒ์˜ state์„ ๋™์‹œ์— ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค? -> ํ•˜๋‚˜์˜ ๋ฆฌ๋žœ๋”๋ง์œผ๋กœ ๋ฌถ์–ด์„œ ์ˆ˜ํ–‰
  • Promise, setTimeout๊ณผ ๊ฐ™์€ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ
    • 17์˜ ๊ฒฝ์šฐ? ๋‘ ๋ฒˆ ๋ฆฌ๋žœ๋”๋ง, 18์—์„œ๋Š” ์ž๋™ ๋ฐฐ์น˜๋กœ ํ•œ ๋ฒˆ ๋ฆฌ๋žœ๋”๋ง(๋ชจ๋“  ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฐฐ์น˜ ์ž‘์—…์œผ๋กœ ์ตœ์ ํ™”๋˜๋„๋ก ๋ฐ”๋€œ)
    • ๊ทธ ์™ธ์˜ ์ด๋ฒคํŠธ๋Š” ๋™์ผํ•˜๊ฒŒ 1๋ฒˆ
  • ์ž๋™ ๋ฐฐ์น˜๋ฅผ ์•ˆํ•  ๊ฒฝ์šฐ > flushSync๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๋ฌธ์ž์—ด ref ์‚ฌ์šฉ ๊ธˆ์ง€

<input type="text" ref="myInput" />; // ๋ฌธ์ž์—ด์„ ref๋กœ ์‚ฌ์šฉํ•จ
console.log(this.refs.myInput); // deprecated
  • ๋ฌธ์ œ์ 
    • ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์— ๊ฑธ์ณ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ถฉ๋Œ์˜ ์—ฌ์ง€ ์žˆ์Œ
    • ๋‹จ์ˆœ ๋งŒ์ž์—ด๋กœ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์–ด๋–ค ref์—์„œ ์ฐธ์กฐ๋˜๊ณ  ์žˆ๋Š” ์ง€ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์›€
    • ๋ฆฌ์•กํŠธ๊ฐ€ ๊ณ„์†ํ•ด์„œ ํ˜„์žฌ ๋žœ๋”๋ง๋˜๊ณ  ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ref ๊ฐ’์„ ์ถ”์ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ์žˆ์Œ

findDOMNode์— ๋Œ€ํ•œ ๊ฒฝ๊ณ 

  • ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค์—์„œ ์‹ค์ œ DOM ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์ง€๊ธˆ์€ ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š” ๋ฉ”์„œ๋“œ
    • ๋ถ€๋ชจ๊ฐ€ ํŠน์ • ์ž์‹๋งŒ ๋ณ„๋„๋กœ ๋žœ๋”๋งํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋ฆฌ์•กํŠธ๊ฐ€ ์ถ”๊ตฌํ•˜๋Š” ํŠธ๋ฆฌ ์ถ”์ƒํ™” ๊ตฌ์กฐ์™€ ๋งž์ง€ ์•Š์Œ

๊ตฌ Context API ์‚ฌ์šฉ ์‹œ ๊ฒฝ๊ณ 

  • (์—„๊ฒฉ ๋ชจ๋“œ) childContextType, getChildContext ์‚ฌ์šฉ ์‹œ ๊ฒฝ๊ณ 

์˜ˆ์ƒ์น˜ ๋ชปํ•œ side-effects ๊ฒ€์‚ฌ

  • (์—„๊ฒฉ ๋ชจ๋“œ) ๋‹ค์Œ์„ ์ด์ค‘ ํ˜ธ์ถœ
    • ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ constructor.render, shoudComponentUpdate.getDerivedStateFromProps
    • ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ์˜ setState์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜
    • ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ์˜ body
    • useState, useMemo, useReducer์— ์ „๋‹ฌ๋˜๋Š” ํ•จ์ˆ˜
  • ์™œ?
    • ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™์— ๋”ฐ๋ผ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ํ•ญ์ƒ ์ˆœ์ˆ˜ํ•˜๋‹ค.
      • ์ •๋ง ์ˆœ์ˆ˜ํ•œ ๊ฒฐ๊ณผ๋ฌผ์„ ๋‚ด๊ณ  ์žˆ๋Š”์ง€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ™•์ธ ์‹œ์ผœ์ฃผ๊ธฐ ์œ„ํ•ด ๋‘ ๋ฒˆ ์‹คํ–‰ํ•œ๋‹ค.
    • ์ž…๋ ฅ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด, ํ•ญ์ƒ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฌผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      • ์ฆ‰, state, props, context๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด? ํ•ญ์ƒ ๋™์ผํ•œ JSX๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.
  • console
    • 17์—์„œ๋Š” ๋‘ ๋ฒˆ log ์•ˆํ•จ
    • 18์—์„œ๋Š” ๋‘ ๋ฒˆ์งธ๋Š” ๊ธ€์ž์ƒ‰์ƒ ํšŒ์ƒ‰

์—„๊ฒฉ ๋ชจ๋“œ ์ถ”๊ฐ€์š”

  • https://react.dev/blog/2022/03/08/react-18-upgrade-guide#updates-to-strict-mode

    In the future, weโ€™d like to add a feature that allows React to add and remove sections of the UI while preserving state. For example, when a user tabs away from a screen and back, React should be able to immediately show the previous screen. To do this, React would unmount and remount trees using the same component state as before.

  • ํ–ฅํ›„ ๋ฆฌ์•กํŠธ์—์„œ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋œ ์ƒํƒœ์—์„œ ๋‚ด๋ถ€์˜ ์ƒํƒœ๊ฐ’์„ ์œ ์ง€ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ์˜ˆ์ • <- ์ด๊ฑฐ ํฐ ์ผ์ž„
    • ex) ๋’ค๋กœ ๊ฐ€๊ธฐ ํ›„ ๋Œ์•„์™”์„ ๋•Œ ์ด์ „ ์ƒํƒœ ์œ ์ง€
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ๋งˆ์šดํŠธ๋  ๋•Œ๋งˆ๋‹ค, ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž๋™์œผ๋กœ ๋งˆ์šดํŠธ ํ•ด์ œ ๋ฐ ์žฌ๋งˆ์šดํŠธํ•ด, ๋‘ ๋ฒˆ์งธ ๋งˆ์šดํŠธ ์ด์ „ ์ƒํƒœ๋กœ ๋ณต์›ํ•œ๋‹ค.
    * React mounts the component.
      * Layout effects are created.
      * Effect effects are created.
    * React simulates unmounting the component.
      * Layout effects are destroyed.
      * Effects are destroyed.
    * React simulates mounting the component with the previous state.
      * Layout effect setup code runs
      * Effect setup code runs
    

Suspense ๊ธฐ๋Šฅ ๊ฐ•ํ™”

  • Suspense๋Š”? 16.6์—์„œ ์‹คํ—˜ ๋ฒ„์ „์œผ๋กœ ๋„์ž… / ์ปดํฌ๋„ŒํŠธ ๋™์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ๊ธฐ๋Šฅ
    • lazy์™€ Suspense๋Š” ํ•œ ์Œ์œผ๋กœ ์‚ฌ์šฉ๋๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ƒ๋Œ€์ ์œผ๋กœ ์ค‘์š”ํ•˜์ง€ ์•Š์€ ์ปดํฌ๋„ŒํŠธ ๋ถ„ํ•  => ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ด
const DynamicSampleComponent = lazy(() => import("./SampleComponent"));

export default function App() {
  return (
    <Suspense fallback={<>...loading</>}>
      <DynamicSampleComponent />
    </Suspense>
  );
}
  • Next.js์—์„œ Suspense๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์ง€์—ฐ ์ค‘์ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋žœ๋”๋ง ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— useEffect๋ฅผ ํ™œ์šฉํ•œ ์ปค์Šคํ…€ ํ›… ๋“ฑ์„ ๋งŒ๋“ค์–ด์„œ ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ์ž‘๋™ํ•˜๋„๋ก ์ฒ˜๋ฆฌํ–ˆ์—ˆ๋‹ค.
  • ๋ฆฌ์•กํŠธ 18
    • ๋งˆ์šดํŠธ ๋˜๊ธฐ ์ง์ „์— effect๊ฐ€ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋˜๋Š” ๋ฌธ์ œ ์ˆ˜์ •๋จ. => ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‹ค์ œ๋กœ ํ™”๋ฉด์— ๋…ธ์ถœ๋  ๋•Œ effect๊ฐ€ ์‹คํ–‰๋œ๋‹ค
    • Supense๋กœ ์ธํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณด์ด๊ฑฐ๋‚˜ ์‚ฌ๋ผ์งˆ ๋•Œ๋„ effect๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰. ์ด์ „์—๋Š” ์ปดํฌ๋„ŒํŠธ ์Šค์Šค๋กœ๊ฐ€ Suspense์— ์˜ํ•ด ํ˜„์žฌ ๋ณด์—ฌ์ง€๊ณ  ์žˆ๋Š”์ง€ ์ˆจ๊ฒจ์ ธ ์žˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์—ˆ๋‹ค.
    • ์ด์ œ? useLayoutEffect์˜ effect์™€ cleanUp์ด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.
      • effect(componentDidMount)
      • cleanUp(componentWillUnmount)
    • Suspense๋ฅผ ์„œ๋ฒ„์—์„œ๋„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ.
    • Suspense์— ์“ฐ๋กœํ‹€๋ง์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹คํ•จ. ์ค‘์ฒฉ๋œ Suspense์˜ fallback์ด ์žˆ๋‹ค๋ฉด ์ž๋™์œผ๋กœ throttle ๋œ๋‹ค
  • ์š”๊ฑฐ ๋‚˜์˜จ๋‹ค ํ•œ๋‹ค. https://codesandbox.io/p/sandbox/sparkling-butterfly-s9zlw3?file=%2FAlbums.js%3A24%2C24

ํ™•์ธํ•  ๊ฒƒ

  • Suspense์— ์“ฐ๋กœํ‹€๋ง์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹คํ•จ. ์ค‘์ฒฉ๋œ Suspense์˜ fallback์ด ์žˆ๋‹ค๋ฉด ์ž๋™์œผ๋กœ throttle ๋œ๋‹ค