Skip to content

[5팀 오새듬] Chapter 2-2. 나만의 React 만들기#46

Open
Toeam wants to merge 8 commits intohanghae-plus:easyfrom
Toeam:easy
Open

[5팀 오새듬] Chapter 2-2. 나만의 React 만들기#46
Toeam wants to merge 8 commits intohanghae-plus:easyfrom
Toeam:easy

Conversation

@Toeam
Copy link

@Toeam Toeam commented Nov 17, 2025

과제 체크포인트

배포 링크

https://toeam.github.io/front_7th_chapter2-2/

기본과제

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

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

이벤트 위임

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

심화 과제

Diff 알고리즘 구현

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

과제 셀프회고

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

  • Virtual DOM의 본질 이해
    Virtual DOM이 단순히 "성능 최적화 도구"라고만 생각했는데, 실제로 구현해보니 "DOM 형태를 본뜬 객체 덩어리"라는 것을 체감했습니다. Real DOM의 구조를 JavaScript 객체로 표현하고, 변경사항을 메모리에서 먼저 계산한 뒤 최종 결과만 실제 DOM에 반영한다는 개념이 명확해졌습니다. 브라우저의 렌더링 과정은 HTML을 파싱하여 DOM Tree를 생성하고, CSS를 파싱하여 CSSOM을 만든 후, 이 둘을 결합하여 Render Tree를 구성합니다. 그 후 Layout 단계에서 각 요소의 정확한 위치와 크기를 계산하고, 마지막으로 Painting 단계에서 픽셀을 화면에 그립니다. Virtual DOM은 이 과정에서 변경사항을 일괄 처리하여 불필요한 reflow와 repaint를 최소화합니다.

  • JSX 변환 과정에서의 발견
    JSX에서 {null}, {undefined}, {false}, {true} 같은 표현식들이 Babel 변환 시 그대로 children 배열에 포함된다는 것을 알게 되었습니다. React에서 false, null, undefined, true는 유효한 children이지만 실제로 렌더링되지 않습니다. 이로 인해 normalizeVNode에서 이런 값들을 적절히 필터링하지 않으면 예상치 못한 동작이 발생할 수 있다는 점을 배웠습니다.

기술적 성장

기존의 알고 있던 개념 되짚어보기

브라우저의 workflow

- 기존의 알고 있었던 원리였지만 이번 과제를 통해서 더 자세히 개념을 정립할 수 있는 기회가 되었습니다
- Dom Tree 생성 -> Render Tree 생성 -> Layout -> Painting
- Dom에 변화가 생기면 렌더트리를 재생성하고 (모든 요소들의 스타일 재계산) 레이아웃을 만들고 페인팅 하는 과정의 반복
참고 : https://velopert.com/3236

Virtual Dom

  • 리액트의 기존 원리가 되는 가상돔에 대해 개념을 한번 더 짚고 나가는 기회가 되었습니다.
  • 뷰에 변화가 있을 시 실제 DOM에 적용되기 전에 가상의 DOM에 먼저 적용시키고 최종적인 결과 값만 실제 DOM에 전달 => 브라우저 내에서 발생하는 연산의 양을 줄이면서 성능 개선 효과를 보임

새로 학습한 개념

js로 dom을 생성 시 babel의 역할

  • JSX의 문법을 일반 js 함수 호출로 변환 => 코드를 텍스트로 변환하는 컴파일러!
		
		// 변환 전 (JSX)
		<div className="box">Hello</div>

		// 변환 후 (JavaScript)
		createElement('div', { className: 'box' }, 'Hello')
		

배열의 평탄화 arr.flat(depth)

  • depth: 중첩 배열 구조를 평탄화 할 때 사용할 깊이 값이자 생략 시 기본값 1
   	  const arr = [1, 2, [3, 4, [5, 6]]]; 
   	  arr.flat(); // [1, 2, 3, 4, [5, 6]] 
   	  arr.flat(2); // [1, 2, 3, 4, 5, 6] 
   	  const arr2 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
   	  arr2.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

documentFragment

  • 여러 DOM 요소들을 메모리에 일시적으로 저장할 수 있는 객체
  • 여러 개의 요소들을 모아두었다가 DOcumentFragment요소를 한번에 HTML DOM 에 추가

=> createElement와의 차이점이 몰까 생각해봤다.
=> 단일 요소를 생성할 때는 createElement, 여러 요소를 한번에 추가할 때는 documentFlagment를 사용해야 불필요한 reflow 과정을 없앨 수 있다.

기존 지식의 재발견/심화

자료구조 선택의 중요성: 이벤트 핸들러 저장을 위해 처음엔 배열을 고려했지만, 중복된 이벤트 타입 처리가 필요해 Map으로 변경했습니다. 이전 회사에서 사용했던 HashMap 경험이 도움이 되었습니다.

구현 과정에서의 기술적 도전과 해결

  • updateElement의 children 타입 불일치 문제

문제: children의 타입이 다를 경우 교체 로직 누락, DOM 인덱스가 어긋나면서 잘못된 위치에 요소 추가
원인: normalizeVNode에서 빈 문자열이 children 배열에 포함되어 메인 화면에 빈 상품카드가 생성됨
해결: updateElement의 타입 비교 로직을 정규화하고, 동적 인덱스를 추적하는 방식으로 개선

  • 이벤트 리스너 중복 등록 문제

문제: 기존 이벤트 리스너를 제거하지 않아 중복 등록 발생
해결: rootEventHandlers.clear()를 사용하여 기존 핸들러 제거 (효율성에 대한 의문은 남음)

코드 품질

리팩토링이 필요한 부분

  • updateElement.js , createElement.js 부분이 조건문 사용이 많아 깔끔해 보이지가 않아서 어떻게 리팩토링을 할 지 고민중입니다.

학습 효과 분석

가장 큰 배움이 있었던 부분

Virtual DOM의 동작 원리를 이론이 아닌 실제 구현을 통해 체득했습니다. Virtual DOM은 성능 향상을 위해 변경사항을 JavaScript 엔진의 메모리에서 먼저 계산하고, 최소한의 DOM 조작만 수행하여 비용이 큰 reflow와 repaint를 줄입니다. Stack OverflowTalent500

추가 학습이 필요한 영역

Diffing 알고리즘의 최적화: 현재 구현은 기본적인 비교만 수행하는데, React의 Fiber 아키텍처처럼 더 효율적인 업데이트 방식에 대해 학습이 필요합니다.
이벤트 핸들러 관리: 현재 방식보다 메모리 효율적이고 성능이 좋은 패턴을 연구하고 싶습니다.

과제 피드백

리뷰 받고 싶은 내용

  • updateElement.js 파일의 updateElement 함수의 조건문들이 너무 남발되어서 가독성이 떨어지는데 다른 조건문을 써도 같은 결과가 나올 것 같은데 어떠한 코드 구조로 작성해야 더 가독성이 좋은 코드가 되는지 궁금합니다.

  • eventManaget.jsdml setupEventListeners작성 중 기존에 있던 이벤트 리스너를 제거를 안해서 중복 등록하는 경우가 발생하여 rootEventHandlers.clear()를 통해 제거를 하였는데 변경된 내용만 선택적으로 업데이트 하는 방식이 괜찮은 것 같은데 또 이러면 코드 내용이 길어지니 과연 효율적인 방식인가?? 하는 의문이 생겼습니다.

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.

1 participant