Skip to content

[4팀 한선민] Chapter 2-2. 나만의 React 만들기 - #56

Open
1lmean wants to merge 17 commits into
hanghae-plus:easyfrom
1lmean:easy
Open

[4팀 한선민] Chapter 2-2. 나만의 React 만들기#56
1lmean wants to merge 17 commits into
hanghae-plus:easyfrom
1lmean:easy

Conversation

@1lmean

@1lmean 1lmean commented Nov 20, 2025

Copy link
Copy Markdown

과제 체크포인트

배포 링크

https://1lmean.github.io/front_7th_chapter2-2/

기본과제

가상돔을 기반으로 렌더링하기

  • createVNode 함수를 이용하여 vNode를 만든다.
  • normalizeVNode 함수를 이용하여 vNode를 정규화한다.
  • createElement 함수를 이용하여 vNode를 실제 DOM으로 만든다.
  • 결과적으로, JSX를 실제 DOM으로 변환할 수 있도록 만들었다.

이벤트 위임

  • 노드를 생성할 때 이벤트를 직접 등록하는게 아니라 이벤트 위임 방식으로 등록해야 한다
  • 동적으로 추가된 요소에도 이벤트가 정상적으로 작동해야 한다
  • 이벤트 핸들러가 제거되면 더 이상 호출되지 않아야 한다

심화 과제

Diff 알고리즘 구현

  • 초기 렌더링이 올바르게 수행되어야 한다
  • diff 알고리즘을 통해 변경된 부분만 업데이트해야 한다
  • 새로운 요소를 추가하고 불필요한 요소를 제거해야 한다
  • 요소의 속성만 변경되었을 때 요소를 재사용해야 한다
  • 요소의 타입이 변경되었을 때 새로운 요소를 생성해야 한다

과제 셀프회고

아하! 모먼트 (A-ha! Moment)

  • 항해 시작 전에 리액트 사전 스터디를 진행했어서, 리액트 라액트 라이프 사이클 같은 개념들이 크게 낯설지는 않았는데, 막상 구현하려는 내용을 보니 매칭이 안됐다가, AI에 코드별 설명을 요청한 후에야 해당 함수가 어떤 개념인지 매칭이 되어 조금은 이해하기 수월해졌던 것 같습니다.
%%{init: { "theme": "neutral", "scale": 0.5 }}%%

flowchart LR

  %% React
  subgraph React
  direction TB
    A["createRoot(root).render(<App />)"]
    B["VDOM 생성"]
    C["이전 VDOM과 diff"]
    D["실제 DOM 패치"]
    E["루트에 이벤트 위임 등록"]
  end

  %% 내 구현
  subgraph Mine
  direction TB
    A2["renderElement(vNode, container)"]
    B2["normalizeVNode"]
    C2["oldVNode 조회 (containerVNodes)"]
    D2["oldVNode 존재?"]
    D2a["초기 렌더: createElement로 DOM 생성"]
    D2b["업데이트: updateElement로 diff"]
    E2["setupEventListeners(container)"]
    F2["containerVNodes.set(container, normalizedVNode)"]
  end


  %% React 내부 흐름
  A --> B --> C --> D --> E

  %% 내 구현 흐름
  A2 --> B2 --> C2 --> D2
  D2 -->|No| D2a --> E2 --> F2
  D2 -->|Yes| D2b --> E2 --> F2

  %% 좌우 매핑 (라벨 제거)
  B -.-> A2
  C -.-> D2
  E -.-> E2

Loading

기술적 성장

  • 가상 DOM 파이프라인을 구조적으로 이해
    각 파일(createVNode, normalizeVNode, createElement, updateElement, renderElement, eventManager)의 책임을 docs 를 통해 정리하면서, “설계도 만들기 → 정규화 → DOM 생성 → diff 업데이트 → 이벤트 위임”이라는 큰 그림을 스스로 설명할 수 있게 되었습니다.
  • React 내부 동작과 1:1 매핑해 보는 연습
    renderElement 와 React 의 createRoot(...).render() 를 비교해 보고, updateElement 단계가 React 의 reconciliation 과 어떤 부분을 공유하는지 정리해 본 덕분에, React 공식 문서를 읽을 때도 훨씬 구체적인 그림이 떠오르게 되었습니다.

코드 품질

  • 리액트의 이벤트 위임 방식을 직접 구현해 보면서 생긴 인사이트
    eventManager 를 구현/리팩터링하는 과정에서, 루트에 한 번만 리스너를 두고 WeakMap(element → Map(eventType → Set(handler)) 구조로 핸들러를 관리하는 방식이 왜 안정적인지 체감했습니다. 이전에 잘못된 리팩터링으로 클릭 한 번에 핸들러가 여러 번 실행되어 카트 수량이 2씩 증가하던 사례를 보고, “이벤트 시스템 설계가 비즈니스 버그로 그대로 이어질 수 있다”는 걸 코드 레벨에서 경험했습니다.

  • 아직 아쉬운 부분 (중복 분기와 플래그들)
    막판에 테스트를 통과시키고 코드를 이해하는 것에 집중하다 보니, updateAttributes 나 Cart 관련 로직에서는 여전히 중복 분기와 플래그가 섞여 있습니다. docs 에서 제안하는 것처럼 이벤트/boolean/일반 속성 처리를 함수로 분리하거나, Cart 상태/뷰 로직을 더 잘게 나누는 리팩터링까지는 진행하지 못해, “동작은 하지만 스스로 다시 읽었을 때 완전히 만족스럽지는 않은” 코드가 남아 있다는 아쉬움이 있습니다.

학습 효과 분석

  • VDOM 파이프라인을 “설계도 → 정규화 → DOM → diff → 이벤트” 흐름으로 이해
    createVNode, normalizeVNode, createElement, updateElement, renderElement, eventManager 를 하나씩 뜯어보고, 그 내용을 제가 직접 정리한 docs 로 남기면서 각 함수가 어디서부터 어디까지 책임을 가져가는지 정리해 볼 수 있었습니다. 그 결과, “JSX처럼 생긴 코드가 어떤 설계도(VNode)로 바뀌고, 그게 어떻게 실제 DOM 으로 만들어진 뒤 diff 로 최소 변경만 적용되는지”를, React 추상화에 기대지 않고 제 코드 기준으로 설명할 수 있게 되었습니다.

  • 버그를 통해 createVNode / normalizeVNode 추상화 경계를 이해한 경험
    과제 시작부터 createVNodenormalizeVNode 가 파일 단위로 나뉘어 있었지만, 처음에는 “왜 굳이 둘을 나눴지?” 라는 생각만 들고, 실제 책임 경계가 잘 보이지 않았습니다. 옛날 normalizeVNode 버전으로 되돌렸을 때 상품 상세 E2E 와 카트 모달이 깨지는 이유를 docs 와 함께 따라가 보면서, “children 평탄화/필터링은 createVNode 에서, 타입 정규화·함수 컴포넌트 실행·불필요한 값 제거는 normalizeVNode 에서 맡는다”는 역할 분리가 왜 필요한지, 그리고 이 경계를 흐리면 어떤 식으로 VDOM 구조 전체가 무너지는지까지 연결해서 이해할 수 있었습니다.

과제 피드백

  • 난이도 조정(easy 브랜치)에 대한 느낌
    easy 브랜치가 아니었다면 이번에도 fail 이었을 것 같다고 생각하기 때문에, 급하게 easy 버전을 만들어주신 코치님께 감사드립니다. 다만, main(hard) 브랜치와의 난이도 격차가 꽤 크게 느껴져서, 다시 hard 구현을 시도하려고 할 때 어떤 학습 순서(예: VDOM → diff → 이벤트 → 라우팅…)로 접근하는 게 좋을지 알고 싶기도 합니다.

  • “리액트를 쓰기만 했던 과거”와의 대비
    사실 현업에서 리액트를 쓸 때는, 라이브러리가 제공하는 컴포넌트/훅과 팀 내 패턴에만 의존해서 개발하는 경우가 많았고, 내부 동작 원리나 로직을 깊게 궁금해하지 않았던 것 같습니다. 이번 과제를 하면서는 “왜 나는 지금까지 이 부분을 궁금해하지 않았을까?”, “내가 쓰는 도구의 내부 동작을 어느 정도까지는 이해해야 할까?” 는 생각을 하면서 과제를 진행했는데, 의도하신 부분인 것 같아서 좋았습니다.

리뷰 받고 싶은 내용

  • Fiber / 스케줄링 / 우선순위 설계에 대한 힌트
    이번 구현에서는 React 처럼 Fiber 기반 스케줄링이나 렌더 우선순위 개념 없이, renderElement 호출 시 VDOM 계산부터 DOM 패치까지를 한 번에 동기적으로 처리하고 있습니다. 만약 “작업을 쪼개서 우선순위/중단/재개가 가능한 렌더링(React의 render phase 쪽 개념)”을 흉내 내 보고 싶다면, 현재 구조에서는 이런 스케줄링/큐잉 로직을 renderElement 바깥의 별도 레이어로 두는 게 맞을지, 아니면 renderElement 내부에서 oldVNode/newVNode 계산 이전 단계로 끼워 넣는 식이 더 자연스러울지 궁금합니다.

  • 렌더/커밋 단계 분리와 Effect 처리에 대한 방향성
    React 는 VDOM 계산(render phase)과 실제 DOM/Effect 반영(commit phase)을 분리하고 useEffect/useLayoutEffect 타이밍도 따로 관리하지만, 제 구현은 normalizeVNode → createElement/updateElement 가 한 번에 돌면서 Effect 개념이 없습니다. 만약 간단한 “렌더 후 사이드 이펙트”나 “레이아웃 이후 실행되어야 하는 작업”을 지원해 보려면, 지금 구조에서는 어떤 지점(예: renderElement 이후 훅, updateElement 내부 콜백, 별도 Effect 큐 등)에 해당 책임을 두는 설계가 가장 자연스럽다고 보시는지 피드백을 부탁드립니다.

  • createVNodenormalizeVNode 책임 경계에 대한 검토
    지금은 children 평탄화/필터링을 createVNode 에 두고, 타입 정규화·함수 컴포넌트 실행·children 정리는 normalizeVNode 가 맡는 구조가 왜 필요한지, 코드를 분석하고 이를 정리한 docs 를 다시 읽어보면서 스스로 납득할 수 있었습니다. 다만 실제 프레임워크 설계 관점에서 봤을 때, 더 복잡한 케이스들(TEXT_NODE 타입 도입, 다양한 children 형태 등)이 추가되면 두 함수의 책임을 어디까지 확장/축소하는 것이 좋은지, “어디까지를 create 단계로 보고 어디서부터를 normalize 단계로 보는지”에 대해 피드백을 부탁드립니다.

  • Node.TEXT_NODE 같은 DOM 상수를 굳이 써야 하는 이유가 있는지
    DOM 스펙에는 Node.TEXT_NODE, Node.DOCUMENT_FRAGMENT_NODE 처럼 숫자 상수로 타입이 정의되어 있지만, “우리 코드 안에서는 그냥 "TEXT_NODE" 같은 문자열로 써도 되는 것 아닌가?” 라는 의문이 들었습니다. 실제 프레임워크 구현에서는 브라우저가 제공하는 상수/숫자 타입을 그대로 따라가는 것과, 자체적으로 "TEXT_NODE" 같은 문자열/심볼을 정의해서 쓰는 것 사이에 어떤 트레이드오프가 있는지, 그리고 이 과제 수준에서는 어느 쪽에 더 익숙해지는 게 좋을지 의견을 듣고 싶습니다.

  • preview 에서는 정상, 배포 후에는 깨졌던 CartModal 수량 버튼 이슈와 CartItem 리팩터링
    preview 단계에서는 카트 모달의 수량 증감 버튼이 정상 동작했는데, 배포 후에만 동작하지 않는 문제가 있었고, 결국 CartItem 컴포넌트를 AI 도움을 받아 리팩터링한 뒤에야 문제가 해소되었습니다. 이런 “환경에 따라서만 드러나는 버그”가 VDOM/diff/event 구조 또는 Cart 상태 관리와 어떤 식으로 엮여 있을 가능성이 있는지, 그리고 지금처럼 원인을 100% 다 파악하기보다는 컴포넌트를 리팩터링해서 증상을 없애는 방식으로 해결한 접근이 현업 기준에서 어느 정도까지는 허용 가능한지(또는 어떤 점에서 위험한지)에 대해 코멘트를 부탁드립니다.


4팀 코드리뷰

  • preview까지는 정상동작하다가, 배포 후에 CartItem 수량버튼 미동작 이슈 또 저만 문제엿나요 캯.

@joshuayeyo

Copy link
Copy Markdown

[Joshua] 4팀 코드리뷰

preview까지는 정상동작하다가, 배포 후에 CartItem 수량버튼 미동작 이슈 또 저만 문제엿나요 캯.

일단 저는 저 부분 이슈는 없었습니다🤔.
아직 지하철이라, 집 가서 헤일리 코드 구경하면서 추가 코멘트 달아볼게용

@joshuayeyo

Copy link
Copy Markdown

언니 배포링크 잘못됐어요 https://1lmean.github.io/front_7th_chapter2-2/

지금꺼는 Chapter2-1로 돼있슨

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants