Skip to content

Instantly share code, notes, and snippets.

@easylogic
Created November 20, 2025 03:47
Show Gist options
  • Select an option

  • Save easylogic/b72a84dc2faadf4175573f123e40cd48 to your computer and use it in GitHub Desktop.

Select an option

Save easylogic/b72a84dc2faadf4175573f123e40cd48 to your computer and use it in GitHub Desktop.
renderer-dom-spec

renderer-dom ์ŠคํŽ™ ๋ฌธ์„œ

๋ณธ ๋ฌธ์„œ๋Š” @barocss/renderer-dom ํŒจํ‚ค์ง€์˜ ๊ธฐ์ˆ  ์ŠคํŽ™์ด๋‹ค. ๊ตฌํ˜„๊ณผ ํ…Œ์ŠคํŠธ๋Š” ๋ณธ ๋ฌธ์„œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•œ๋‹ค.

๋ชฉ์ฐจ

  1. ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์š”
  2. DSL ๊ทœ์น™
  3. VNode ๊ตฌ์กฐ
  4. Reconciliation: Render Phase
  5. Reconciliation: Commit Phase
  6. VNode ๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜
  7. ๋ฐ์ดํ„ฐ ์†์„ฑ ์ฒ˜๋ฆฌ
  8. ๋งˆํฌ์™€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
  9. ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ ๊ด€๋ฆฌ
  10. ํฌํ„ธ
  11. skipNodes ๊ธฐ๋Šฅ
  12. ๋กœ๊น… ์‹œ์Šคํ…œ
  13. ์ƒ์ˆ˜ ์ •์˜
  14. ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
  15. ์„ฑ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ
  16. ํ…Œ์ŠคํŠธ/๊ฒ€์ฆ ์›์น™
  17. ๊ธˆ์ง€ ์‚ฌํ•ญ

1. ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์š”

1.1 ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ

ModelData โ†’ VNodeBuilder โ†’ VNode Tree โ†’ Fiber Reconciliation โ†’ DOM
                                    โ†“
                            Render Phase (๋ณ€๊ฒฝ ๊ณ„์‚ฐ)
                                    โ†“
                            Commit Phase (DOM ์ ์šฉ)
  1. VNodeBuilder: ๋ชจ๋ธ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆœ์ˆ˜ํ•œ VNode ํŠธ๋ฆฌ๋กœ ๋ณ€ํ™˜
  2. Fiber Reconciliation: React-style ๋‘ ๋‹จ๊ณ„ reconciliation
    • Render Phase: ์ด์ „ VNode์™€ ์ƒˆ VNode๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ ๊ณ„์‚ฐ (DOM ์กฐ์ž‘ ์—†์Œ)
    • Commit Phase: ๊ณ„์‚ฐ๋œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์‹ค์ œ DOM์— ์ ์šฉ
  3. ComponentManager: sid ๊ธฐ๋ฐ˜์œผ๋กœ ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค์™€ ์ƒํƒœ๋ฅผ ์ „์—ญ ๊ด€๋ฆฌ

1.2 React-style Fiber Reconciliation

renderer-dom์€ React์˜ Fiber ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋‘ ๋‹จ๊ณ„ reconciliation์„ ์‚ฌ์šฉํ•œ๋‹ค.

Render Phase (๋ณ€๊ฒฝ ๊ณ„์‚ฐ)

๋ชฉ์ : ๋ณ€๊ฒฝ์‚ฌํ•ญ ๊ณ„์‚ฐ๋งŒ ์ˆ˜ํ–‰, DOM ์กฐ์ž‘ ์—†์Œ

์ž‘์—…:

  • VNode ๋น„๊ต (prevVNode vs nextVNode)
  • effectTag ์„ค์ • (EffectTag.PLACEMENT, EffectTag.UPDATE, EffectTag.DELETION)
  • DOM ์š”์†Œ ์ƒ์„ฑ/์ฐธ์กฐ (๋ถ€๋ชจ์— ์‚ฝ์ž…ํ•˜์ง€ ์•Š์Œ)
  • ์†์„ฑ/์Šคํƒ€์ผ ์„ค์ •ํ•˜์ง€ ์•Š์Œ

์ˆœ์„œ: child โ†’ sibling ์ˆœ์„œ๋กœ DFS ์ˆœํšŒ

Commit Phase (DOM ์ ์šฉ)

๋ชฉ์ : ๊ณ„์‚ฐ๋œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์‹ค์ œ DOM์— ์ ์šฉ

์ž‘์—…:

  • effectTag์— ๋”ฐ๋ผ DOM ์‚ฝ์ž…/์žฌ๋ฐฐ์น˜/์ œ๊ฑฐ
  • ์†์„ฑ/์Šคํƒ€์ผ diff ๋ฐ ์—…๋ฐ์ดํŠธ
  • Component lifecycle ํ˜ธ์ถœ (mountComponent, updateComponent, unmountComponent)
  • Text node ์ฒ˜๋ฆฌ

์ˆœ์„œ: child โ†’ sibling ์ˆœ์„œ๋กœ DFS ์ˆœํšŒ

1.3 ํ•ต์‹ฌ ์›์น™

  • VNode ์ˆœ์ˆ˜์„ฑ: VNode๋Š” DOM ํ‘œ์‹(data-bc-*, data-decorator-*)์„ ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋Š” Reconciler ๋‹จ๊ณ„์—์„œ๋งŒ DOM์— ๋ถ€์ฐฉ๋œ๋‹ค.
  • sid ๊ธฐ๋ฐ˜ ์•ˆ์ •์„ฑ: sid๋Š” React์˜ key์ฒ˜๋Ÿผ DOM ์žฌ์‚ฌ์šฉ์˜ ๊ธฐ์ค€์ด๋‹ค. ๋™์ผ sid๋Š” ๋™์ผ DOM ์š”์†Œ์™€ ์ƒํƒœ ์ธ์Šคํ„ด์Šค๋ฅผ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ „์ฒด ๋ฌธ์„œ ์žฌ๋นŒ๋“œ: ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ํ•ญ์ƒ ์ „์ฒด ๋ฌธ์„œ๋ฅผ ์žฌ๋นŒ๋“œํ•˜๊ณ  prevVNode์™€ nextVNode๋ฅผ ๋น„๊ตํ•˜์—ฌ ์ตœ์†Œ ๋ณ€๊ฒฝ๋งŒ ์ ์šฉํ•œ๋‹ค. ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ API๋Š” ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋‘ ๋‹จ๊ณ„ ๋ถ„๋ฆฌ: Render Phase์™€ Commit Phase๋Š” ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‹ค. Render Phase์—์„œ๋Š” DOM ์กฐ์ž‘์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

2. DSL ๊ทœ์น™

2.1 ํ…œํ”Œ๋ฆฟ ์ •์˜

define(stype: string, template: ElementTemplate | ContextualComponent)

ํ…œํ”Œ๋ฆฟ์„ ๋“ฑ๋กํ•œ๋‹ค.

์‹œ๊ทธ๋‹ˆ์ฒ˜:

define<P, M, C>(
  stype: string,
  template: ElementTemplate | ContextualComponent<P, M, C>
): void

๊ทœ์น™:

  • stype์€ ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด ์‹๋ณ„์ž์—ฌ์•ผ ํ•œ๋‹ค.
  • ํ…œํ”Œ๋ฆฟ ํ•จ์ˆ˜๋Š” (props: P, model: M, context: C) => ElementTemplate ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.
  • ํ…œํ”Œ๋ฆฟ ํ•จ์ˆ˜๋Š” ํ•ญ์ƒ ElementTemplate์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.
  • props์™€ model์€ ์ ˆ๋Œ€ ํ˜ผํ•ฉํ•˜์ง€ ์•Š๋Š”๋‹ค. props๋Š” ์ˆœ์ˆ˜ ๋ฐ์ดํ„ฐ, model์€ ์›๋ณธ ๋ชจ๋ธ์ด๋‹ค.
  • context.model์€ ์›๋ณธ ๋ชจ๋ธ์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

2.2 ์—˜๋ฆฌ๋จผํŠธ ํ…œํ”Œ๋ฆฟ

element(tag: string | Function, attrs?: Record<string, any>, children?: Array<any>)

์—˜๋ฆฌ๋จผํŠธ ํ…œํ”Œ๋ฆฟ์„ ์ƒ์„ฑํ•œ๋‹ค.

๊ทœ์น™:

  • tag๋Š” ๋ฌธ์ž์—ด ๋˜๋Š” ํ•จ์ˆ˜(๋™์  ํƒœ๊ทธ)์ผ ์ˆ˜ ์žˆ๋‹ค.
  • attrs์˜ ์ •์ /๋™์  ์†์„ฑ์€ ๊ทธ๋Œ€๋กœ ๋ณด์กด๋˜์–ด VNode์— ๋ฐ˜์˜๋œ๋‹ค.
  • ๋„ค์ž„์ŠคํŽ˜์ด์Šค(SVG/MathML)๋Š” DOM ๋‹จ๊ณ„์—์„œ ์ž๋™ ์ฒ˜๋ฆฌ๋œ๋‹ค.
  • children์€ ElementTemplate, DataTemplate, ๋˜๋Š” slot() ๊ฒฐ๊ณผ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค.

2.3 ์ž์‹ ํ™•์žฅ: slot(key: string)

๊ทœ์น™

  • ํ•˜์œ„ ๋ชจ๋ธ(model.content: [])์„ VNode๋กœ ํ™•์žฅํ•˜๋Š” ์œ ์ผํ•œ ๊ฒฝ๋กœ์ด๋‹ค.
  • Reconciler๋Š” slot ์ง€์ ์—์„œ ์ž์‹๋“ค์„ ์žฌ๊ท€์ ์œผ๋กœ ๋นŒ๋“œ/๋ฆฌ์ปจ์‹คํ•œ๋‹ค.
  • data(key)๋Š” ๋ฐฐ์—ด ์›๋ณธ ์ ‘๊ทผ๋งŒ ์ œ๊ณตํ•˜๋ฉฐ, children ํ™•์žฅ์—๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

์˜ˆ์‹œ:

define('list', element('ul', {}, [slot('content')]));

// model.content ๋ฐฐ์—ด์ด ์ž๋™์œผ๋กœ ํ™•์žฅ๋จ
const model = {
  sid: 'list1',
  stype: 'list',
  content: [
    { sid: 'item1', stype: 'listItem', text: 'Item 1' }
  ]
};

2.4 ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง: when(condition, thenTemplate, elseTemplate?)

๊ทœ์น™:

  • condition์€ ํ•จ์ˆ˜ ๋˜๋Š” ๋ถˆ๋ฆฌ์–ธ ๊ฐ’์ด๋‹ค.
  • thenTemplate์€ ์กฐ๊ฑด์ด ์ฐธ์ผ ๋•Œ ๋ Œ๋”๋งํ•  ํ…œํ”Œ๋ฆฟ์ด๋‹ค.
  • elseTemplate์€ ์„ ํƒ์ ์ด๋ฉฐ, ์กฐ๊ฑด์ด ๊ฑฐ์ง“์ผ ๋•Œ ๋ Œ๋”๋ง๋œ๋‹ค.

2.5 ๋ฐ˜๋ณต ๋ Œ๋”๋ง: each(items, itemTemplate, keyFn?)

๊ทœ์น™:

  • items๋Š” ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ ๋˜๋Š” ๋ฐฐ์—ด์ด๋‹ค.
  • itemTemplate์€ ๊ฐ ํ•ญ๋ชฉ์— ๋Œ€ํ•ด ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜ (item, index) => ElementTemplate์ด๋‹ค.
  • keyFn์€ ์„ ํƒ์ ์ด๋ฉฐ, ๊ฐ ํ•ญ๋ชฉ์˜ ๊ณ ์œ  ํ‚ค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค. ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด sid๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

2.6 ๋งˆํฌ/๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋“ฑ๋ก

defineMark(name: string, template: ElementTemplate)

ํ…์ŠคํŠธ ๋งˆํฌ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.

defineDecorator(name: string, template: ElementTemplate)

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.


3. VNode ๊ตฌ์กฐ

3.1 ๊ธฐ๋ณธ ํ•„๋“œ

interface VNode {
  tag?: string;                    // HTML ํƒœ๊ทธ๋ช… ๋˜๋Š” VNodeTag.TEXT, VNodeTag.PORTAL
  text?: string | number;          // ํ…์ŠคํŠธ ๋…ธ๋“œ์˜ ํ…์ŠคํŠธ ๋‚ด์šฉ
  attrs?: Record<string, any>;     // HTML ์†์„ฑ (data-bc-*, data-decorator-* ์ œ์™ธ)
  style?: Record<string, any>;     // ์ธ๋ผ์ธ ์Šคํƒ€์ผ
  children?: Array<string | number | VNode>;
  key?: string;                    // VNode ๋งค์นญ์šฉ ํ‚ค (DOM์— ์ €์žฅ๋˜์ง€ ์•Š์Œ)
}

3.2 ์ปดํฌ๋„ŒํŠธ ์‹๋ณ„์ž

interface ComponentVNode extends VNode {
  sid?: string;                    // ๋ชจ๋ธ์˜ ๊ณ ์œ  ์‹๋ณ„์ž
  stype?: string;                  // ์ปดํฌ๋„ŒํŠธ ํƒ€์ž… (ํ…œํ”Œ๋ฆฟ ์„ ํƒ)
  props?: Record<string, any>;     // ์ˆœ์ˆ˜ ๋ฐ์ดํ„ฐ (props์™€ model์€ ๋ถ„๋ฆฌ๋จ)
  model?: Record<string, any>;     // ์›๋ณธ ๋ชจ๋ธ ๋ฐ์ดํ„ฐ
}

๊ทœ์น™:

  • sid: ๋ชจ๋ธ์˜ ๊ณ ์œ  ์‹๋ณ„์ž. ๋ชจ๋ธ์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๋ฉฐ ์ƒ์„ฑ/๋ณ€ํ˜•ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • stype: ์ปดํฌ๋„ŒํŠธ ํƒ€์ž…. ํ…œํ”Œ๋ฆฟ ์„ ํƒ์— ์‚ฌ์šฉ๋œ๋‹ค.
  • props: ์ˆœ์ˆ˜ ๋ฐ์ดํ„ฐ (props์™€ model์€ ๋ถ„๋ฆฌ๋จ)
  • model: ์›๋ณธ ๋ชจ๋ธ ๋ฐ์ดํ„ฐ

3.3 Text VNode

interface TextVNode extends VNode {
  tag: VNodeTag.TEXT;              // '#text'
  text: string | number;
  children?: never;                 // Text VNode๋Š” children์„ ๊ฐ€์งˆ ์ˆ˜ ์—†์Œ
}

๊ทœ์น™:

  • tag: VNodeTag.TEXT๋กœ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œ์‹œ
  • createTextVNode(text)๋กœ ์ƒ์„ฑ
  • data('text')๋Š” ํ•ญ์ƒ <span>์œผ๋กœ ๊ฐ์‹ธ์ง„ ๊ตฌ์กฐ๋ฅผ ์ƒ์„ฑ (ํŽธ์ง‘ ์ผ๊ด€์„ฑ)

3.4 ๋งˆํฌ/๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ฐธ์กฐ

interface MarkedVNode extends VNode {
  marks?: Array<{
    type: string;
    range?: [number, number];
    attrs?: Record<string, any>;
  }>;
  decorators?: unknown[];
}

๋งˆํฌ ๊ทœ์น™:

  • range: [start, end] ํ˜•์‹์œผ๋กœ ํ…์ŠคํŠธ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•œ๋‹ค.
  • ๋งˆํฌ๋Š” ํ…์ŠคํŠธ ๋…ธ๋“œ์—๋งŒ ์ ์šฉ๋œ๋‹ค.

3.5 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ •๋ณด ์ €์žฅ

์ค‘์š” ๋ณ€๊ฒฝ์‚ฌํ•ญ:

  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ •๋ณด๋Š” VNode ์ตœ์ƒ์œ„์— decoratorSid, decoratorStype ๋“ฑ์œผ๋กœ ์ €์žฅ๋˜์ง€๋งŒ,
  • DOM์—๋Š” attrs['data-decorator-sid'], attrs['data-decorator-stype'] ๋“ฑ์œผ๋กœ ์ €์žฅ๋œ๋‹ค.
  • Reconciler๊ฐ€ VNode์˜ ์ตœ์ƒ์œ„ ์ •๋ณด๋ฅผ DOM ์†์„ฑ์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
interface DecoratorVNode extends VNode {
  decoratorSid?: string;
  decoratorStype?: string;
  decoratorCategory?: 'inline' | 'block' | 'layer' | string;
  decoratorPosition?: 'before' | 'after' | 'inside' | string;
  decoratorModel?: Record<string, any>;
}

๊ทœ์น™:

  • decoratorCategory์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด ๋‹ค๋ฅด๋‹ค:
    • inline: ํ…์ŠคํŠธ ๋ฒ”์œ„์— ์ ์šฉ
    • block: ์ปดํฌ๋„ŒํŠธ VNode ์•ž/๋’ค์— ์‚ฝ์ž…
    • layer: ๋ ˆ์ด์–ด ์˜ค๋ฒ„๋ ˆ์ด
  • ๋ธ”๋ก/๋ ˆ์ด์–ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์ปดํฌ๋„ŒํŠธ VNode์—๋งŒ ์ ์šฉ๋œ๋‹ค (๋งˆํฌ VNode์— ์ ์šฉ ๊ธˆ์ง€).

3.6 ํฌํ„ธ VNode

interface PortalVNode extends VNode {
  tag: VNodeTag.PORTAL;            // 'portal'
  portal?: {
    target: HTMLElement | (() => HTMLElement) | string;
    template: any;
    portalId?: string;
  };
}

๊ทœ์น™:

  • target์€ HTMLElement, ํ•จ์ˆ˜, ๋˜๋Š” CSS ์„ ํƒ์ž ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์žˆ๋‹ค.
  • portalId๊ฐ€ ์ œ๊ณต๋˜๋ฉด ๋™์ผ ID๋กœ ํ˜ธ์ŠคํŠธ๋ฅผ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

4. Reconciliation: Render Phase

4.1 ๊ฐœ์š”

Render Phase๋Š” ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ณ„์‚ฐ๋งŒ ํ•˜๊ณ  DOM ์กฐ์ž‘์€ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ž…๋ ฅ:

reconcileWithFiber(
  container: HTMLElement,
  vnode: VNode,
  prevVNode: VNode | undefined,
  context: any,
  deps: FiberReconcileDependencies,
  onComplete?: () => void
): void

FiberReconcileDependencies:

interface FiberReconcileDependencies {
  dom: DOMOperations;
  components: ComponentManager;
  currentVisitedPortalIds: Set<string> | null;
  portalHostsById: Map<string, { target: HTMLElement; host: HTMLElement }>;
  rootModel?: any;
  prevVNodeTree?: Map<string, VNode>;
  rootSid?: string;
  context?: any;
  skipNodes?: Set<string>;  // ์ž…๋ ฅ ์ค‘์ธ ๋…ธ๋“œ ๋ชฉ๋ก (reconcile ์Šคํ‚ต)
}

4.2 Fiber ํŠธ๋ฆฌ ์ƒ์„ฑ

const rootFiber = createFiberTree(container, vnode, prevVNode, context);

FiberNode ๊ตฌ์กฐ:

interface FiberNode {
  vnode: VNode;
  prevVNode: VNode | undefined;
  domElement: HTMLElement | Text | null;
  parent: HTMLElement;
  parentFiber: FiberNode | null;
  child: FiberNode | null;
  sibling: FiberNode | null;
  return: FiberNode | null;
  effectTag: EffectTagType;  // PLACEMENT, UPDATE, DELETION, ๋˜๋Š” null
  context: any;
  index: number;
  primitiveTextChildren?: Array<{ text: string | number; index: number }>;
}

4.3 renderFiberNode ๋™์ž‘

ํ•ต์‹ฌ ์›์น™:

  • DOM ์กฐ์ž‘ ์—†์Œ (์ƒ์„ฑ๋งŒ)
  • ์†์„ฑ/์Šคํƒ€์ผ ์„ค์ • ์—†์Œ
  • effectTag ์„ค์ •๋งŒ

์ฃผ์š” ๋‹จ๊ณ„:

  1. skipNodes ์ฒดํฌ

    const sid = getVNodeId(vnode);
    if (sid && skipNodes?.has(sid)) {
      // ์ด์ „ VNode์™€ DOM ์œ ์ง€
      // effectTag ์„ค์ • ์•ˆ ํ•จ
      return;
    }
  2. ID ์ „๋‹ฌ ๋ฐ ์ƒ์„ฑ

    transferVNodeIdFromPrev(vnode, prevVNode);
    generateVNodeIdIfNeeded(vnode, fiber, components);
  3. Portal ์ฒ˜๋ฆฌ

    • Portal์€ ๋ณ„๋„ FiberScheduler๋กœ ์ฒ˜๋ฆฌ
  4. ํƒ€์ž… ๋น„๊ต

    const prevType = prevVNode ? (isTextVNode(prevVNode) ? 'text' : 'host') : null;
    const nextType = isTextVNode(vnode) ? 'text' : 'host';
    const typeChanged = prevType !== null && prevType !== nextType;
  5. effectTag ์„ค์ •

    if (!prevVNode) {
      fiber.effectTag = EffectTag.PLACEMENT;
    } else if (typeChanged) {
      fiber.effectTag = EffectTag.PLACEMENT;  // ํƒ€์ž… ๋ณ€๊ฒฝ ์‹œ ์žฌ์ƒ์„ฑ
    } else {
      fiber.effectTag = EffectTag.UPDATE;
    }
  6. DOM ์š”์†Œ ์ƒ์„ฑ/์ฐธ์กฐ

    if (prevVNode?.meta?.domElement && !typeChanged) {
      // ๊ธฐ์กด DOM ์žฌ์‚ฌ์šฉ
      domElement = prevVNode.meta.domElement;
    } else if (vnode.tag === VNodeTag.TEXT) {
      // Text ๋…ธ๋“œ ์ƒ์„ฑ
      domElement = document.createTextNode(String(vnode.text));
    } else if (vnode.tag) {
      // Host ์š”์†Œ ์ƒ์„ฑ
      domElement = dom.createSimpleElement(vnode.tag);
    }
    
    vnode.meta = vnode.meta || {};
    vnode.meta.domElement = domElement;
    fiber.domElement = domElement;

์ค‘์š”:

  • DOM ์š”์†Œ๋ฅผ ์ƒ์„ฑํ•˜์ง€๋งŒ ๋ถ€๋ชจ์— ์‚ฝ์ž…ํ•˜์ง€ ์•Š์Œ
  • ์†์„ฑ/์Šคํƒ€์ผ์„ ์„ค์ •ํ•˜์ง€ ์•Š์Œ
  • mountComponent, updateComponent๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Œ

4.4 FiberScheduler

์—ญํ• :

  • Render Phase๋ฅผ ๋น„๋™๊ธฐ๋กœ ์Šค์ผ€์ค„๋ง
  • ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ๋™๊ธฐ ๋ชจ๋“œ ์ง€์›

ํ๋ฆ„:

const scheduler = new FiberScheduler(fiberRender, () => {
  // Render Phase ์™„๋ฃŒ ํ›„ Commit Phase ์‹คํ–‰
  commitFiberTree(rootFiber, deps, context);
  if (onComplete) onComplete();
});
scheduler.scheduleWork(rootFiber, FiberPriority.Normal);

5. Reconciliation: Commit Phase

5.1 ๊ฐœ์š”

Commit Phase๋Š” Render Phase์—์„œ ๊ณ„์‚ฐ๋œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์‹ค์ œ DOM์— ์ ์šฉํ•œ๋‹ค.

์ž…๋ ฅ:

commitFiberTree(
  rootFiber: FiberNode,
  deps: FiberReconcileDependencies,
  context: any
): void

5.2 commitFiberTree ๋™์ž‘

์ˆœ์„œ:

  • child โ†’ sibling ์ˆœ์„œ๋กœ DFS ์ˆœํšŒ
  • ๊ฐ Fiber ๋…ธ๋“œ์— ๋Œ€ํ•ด commitFiberNode ํ˜ธ์ถœ

5.3 commitFiberNode ๋™์ž‘

์ฃผ์š” ๋‹จ๊ณ„:

  1. skipNodes ์ฒดํฌ

    const sid = getVNodeId(vnode);
    if (sid && skipNodes?.has(sid)) {
      // DOM ์—…๋ฐ์ดํŠธ ์•ˆ ํ•จ
      return;
    }
  2. effectTag๊ฐ€ ์—†์œผ๋ฉด ์Šคํ‚ต

    if (!fiber.effectTag) {
      return;
    }
  3. DELETION ์ฒ˜๋ฆฌ

    if (fiber.effectTag === EffectTag.DELETION && prevVNode) {
      components.unmountComponent(prevVNode, context);
      if (prevHost.parentNode) {
        prevHost.parentNode.removeChild(prevHost);
      }
    }
  4. PLACEMENT ์ฒ˜๋ฆฌ (DOM ์‚ฝ์ž…)

    if (fiber.effectTag === EffectTag.PLACEMENT) {
      const actualParent = getActualParent(fiber);
      const before = getHostSibling(fiber);
      
      // before๊ฐ€ actualParent์˜ ์ž์‹์ธ์ง€ ํ™•์ธ
      if (before && before.parentNode !== actualParent) {
        before = null;
      }
      
      actualParent.insertBefore(domElement, before);
      
      // Component lifecycle: mount
      if (vnode.stype && domElement instanceof HTMLElement) {
        components.mountComponent(vnode, domElement, context);
      }
    }
  5. UPDATE ์ฒ˜๋ฆฌ (์†์„ฑ/์Šคํƒ€์ผ ์—…๋ฐ์ดํŠธ)

    if (fiber.effectTag === EffectTag.UPDATE) {
      if (domElement instanceof HTMLElement) {
        // ์†์„ฑ ์—…๋ฐ์ดํŠธ
        dom.updateAttributes(domElement, prevVNode?.attrs, vnode.attrs);
        
        // ์Šคํƒ€์ผ ์—…๋ฐ์ดํŠธ
        dom.updateStyles(domElement, prevVNode?.style, vnode.style);
      }
      
      // Text ๋…ธ๋“œ ์—…๋ฐ์ดํŠธ
      if (domElement instanceof Text && vnode.text !== undefined) {
        if (domElement.textContent !== String(vnode.text)) {
          domElement.textContent = String(vnode.text);
        }
      }
      
      // Component lifecycle: update
      if (vnode.stype && !context.__isReconciling) {
        components.updateComponent(prevVNode, vnode, domElement, context);
      }
    }
  6. Text ์ฒ˜๋ฆฌ

    if (domElement instanceof HTMLElement && vnode.text !== undefined && !vnode.children?.length) {
      handleVNodeTextProperty(domElement, vnode, prevVNode);
    }

5.4 getHostSibling (React ์•Œ๊ณ ๋ฆฌ์ฆ˜)

๋ชฉ์ : ๋‹ค์Œ ํ˜•์ œ์˜ DOM ๋…ธ๋“œ๋ฅผ ์ฐพ์•„ insertBefore์˜ referenceNode๋กœ ์‚ฌ์šฉ

์•Œ๊ณ ๋ฆฌ์ฆ˜:

function getHostSibling(fiber: FiberNode): HTMLElement | Text | null {
  let sibling = fiber.sibling;
  while (sibling) {
    if (sibling.domElement) {
      return sibling.domElement;
    }
    if (sibling.child) {
      let child = sibling.child;
      while (child) {
        if (child.domElement) {
          return child.domElement;
        }
        child = child.child;
      }
    }
    sibling = sibling.sibling;
  }
  return null;
}

ํ•ต์‹ฌ:

  • ๋‹ค์Œ ํ˜•์ œ์˜ DOM ๋…ธ๋“œ๋ฅผ ์ฐพ์Œ
  • ๋‹ค์Œ ํ˜•์ œ๊ฐ€ ์—†์œผ๋ฉด null ๋ฐ˜ํ™˜ (appendChild์™€ ๋™์ผ)
  • Render Phase์—์„œ domElement๋ฅผ ๋ฏธ๋ฆฌ ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅ

5.5 removeStaleChildren

๋ชฉ์ : ์‚ฌ์šฉ๋˜์ง€ ์•Š์€ prev DOM ์š”์†Œ ์ œ๊ฑฐ

๋™์ž‘:

function removeStaleChildren(fiber: FiberNode, deps: FiberReconcileDependencies): void {
  const host = fiber.domElement;
  if (!host || !(host instanceof HTMLElement)) return;
  
  // ์‚ฌ์šฉ๋œ DOM ์š”์†Œ ์ถ”์ 
  const usedDomElements = new Set<HTMLElement | Text>();
  // ... vnode.children์„ ์ˆœํšŒํ•˜๋ฉฐ ์‚ฌ์šฉ๋œ DOM ์š”์†Œ ์ˆ˜์ง‘
  
  // ์‚ฌ์šฉ๋˜์ง€ ์•Š์€ DOM ์š”์†Œ ์ œ๊ฑฐ
  const childNodes = Array.from(host.childNodes);
  for (const node of childNodes) {
    if (!usedDomElements.has(node as HTMLElement | Text)) {
      host.removeChild(node);
    }
  }
}

5.6 processPrimitiveTextChildren

๋ชฉ์ : Primitive text children (๋ฌธ์ž์—ด/์ˆซ์ž) ์ฒ˜๋ฆฌ

๋™์ž‘:

  • VNode children ์ค‘ ๋ฌธ์ž์—ด/์ˆซ์ž๋ฅผ Text ๋…ธ๋“œ๋กœ ๋ณ€ํ™˜
  • ์˜ฌ๋ฐ”๋ฅธ ์œ„์น˜์— ์‚ฝ์ž…

6. VNode ๋งค์นญ ์•Œ๊ณ ๋ฆฌ์ฆ˜

6.1 Child Matching Matrix

Fiber ์ƒ์„ฑ ์‹œ prev/next VNode ์ž์‹ ๋งค์นญ์€ ์•„๋ž˜ ์šฐ์„ ์ˆœ์œ„๋กœ ์ง„ํ–‰๋œ๋‹ค.

์šฐ์„ ์ˆœ์œ„ ์กฐ๊ฑด ๋งค์นญ ๊ธฐ์ค€ ๊ฒฐ๊ณผ
1 getVNodeId(childVNode)๊ฐ€ truthy (sid, key, attrs['data-decorator-sid'] ๋“ฑ) prevVNode.children ์ค‘ ๋™์ผ ID, ์•„์ง ๋งค์นญ๋˜์ง€ ์•Š์€ ํ•ญ๋ชฉ ๊ฒ€์ƒ‰ DOM/์ปดํฌ๋„ŒํŠธ ์™„์ „ ์žฌ์‚ฌ์šฉ. ์œ„์น˜๊ฐ€ ๋‹ฌ๋ผ๋„ ํ˜„์žฌ ์ธ๋ฑ์Šค๋กœ ์ด๋™.
2 ๋ช…์‹œ ID ์—†์Œ, transferVNodeIdFromPrev๊ฐ€ stype ๊ธฐ๋ฐ˜ ID๋ฅผ ๋ถ€์—ฌ prev ์ž์‹์˜ stype์ด ๊ฐ™๊ณ  ID๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ID๋ฅผ ๋ณต์‚ฌ โ†’ 1๋‹จ๊ณ„์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ ์ปดํฌ๋„ŒํŠธ/Decorator๊ฐ€ ๋ช…์‹œ ID ์—†์ด๋„ ์•ˆ์ •์ ์œผ๋กœ ์žฌ์‚ฌ์šฉ๋จ.
3 ID ์—†์Œ, ๊ฐ™์€ ์ธ๋ฑ์Šค์— prev ์ž์‹ ์กด์žฌ prev.children[i]์˜ tag์™€ ํ˜„์žฌ tag๊ฐ€ ๊ฐ™๊ฑฐ๋‚˜ ๋‘˜ ๋‹ค ํ…์ŠคํŠธ (VNodeTag.TEXT) ๋™์ผ ํƒ€์ž…์ผ ๋•Œ๋งŒ DOM ์žฌ์‚ฌ์šฉ. ํƒ€์ž…์ด ๋‹ค๋ฅด๋ฉด ๋งค์นญ ์‹คํŒจ.
4 ID/์ธ๋ฑ์Šค ๋งค์นญ ์‹คํŒจ, ๋‘˜ ๋‹ค Host VNode prev children ์ „์ฒด๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ tag + ํด๋ž˜์Šค ์กฐํ•ฉ์ด ๊ฐ™์€ ํ•ญ๋ชฉ ํƒ์ƒ‰ (์ด๋ฏธ ๋งค์นญ๋œ VNode ์ œ์™ธ) mark/decorator wrapper์ฒ˜๋Ÿผ ID๊ฐ€ ์—†๋Š” Host ๋…ธ๋“œ๋„ ์•ˆ์ •์ ์œผ๋กœ ์žฌ์‚ฌ์šฉ.
5 ์–ด๋А ์กฐ๊ฑด์—๋„ ํ•ด๋‹นํ•˜์ง€ ์•Š์Œ ๋งค์นญ ์‹คํŒจ๋กœ ๊ฐ„์ฃผ Render Phase์—์„œ ์ƒˆ DOM ์ƒ์„ฑ, Commit Phase์—์„œ ๊ธฐ์กด DOM ์ œ๊ฑฐ ํ›„ ์‚ฝ์ž….

์ถ”๊ฐ€ ๊ทœ์น™:

  • ํ…์ŠคํŠธ ์ž์‹์€ tag: VNodeTag.TEXT๋กœ ํ†ต์ผํ•ด 3๋‹จ๊ณ„์—์„œ ํƒ€์ž… ๊ฒ€์‚ฌ๊ฐ€ ์ผ๊ด€์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.
  • generateVNodeIdIfNeeded๊ฐ€ Host/Text VNode์— tag-index ๊ธฐ๋ฐ˜ auto ID๋ฅผ ๋ถ€์—ฌํ•ด ๋™์ผ ๊ตฌ์กฐ๊ฐ€ ๋ฐ˜๋ณต๋  ๋•Œ๋„ ๋งค์นญ์ด ์•ˆ์ •์ ์ด๋‹ค.
  • Render Phase๋Š” ๋งค์นญ ๊ฒฐ๊ณผ๋งŒ ๊ณ„์‚ฐํ•˜๊ณ  DOM ์ด๋™/์‚ญ์ œ๋Š” ํ•˜์ง€ ์•Š๋Š”๋‹ค. Commit Phase์—์„œ commitFiberTree๊ฐ€ effectTag์— ๋”ฐ๋ผ DOM์„ ์‚ฝ์ž…ยท์—…๋ฐ์ดํŠธยท์‚ญ์ œํ•˜๊ณ , ๋งˆ์ง€๋ง‰์— removeStaleChildren/processPrimitiveTextChildren๊ฐ€ ๋‚จ์€ ๋™๊ธฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

6.2 Matching Flow Diagram

flowchart TD
  A[Next child VNode] --> B{getVNodeId ์กด์žฌ}
  B -->|Yes| C[prev.children ์ค‘ ๋™์ผ ID & ๋ฏธ๋งค์นญ ํ•ญ๋ชฉ]
  C --> D[DOM ์žฌ์‚ฌ์šฉ<br/>effectTag = UPDATE]
  B -->|No| E{transferVNodeIdFromPrev ๊ฒฐ๊ณผ ID}
  E -->|Yes| C
  E -->|No| F{๊ฐ™์€ ์ธ๋ฑ์Šค prev child}
  F -->|Yes, tag ๋™์ผ| D
  F -->|No| G{Host ๊ตฌ์กฐ ๋งค์นญ}
  G -->|Yes| H[prev ์ „์ฒด ์ˆœํšŒ<br/>tagยทclass ๋™์ผ ๋…ธ๋“œ]
  H --> D
  G -->|No| I[์ƒˆ Fiber/DOM ์ƒ์„ฑ<br/>effectTag = PLACEMENT]
  D --> J[Commit Phase์—์„œ ๊ธฐ์กด DOM ์œ„์น˜๋ณด์ •]
  I --> K[Commit Phase์—์„œ ์ƒˆ DOM ์‚ฝ์ž…]
  J --> L[removeStaleChildren๋กœ ์‚ฌ์šฉ๋˜์ง€ ์•Š์€ prev DOM ์ œ๊ฑฐ]
  K --> L
Loading

7. ๋ฐ์ดํ„ฐ ์†์„ฑ ์ฒ˜๋ฆฌ

7.1 DOM ํ‘œ์‹ ๊ทœ์น™

๊ทœ์น™:

  • data-bc-sid, data-bc-stype ๋“ฑ ๋ชจ๋“  data-* ํ‘œ์‹์€ Reconciler๊ฐ€ DOM์—์„œ๋งŒ ๋ถ€์ฐฉ/๊ฐฑ์‹ ํ•œ๋‹ค.
  • VNode์—๋Š” sid, stype ๋“ฑ ์‹๋ณ„ ์ •๋ณด๋งŒ ์ตœ์ƒ์œ„๋กœ ๊ฐ€์ง„๋‹ค.
  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ •๋ณด๋Š” VNode ์ตœ์ƒ์œ„์— ์žˆ์ง€๋งŒ, DOM์—๋Š” attrs['data-decorator-*']๋กœ ์ €์žฅ๋œ๋‹ค.

7.2 ์†์„ฑ ๋ถ€์ฐฉ ์‹œ์ 

  • ๋ฃจํŠธ ํ˜ธ์ŠคํŠธ: Commit Phase์—์„œ data-bc-sid, data-bc-stype ๋ถ€์ฐฉ
  • ์ž์‹ ํ˜ธ์ŠคํŠธ: Commit Phase์—์„œ ๊ฐ ์ž์‹์— ๋ถ€์ฐฉ
  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ: Commit Phase์—์„œ data-decorator-* ์†์„ฑ์„ ๋ถ€์ฐฉ/๊ฐฑ์‹ 

7.3 ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ฒ˜๋ฆฌ

๊ทœ์น™:

  • SVG, MathML ๋“ฑ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๊ฐ€ ํ•„์š”ํ•œ ์š”์†Œ๋Š” ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค.
  • DOMOperations.createSimpleElement(tag, parent?)๊ฐ€ ๋ถ€๋ชจ ์š”์†Œ์˜ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์ƒ์†ํ•œ๋‹ค.
  • DOMOperations.setAttributeWithNamespace๊ฐ€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ๊ณ ๋ คํ•˜์—ฌ ์†์„ฑ์„ ์„ค์ •ํ•œ๋‹ค.
  • xlink:href ๋“ฑ ํŠน์ˆ˜ ์†์„ฑ๋„ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ๊ณ ๋ คํ•˜์—ฌ ์ฒ˜๋ฆฌ๋œ๋‹ค.

8. ๋งˆํฌ์™€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ

8.1 ๋งˆํฌ ์ฒ˜๋ฆฌ

๊ทœ์น™:

  • ๋งˆํฌ๋Š” ํ…์ŠคํŠธ ๋…ธ๋“œ์—๋งŒ ์ ์šฉ๋œ๋‹ค.
  • range: [start, end] ํ˜•์‹์œผ๋กœ ํ…์ŠคํŠธ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•œ๋‹ค.
  • ์—ฌ๋Ÿฌ ๋งˆํฌ๊ฐ€ ๊ฒน์น  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋œ๋‹ค.

8.2 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ฒ˜๋ฆฌ

๊ทœ์น™:

  • ์ธ๋ผ์ธ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ: ํ…์ŠคํŠธ ๋ฒ”์œ„์— ์ ์šฉ
  • ๋ธ”๋ก ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ: ์ปดํฌ๋„ŒํŠธ VNode ์•ž/๋’ค์— ์‚ฝ์ž… (position: 'before' | 'after')
  • ๋ ˆ์ด์–ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ: ์˜ค๋ฒ„๋ ˆ์ด๋กœ ์ ์šฉ
  • ๋ธ”๋ก/๋ ˆ์ด์–ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์ปดํฌ๋„ŒํŠธ VNode์—๋งŒ ์ ์šฉ๋œ๋‹ค (๋งˆํฌ VNode์— ์ ์šฉ ๊ธˆ์ง€).
  • ์ธ๋ผ์ธ ๋งˆํฌ์™€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋™์‹œ์— ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๊ฒน์นจ/๋ถ„ํ•  ์ผ€์ด์Šค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค๋ฃฌ๋‹ค.

8.3 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์œ„์น˜ ๊ทœ์น™

๊ทœ์น™:

  • decoratorPosition์„ ๊ธฐ์ค€์œผ๋กœ ์‚ฝ์ž… ์œ„์น˜๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค:
    • 'before': ์ปดํฌ๋„ŒํŠธ VNode ์•ž์— ์‚ฝ์ž…
    • 'after': ์ปดํฌ๋„ŒํŠธ VNode ๋’ค์— ์‚ฝ์ž…
    • 'inside': ์ปดํฌ๋„ŒํŠธ VNode ๋‚ด๋ถ€์— ์‚ฝ์ž…

8.4 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ •๋ณด ์ €์žฅ

์ค‘์š”:

  • VNode ์ตœ์ƒ์œ„์— decoratorSid, decoratorStype ๋“ฑ์œผ๋กœ ์ €์žฅ
  • DOM์—๋Š” attrs['data-decorator-sid'], attrs['data-decorator-stype'] ๋“ฑ์œผ๋กœ ์ €์žฅ
  • getVNodeId()๋Š” vnode.sid์™€ vnode.attrs?.[DOMAttribute.DECORATOR_SID]๋ฅผ ๋ชจ๋‘ ํ™•์ธ

9. ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ ๊ด€๋ฆฌ

9.1 ์ƒํƒœ ํด๋ž˜์Šค ์ •์˜

class MyState extends BaseComponentState {
  initState(initial: any): void {
    // ์„ ํƒ: ์ดˆ๊ธฐ ์ƒํƒœ ์„ค์ •
    this.data = { ...initial };
  }
  
  snapshot(): Record<string, any> {
    // ์„ ํƒ: ์Šค๋ƒ…์ƒท ์ƒ์„ฑ (๋ฏธ์ œ๊ณต ์‹œ ์–•์€ ๋ณต์‚ฌ ์‚ฌ์šฉ)
    return { ...this.data };
  }
  
  set(patch: Record<string, any>): void {
    // ๋ณ€๊ฒฝ ๋ˆ„์  ํ›„ changeState ์ด๋ฒคํŠธ ๋ฐฉ์ถœ
    super.set(patch);
  }
}

๊ทœ์น™:

  • initState()๋Š” ์„ ํƒ์ ์ด๋‹ค. ์ œ๊ณต๋˜๋ฉด ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋กœ ํ˜ธ์ถœ๋œ๋‹ค.
  • snapshot()์€ ์„ ํƒ์ ์ด๋‹ค. ๋ฏธ์ œ๊ณต ์‹œ ์–•์€ ๋ณต์‚ฌ ์Šค๋ƒ…์ƒท์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • set(patch)๋Š” ๋ณ€๊ฒฝ์„ ๋ˆ„์ ํ•œ ํ›„ ComponentManager.emit('changeState', sid, ...)๋ฅผ ๋ฐฉ์ถœํ•œ๋‹ค.

9.2 ์ƒํƒœ ๋“ฑ๋ก

defineState('stype', StateClass);

๊ทœ์น™:

  • ComponentManager๊ฐ€ sid ๊ธฐ๋ฐ˜์œผ๋กœ BaseComponentState ์ธ์Šคํ„ด์Šค๋ฅผ ์ „์—ญ ๊ด€๋ฆฌํ•œ๋‹ค.
  • context.instance๋กœ ์ƒํƒœ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

9.3 ๋ผ์ดํ”„์‚ฌ์ดํด ํ›…

๊ทœ์น™:

  • mountComponent: Commit Phase์—์„œ effectTag === EffectTag.PLACEMENT์ผ ๋•Œ ํ˜ธ์ถœ
  • updateComponent: Commit Phase์—์„œ effectTag === EffectTag.UPDATE์ผ ๋•Œ ํ˜ธ์ถœ (๋‹จ, __isReconciling์ด false์ผ ๋•Œ๋งŒ)
  • unmountComponent: Commit Phase์—์„œ effectTag === EffectTag.DELETION์ผ ๋•Œ ํ˜ธ์ถœ

ํƒ€์ด๋ฐ:

  • Render Phase์—์„œ๋Š” lifecycle์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Œ
  • Commit Phase์—์„œ๋งŒ ํ˜ธ์ถœ๋จ

9.4 isReconciling ํ”Œ๋ž˜๊ทธ

๋ชฉ์ : updateComponent ์ค‘ setState ํ˜ธ์ถœ ๋ฐฉ์ง€ (๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€)

๋™์ž‘:

// ComponentManager
private isReconciling: boolean = false;

updateComponent(prevVNode, nextVNode, host, context) {
  this.isReconciling = true;
  try {
    // ... update logic
  } finally {
    this.isReconciling = false;
  }
}

// BaseComponentState
set(patch: Record<string, any>) {
  if (this.componentManager.getReconciling()) {
    logger.warn(LogCategory.COMPONENT, 'setState called during reconciliation, ignoring');
    return;
  }
  // ... set logic
}

9.5 ์ž๋™ ์žฌ๋ Œ๋”๋ง

๊ทœ์น™:

  • DOMRenderer๋Š” changeState ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ•œ๋‹ค.
  • ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ requestAnimationFrame์œผ๋กœ ์Šค๋กœํ‹€๋œ ์ „์ฒด re-render๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•œ๋‹ค.
  • ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ API๋Š” ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค (updateBySid ์ œ๊ฑฐ). ํ•ญ์ƒ ์ „์ฒด ๋ฌธ์„œ ์žฌ๋นŒ๋“œ + prev/next ๋น„๊ต ์›์น™์„ ๋”ฐ๋ฅธ๋‹ค.

10. ํฌํ„ธ

10.1 ํฌํ„ธ ์ •์˜

portal(
  target: HTMLElement | (() => HTMLElement) | string,
  template: ElementTemplate,
  portalId?: string
)

๊ทœ์น™:

  • target์€ HTMLElement, ํ•จ์ˆ˜, ๋˜๋Š” CSS ์„ ํƒ์ž ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์žˆ๋‹ค.
  • portalId๊ฐ€ ์ œ๊ณต๋˜๋ฉด ๋™์ผ ID๋กœ ํ˜ธ์ŠคํŠธ๋ฅผ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

10.2 ํฌํ„ธ ์ฒ˜๋ฆฌ

๊ทœ์น™:

  1. Render Phase์—์„œ Portal VNode ๊ฐ์ง€
  2. ๋ณ„๋„ FiberScheduler๋กœ Portal ๋‚ด๋ถ€ reconcile ์ˆ˜ํ–‰
  3. portalId๋กœ ๋Œ€์ƒ ์ปจํ…Œ์ด๋„ˆ ๋‚ด ํ˜ธ์ŠคํŠธ๋ฅผ ์‹๋ณ„/์žฌ์‚ฌ์šฉํ•œ๋‹ค.
  4. ๋ Œ๋” ์‚ฌ์ดํด์—์„œ ๋ฐฉ๋ฌธ๋˜์ง€ ์•Š์€ ํฌํ„ธ์€ ์ •๋ฆฌ๋œ๋‹ค.
  5. ํƒ€๊ฒŸ์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์ด์ „ ํƒ€๊ฒŸ์˜ ํ˜ธ์ŠคํŠธ๋ฅผ ์ •๋ฆฌํ•˜๊ณ  ์ƒˆ ํƒ€๊ฒŸ์œผ๋กœ ์ด๊ด€ํ•œ๋‹ค.
  6. ๋™์ผ portalId๋Š” ๋™์ผ DOM ํ˜ธ์ŠคํŠธ ์žฌ์‚ฌ์šฉ์„ ๋ณด์žฅํ•œ๋‹ค.

11. skipNodes ๊ธฐ๋Šฅ

11.1 ๋ชฉ์ 

์ž…๋ ฅ ์ค‘์ธ ๋…ธ๋“œ๋ฅผ ์™ธ๋ถ€ ๋ณ€๊ฒฝ(AI, ๋™์‹œํŽธ์ง‘)์œผ๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธ

11.2 ๋™์ž‘ ๋ฐฉ์‹

Render Phase:

const sid = getVNodeId(vnode);
if (sid && skipNodes?.has(sid)) {
  // ์ด์ „ VNode์™€ DOM ์œ ์ง€
  // effectTag ์„ค์ • ์•ˆ ํ•จ
  return;
}

Commit Phase:

const sid = getVNodeId(vnode);
if (sid && skipNodes?.has(sid)) {
  // DOM ์—…๋ฐ์ดํŠธ ์•ˆ ํ•จ
  // ์ด์ „ DOM ์œ ์ง€
  return;
}

11.3 ๊ทœ์น™

  • skipNodes์— ํฌํ•จ๋œ ๋…ธ๋“œ๋Š” Render Phase์™€ Commit Phase ๋ชจ๋‘์—์„œ ์Šคํ‚ต
  • ์ด์ „ VNode์™€ DOM์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€
  • ์ž์‹ ๋…ธ๋“œ๋Š” ๊ณ„์† ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ (์ž…๋ ฅ ์ค‘์ธ ๋…ธ๋“œ๋งŒ ๋ณดํ˜ธ)
  • ๋ชจ๋ธ์€ ์—…๋ฐ์ดํŠธ๋˜์ง€๋งŒ DOM์—๋Š” ๋ฐ˜์˜ ์•ˆ ๋จ
  • skipNodes ์ œ๊ฑฐ ํ›„ ์žฌ๋ Œ๋”๋งํ•˜๋ฉด ์ตœ์‹  ๋ชจ๋ธ์ด DOM์— ๋ฐ˜์˜๋จ

11.4 EditorViewDOM ์—ฐ๋™

์ž…๋ ฅ ์‹œ์ž‘:

  • handleInput(), handleCompositionStart()์—์„œ _onInputStart() ํ˜ธ์ถœ
  • Selection ๊ธฐ๋ฐ˜์œผ๋กœ ํŽธ์ง‘ ์ค‘์ธ ๋…ธ๋“œ์˜ sid ์ถ”์ถœํ•˜์—ฌ editingNodes์— ์ถ”๊ฐ€

์ž…๋ ฅ ์ข…๋ฃŒ:

  • handleCompositionEnd(), handleBlur()์—์„œ _onInputEnd() ํ˜ธ์ถœ
  • debounce ํ›„ editingNodes ์ œ๊ฑฐ ๋ฐ ์žฌ๋ Œ๋”๋ง

๋ Œ๋”๋ง:

  • render() ํ˜ธ์ถœ ์‹œ skipNodes: editingNodes ์ „๋‹ฌ

12. ๋กœ๊น… ์‹œ์Šคํ…œ

12.1 ๊ตฌ์กฐํ™”๋œ ๋กœ๊น…

LogCategory:

enum LogCategory {
  VNODE = 'vnode',
  FIBER = 'fiber',
  RECONCILE = 'reconcile',
  COMPONENT = 'component',
}

์‚ฌ์šฉ:

logger.debug(LogCategory.FIBER, 'message', { data });
logger.warn(LogCategory.COMPONENT, 'message', { data });
logger.error(LogCategory.RECONCILE, 'message', error);

12.2 ๋””๋ฒ„๊ทธ ํ”Œ๋ž˜๊ทธ

ํ™˜๊ฒฝ ๋ณ€์ˆ˜:

  • BAROCSS_DEBUG_VNODE: VNode ๊ด€๋ จ ๋กœ๊ทธ ํ™œ์„ฑํ™”
  • __DEBUG_RECONCILE__: Reconciliation ๊ด€๋ จ ๋กœ๊ทธ ํ™œ์„ฑํ™”
  • __DEBUG_MARKS__: Marks ๊ด€๋ จ ๋กœ๊ทธ ํ™œ์„ฑํ™”

์ „์—ญ ํ”Œ๋ž˜๊ทธ:

  • globalThis.__BAROCSS_DEBUG_VNODE__: VNode ๊ด€๋ จ ๋กœ๊ทธ ํ™œ์„ฑํ™”

13. ์ƒ์ˆ˜ ์ •์˜

13.1 EffectTag

export const EffectTag = {
  PLACEMENT: 'PLACEMENT',  // ์ƒˆ DOM ์š”์†Œ ์‚ฝ์ž…
  UPDATE: 'UPDATE',        // ๊ธฐ์กด DOM ์š”์†Œ ์—…๋ฐ์ดํŠธ
  DELETION: 'DELETION',    // ๊ธฐ์กด DOM ์š”์†Œ ์ œ๊ฑฐ
} as const;

์‚ฌ์šฉ:

  • fiber.effectTag = EffectTag.PLACEMENT
  • if (fiber.effectTag === EffectTag.UPDATE)

13.2 VNodeTag

export const VNodeTag = {
  TEXT: '#text',      // ํ…์ŠคํŠธ ๋…ธ๋“œ
  PORTAL: 'portal',   // ํฌํ„ธ ๋…ธ๋“œ
} as const;

์‚ฌ์šฉ:

  • vnode.tag = VNodeTag.TEXT
  • if (vnode.tag === VNodeTag.PORTAL)

13.3 DOMAttribute

export const DOMAttribute = {
  BC_SID: 'data-bc-sid',
  DECORATOR_SID: 'data-decorator-sid',
  DECORATOR_STYPE: 'data-decorator-stype',
  DECORATOR_CATEGORY: 'data-decorator-category',
  DECORATOR_POSITION: 'data-decorator-position',
  SKIP_RECONCILE: 'data-skip-reconcile',
  DECORATOR: 'data-decorator',
} as const;

์‚ฌ์šฉ:

  • dom.setAttribute(element, DOMAttribute.BC_SID, sid)
  • vnode.attrs?.[DOMAttribute.DECORATOR_SID]

14. ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ

14.1 ๋ชจ๋ธ ๊ฒ€์ฆ

๊ทœ์น™:

  • stype ๋ˆ„๋ฝ ๋ชจ๋ธ: ๋ Œ๋” ์‹œ์ž‘ ์‹œ ์ฆ‰์‹œ ์—๋Ÿฌ๋ฅผ ๋˜์ง„๋‹ค. ๋ Œ๋”๋Š” ์ค‘๋‹จ๋œ๋‹ค.
  • sid ๋ˆ„๋ฝ ๋ชจ๋ธ: ์Šคํ‚ตํ•˜๊ณ  ๊ฒฝ๊ณ ๋ฅผ ๊ธฐ๋กํ•œ๋‹ค. ๊ธฐ์กด DOM์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋ฏธ๋“ฑ๋ก stype: ์—๋Ÿฌ๋ฅผ ๋˜์ง„๋‹ค.

14.2 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๊ฒ€์ฆ

๊ทœ์น™:

  • ์ž˜๋ชป๋œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋ฒ”์œ„/ํฌ์ง€์…˜: ํ•ด๋‹น ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋ฌด์‹œํ•œ๋‹ค (ํฌ๋ž˜์‹œํ•˜์ง€ ์•Š์Œ).

14.3 ํฌํ„ธ ๊ฒ€์ฆ

๊ทœ์น™:

  • ํฌํ„ธ ํƒ€๊ฒŸ ๋ฌดํšจ: ํ•ด๋‹น ํฌํ„ธ์€ ์Šคํ‚ตํ•˜๊ณ  ๊ฒฝ๊ณ ๋ฅผ ๊ธฐ๋กํ•œ๋‹ค.

15. ์„ฑ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ

15.1 DOM ์•ˆ์ •์„ฑ

๊ทœ์น™:

  • sid/decoratorSid๋Š” React์˜ key์ฒ˜๋Ÿผ DOM ์žฌ์‚ฌ์šฉ์˜ ๊ธฐ์ค€์ด๋‹ค.
  • ๋™์ผ sid๋ฅผ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋Š” DOM ์š”์†Œ์™€ ์ƒํƒœ ์ธ์Šคํ„ด์Šค๊ฐ€ ์žฌ์‚ฌ์šฉ๋œ๋‹ค.

15.2 ๋ Œ๋”๋ง ์„ฑ๋Šฅ

๊ทœ์น™:

  • ์ „์ฒด ๋ฌธ์„œ ๋ฆฌ์ปจ์‹ค๋„ ํ—ˆ์šฉ๋œ๋‹ค. VNode ์ƒ์„ฑ์€ ์ˆœ์ˆ˜/๋น ๋ฅด๊ฒŒ ์œ ์ง€ํ•œ๋‹ค.
  • ๋ถˆํ•„์š”ํ•œ DOM ์ฝ๊ธฐ๋Š” ๊ธˆ์ง€๋œ๋‹ค. ๋น„๊ต๋Š” prevVNode vs nextVNode๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  • Render Phase์™€ Commit Phase ๋ถ„๋ฆฌ๋กœ DOM ์กฐ์ž‘ ์ตœ์†Œํ™”

15.3 ์„ฑ๋Šฅ ๊ธฐ์ค€

ํ…Œ์ŠคํŠธ ๊ธฐ์ค€:

  • 1000 ๋…ธ๋“œ: < 3์ดˆ
  • 5000 ๋…ธ๋“œ: < 60์ดˆ (๋А๋ฆฐ CI ํ™˜๊ฒฝ ๊ธฐ์ค€)
  • ๋ธ”๋ก ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ˜ผํ•ฉ 1000 ๋…ธ๋“œ: < 30์ดˆ
  • ๋ฐ˜๋ณต 50ํšŒ ์ „์ฒด ๋ Œ๋” ์‹œ ๋ฉ”๋ชจ๋ฆฌ ์ฆ๊ฐ€: 5MB ๋ฏธ๋งŒ

16. ํ…Œ์ŠคํŠธ/๊ฒ€์ฆ ์›์น™

16.1 DOM ๊ฒ€์ฆ

๊ทœ์น™:

  • DOM ๋น„๊ต๋Š” normalizeHTML(container.firstElementChild) ๊ธฐ๋ฐ˜ ์ •๊ทœํ™” ๋ฌธ์ž์—ด๋กœ ๊ฒ€์ฆํ•œ๋‹ค.
  • prev/next ๋น„๊ต๋กœ ์†์„ฑ/์Šคํƒ€์ผ ์ œ๊ฑฐ๊ฐ€ ๋ฐ˜์˜๋˜์–ด์•ผ ํ•œ๋‹ค.

16.2 ํฌํ„ธ ๊ฒ€์ฆ

๊ทœ์น™:

  • ํฌํ„ธ์€ portalId๋กœ ํ˜ธ์ŠคํŠธ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๊ณ , ๋ฐฉ๋ฌธ๋˜์ง€ ์•Š์œผ๋ฉด ์ •๋ฆฌ๋œ๋‹ค.

16.3 skipNodes ๊ฒ€์ฆ

๊ทœ์น™:

  • skipNodes์— ํฌํ•จ๋œ ๋…ธ๋“œ๋Š” DOM ์—…๋ฐ์ดํŠธ๊ฐ€ ์Šคํ‚ต๋˜์–ด์•ผ ํ•œ๋‹ค.
  • ์ž์‹ ๋…ธ๋“œ๋Š” ๊ณ„์† ์ฒ˜๋ฆฌ๋˜์–ด์•ผ ํ•œ๋‹ค.

17. ๊ธˆ์ง€ ์‚ฌํ•ญ

๋‹ค์Œ์€ ๋ช…์‹œ์ ์œผ๋กœ ๊ธˆ์ง€๋œ๋‹ค:

  1. ๋ž˜ํผ(wrapper) ๋„์ž…: ๋ž˜ํผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋„์ž…ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  2. VNode์— DOM ํ‘œ์‹ ์ฃผ์ž…: VNode์˜ attrs์— data-bc-*, data-decorator-*๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค. (๋‹จ, attrs['data-decorator-*']๋Š” ์˜ˆ์™ธ - Reconciler๊ฐ€ ์„ค์ •)
  3. SSR ์œ ํ‹ธ ๋…ธ์ถœ: SSR ๊ด€๋ จ ์œ ํ‹ธ์€ ํ˜„์žฌ ์ œ๊ฑฐ๋˜์—ˆ๋‹ค.
  4. sid ์ƒ์„ฑ/๋ณ€ํ˜•: sid๋Š” ๋ชจ๋ธ์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๋ฉฐ ์ƒ์„ฑ/๋ณ€ํ˜•ํ•˜์ง€ ์•Š๋Š”๋‹ค. (๋‹จ, generateVNodeIdIfNeeded๋Š” ์˜ˆ์™ธ - auto ID ์ƒ์„ฑ์šฉ)
  5. ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ API: updateBySid ๊ฐ™์€ ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ API๋Š” ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.
  6. Render Phase์—์„œ DOM ์กฐ์ž‘: Render Phase์—์„œ๋Š” DOM ์กฐ์ž‘์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. (์ƒ์„ฑ๋งŒ)
  7. Render Phase์—์„œ lifecycle ํ˜ธ์ถœ: Render Phase์—์„œ๋Š” mountComponent, updateComponent๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ด ์ŠคํŽ™์€ renderer-dom์˜ ๋‹จ์ผ ๊ธฐ์ค€ ๋ฌธ์„œ๋‹ค. ๊ตฌํ˜„๊ณผ ํ…Œ์ŠคํŠธ๋Š” ์ด ๊ทœ์น™์— ๋งž์ถฐ ์œ ์ง€/๊ฒ€์ฆํ•œ๋‹ค.

@easylogic

Copy link
Copy Markdown
Author

์•ž์œผ๋กœ๋Š” ์ด๋Ÿฐ ๋ฌธ์„œ๋งŒ ๋ณด๊ณ ๋„ ์•Œ์•„์„œ ๋‹ค ๋‚˜์˜ฌ๋ ค๋‚˜?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment