-
Notifications
You must be signed in to change notification settings - Fork 49
[6팀 김소리] Chapter2-2. 나만의 React 만들기 #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
milmilkim
wants to merge
29
commits into
hanghae-plus:main
Choose a base branch
from
milmilkim:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
아직하면안됨..
feat. 코덱스.. ㅜㅜ - 자식 재조정에서 항상 ''만 넘겼던 것을 수정 - 텍스트 노드는 host가 아니라 NodeType.TEXT 인스턴스를 돌려주도록 수정 - setProps가 모든 속성을 덮어쓰는 것을 수정
죄송합니다 저도 잘 모르겠어요
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
과제 체크포인트
배포 링크
https://milmilkim.github.io/front_7th_chapter2-2/
기본과제
Phase 1: VNode와 기초 유틸리티
core/elements.ts:createElement,normalizeNode,createChildPathutils/validators.ts:isEmptyValueutils/equals.ts:shallowEquals,deepEqualsPhase 2: 컨텍스트와 루트 초기화
core/types.ts: VNode/Instance/Context 타입 선언core/context.ts: 루트/훅 컨텍스트와 경로 스택 관리core/setup.ts: 컨테이너 초기화, 컨텍스트 리셋, 루트 렌더 트리거Phase 3: DOM 인터페이스 구축
core/dom.ts: 속성/스타일/이벤트 적용 규칙, DOM 노드 탐색/삽입/제거Phase 4: 렌더 스케줄링
utils/enqueue.ts:enqueue,withEnqueue로 마이크로태스크 큐 구성core/render.ts:render,enqueueRender로 루트 렌더 사이클 구현Phase 5: Reconciliation
core/reconciler.ts: 마운트/업데이트/언마운트, 자식 비교, key/anchor 처리core/dom.ts: Reconciliation에서 사용할 DOM 재배치 보조 함수 확인Phase 6: 기본 Hook 시스템
core/hooks.ts: 훅 상태 저장,useState,useEffect, cleanup/queue 관리core/context.ts: 훅 커서 증가, 방문 경로 기록, 미사용 훅 정리기본 과제 완료 기준:
basic.equals.test.tsx,basic.mini-react.test.tsx전부 통과심화과제
Phase 7: 확장 Hook & HOC
hooks/useRef.ts: ref 객체 유지hooks/useMemo.ts,hooks/useCallback.ts: shallow 비교 기반 메모이제이션hooks/useDeepMemo.ts,hooks/useAutoCallback.ts: deep 비교/자동 콜백 헬퍼hocs/memo.ts,hocs/deepMemo.ts: props 비교 기반 컴포넌트 메모이제이션과제 셀프회고
기존에 jsx에 대해 학습한 적이 있는데 커스텀h 함수로 dom으로 변환하는 과정만 다루었다.
이번 과제에서는 그 h() 호출과 실제 DOM 업데이트 사이에서 벌어지는 훨씬 많은 과정들을 구현해야 했다.
자력으로 구현하는데는 실패했다. 나보다 똑똑한 사람들이 모여서 몇 년 동안 고민하면서 내놓은 최적의 설계를 며칠동안 구현할 수 있다면 내가 이미 프레임워크 개발자가 되었을 것이다. 하지만 이 과정을 통해 리액트와 훅에 대해 더 이해하게 되었다는 것에 의의가 있다.
전체적으로는 chatGPT에 단계적으로 질문해가며 진행했다.
아하! 모먼트 (A-ha! Moment)
먼저 VNode의 스펙을 확인해보았는데 그러면서 가상 돔이란 VNode의 트리라는 것을 이해했다. 이 과정에서 TextElement와 Fragment를 예외적으로 처리해야 한다거나... number를 string으로 변환해야 한다는 걸 깨달았다. 그리고 children이 존재할 때만 props를 추가해서 몇 개의 테스트 코드를 통과했다. 다만 Path라는 게 어떤 방식으로 만들고 어떻게 다루는지 잘 알 수 없었기에 일단 나중으로 미루었다.
여기까지는 나쁘지 않았다. 하지만 이후의 전역 컨텍스트, 재조정, 인스턴스, 돔 업데이트가 얽혀있었고 어떤 순서로 어떻게 진행해야 할지 혼란스러웠다. 인스턴스를 어떻게 다루어야 하는지도 어려웠다. 재조정 알고리즘까지 가기 전에 그냥 단순 마운트만 시켜보고 싶었는데 그것조차 잘 이해가 안 됐다. 어디에서 DOM을 업데이트 해야 하는 거지? 나는 별도의 instance 관련된 파일을 하나 추가해서 그곳에서 처리했다. 그 과정에서 이제 VNode는 매번 새로 만들어지는 것이고, 인스턴스는 실제 DOM을 가지고 있는 객체라는 것을 깨달았다. 새로 만들어진 가상 DOM과 기존의 인스턴스를 비교해서 바뀐 부분만 렌더링하는 것이 리액트의 핵심이다. 그래서 reconcile에서 마운트도 일어난다.
일반 dom뿐 아니라 함수형 컴포넌트와 같은 처리를 별도로 해주어야 한다는 깨달음을 얻었다.
그 다음으로 개발 서버에서 단순히 컴포넌트가 렌더링 되는 부분을 확인하려 했다. (setDomProps, updateDomProps를 제대로 구현하지 않았다.) 아무 일도 일어나지 않았다. 🥺 vNode가 falsy한 상태로 넘어오는 경우가 있었기 때문이었다. 이 부분을 예외 처리했더니 렌더링 문제는 없어졌지만 여기저기서 undefined, null, false, "", 0등이 다양한 상황에서 섞여 들어오기 때문에 쉽지 않았다. 정규화를 제대로 해야 하는 부분이다.
통과 되어야 할 테스트가 통과되지 않아 한참 고민했는데 제대로 구현하지 않은 setDomProps가 문제였다. id같은 기본적인 속성도 안 넣어놓고 있어서 요소를 찾지 못해 발생하는 문제였다. 여기서 또한 children, nodeValue같은 속성은 건너뛰어야 했다.
useState의 구현은 AI에게 맡겼는데, 우선 hook에 대한 구현을 이해하지는 못했으나 재렌더링이 되지 않는 문제가 있었다. 그래서 전체적으로 코드를 다시 살펴보며 AI와 함께 디버깅 했다. 어떤 문제들이 있었냐면..
추가로 'path'를 생성하도록 했다.
여기서 대략적으로 재조정 알고리즘에 key가 왜 중요한지 체감하게 되었으며 리액트에서 컴포넌트를 전역적으로 어떻게 제어하고 있는지 알게 되었다.
이후의 useEffect를 AI에게 구현을 하게 한 후 전반적인 hook과 useState, useEffect에 대해 공부했다. hook은 컴포넌트 단위의 배열이었다.
hook이 만들어지는 시점은 인스턴스가 만들어지는 시점이며 실행 순서와 커서의 순서에 대해 깨달았다.
기술적 성장
내가 지난주차에 만든 spa의 컴포넌트는 (그럴 거라고 생각 했지만) 이 방식에 비하면 정말 간단하게 구현했다.
아무래도 전역적으로 컴포넌트 관리를 하는 게 맞는 거 같지만 그래서 그걸 어떻게 하나 했는데 여기서는 path를 사용한다. 그렇게 인덱스를 붙이는데 유니크 할 수가 있나? 했는데 생각해보니 트리의 구조라서 당연한 일이었다.
지난 과제를 하면서 딱히(?) 리액트(정확히는 리액트의 함수형 컴포넌트)에 대한 깊은 이해로 가지 않고 Vue가 이해되는 것 같다 생각했던 까닭은 함수형 컴포넌트의 useEffect는 항상 생명주기 비슷한 거지 생명주기는 아니다...라고 설명에서 여러 번 읽었기 때문이다. 이번주 과제를 하면서 좀 더 useEffect에 대한 정말로 깊은 이해를 좀 해 본 것 같다. 직접 구현하기는 거의 불가능했다. 이 정도로 리액트를 딥 다이브 한 적 없다. 모던 리액트 딥 다이브라는 책을 항상 똑같은 부분을 읽으면서 세부 구현 부분은 외면했었는데 백문이 불여일견이다.
코드 품질
diff 알고리즘이 단순 인덱스 기반 키 비교여서 더 정교하게 수정하는 게 좋을 거 같다.
학습 효과 분석
개인적으로 공부라는 것은 순서가 어떻게 되든 결국 공백을 채워넣으면 되는 게 아닌가 하는 생각을 가지고 있다.
그래서 우선 AI로 빠르게 코드를 일단 만들어놓고 전체적으로 이해 한 후, 뜯어보고 필요하다면 리팩토링 할 생각이었다. 이해만 해도 성공 아닐까? 하지만 이번주엔 회사 일을 정리하며 많은 심경의 변화가 있었기 때문에 추가적인 작업을 하지 않았다. 그래서 조금 아쉽지만, 시간 대비 정말 많은 학습을 할 수 있어서 유익한 경험이었다.
과제 피드백
너무 어려워서 기절할 뻔 했습니다.
완성을 목표로 하면 간단하게 끝낼 수 있고 깊게 파려면 한도 끝도 없이 파고들 수 있을 거 같은 과제인 듯 합니다.
리뷰 받고 싶은 내용
현재 구현에서는 각 DOM 요소에 직접 이벤트 리스너를 추가하고 있습니다. React처럼 이벤트 위임을 사용하려면 어떻게 구현해야 할까요? 트 컨테이너에 이벤트 리스너를 등록하는 시점과 방법은 무엇일까요?