k-sang-soo 님의 상세페이지[8팀 김상수] Chapter 1-2. 프레임워크 없이 SPA 만들기

과제 체크포인트

배포 링크

https://k-sang-soo.github.io/front_6th_chapter1-2

기본과제

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

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

이벤트 위임

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

심화 과제

Diff 알고리즘 구현

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

과제 셀프회고

JSX에서 Virtual DOM 변환까지의 흐름을 직접 구현하면서 리액트 내부에서는 이런 동작들이 일어나는구나 라는걸 깨닫고 meta 회사 방향으로 절 한번 올렸다.. 특히 항상 궁금했던거지만 귀찮아서 찾아보지 않았던 diff 알고리즘의 핵심 원리들도 알게되었고 리액트에서 이벤트 핸드러를 사용할 때 왜 기존 브라우저에 이벤트 핸들러를 사용하지않고 자체적으로 만들었을까 궁금했는데 이번에 SyntheticEvent를 구현 해보면서 이런 방식으로 만들었고 왜 React가 기존 브라우저의 이벤트를 직접 쓰지 않는지 깨달았다. 확장성과 일관성면에서 아주 유용하다고 생각했다.

기술적 성장

SyntheticEvent 이벤트 핸들러를 구현할 때 기존 브라우저의 이벤트 핸들러를 그대로 사용하는게 아닌 React 처럼 SyntheticEvent를 구현해 본게 기억에 남았다. 생각보다 구현이 엄청 어려운 거는 아니였지만 어떤 의도와 목적으로 SyntheticEvent를 사용하게 됐는지 알 수 있었다. SyntheticEvent 과정에서 실수를 했던 점은 key 이벤트에 대한 처리를 안해서 input에 검색하는 로직에서 event가 제대로 들어오지 않아 한참 헤맸다.. 나중에 보니 단순하게 key에 대한 이벤트 처리를 하지 않았서 생긴 문제였다.

diff diffing 알고리즘을 실제로 구현 해보면서 index 기반으로 DOM들을 접근하려니 내 생각보다 에러 사항이 많았다. 여러 방어 코드들을 사용하여 어찌어찌 구현하긴 했지만 이게 최선의 방법인지 이전에 내가 로직을 잘못짜서 그런건지 정확히 파악은 못해서 조금은 아쉬웠다.

마이크로태스크 사실 이번 과제에서 사용한건 없지만 발제 자료에서 코치님이 사용하신걸 보고 궁금해서 MDN에서 문서를 살펴봤다. 브라우저 동작 과정에서 사용하던 그 마이크태스크를 사용?하는 함수이고 활용 방법 중에 여러 메시지를 배열에 넣어놓고 마이크로 태스크를 사용해서 실행 컨텍스트 종료 시점에 넣어놨던 메시지를 한 번에 하나의 객체로 전송하는 예제가 인상적이였다. 주식이나 챗봇? 같이 많은 데이터를 다룰 때 이런 배치 전력을 사용한다고 들었는데 경험해본적이 없어 어떤 전략으로 하는지 궁금했는데 이렇게 짜는건 아닐 테지만 어느정도 이해하는데 도움이 됐다.

const messageQueue = [];

let sendMessage = (message) => {
  messageQueue.push(message);

  if (messageQueue.length === 1) {
    queueMicrotask(() => {
      const json = JSON.stringify(messageQueue);
      messageQueue.length = 0;
      fetch("url-of-receiver", json);
    });
  }
};

WeakMap 이것도 마찬가지로 과제에서 사용하지 않았지만 발제 자료에서 코치님이 사용하신걸 보고 찾아봤다. WeakMap은 객체를 키로 사용할 수있다는게 신기했고 키에 약한 참조를 활용해서 사용된 객체가 더 이상 필요없어지면 해당 키/값도 자동으로 가비지 컬렉션 대상이 된다. 또 다른 이점으로는 키의 활성 상태를 관찰하는 것을 하용하지 않으므로 키를 열거할 수 없다. 이러한 특성 덕분에 메모리 누수 없이 객체에 비공개 데이터를 안전하게 연결할 수있다.

배포 이전에 다른 분들의 공유 문서를 보지 않고 직접 검색을 통해서 배포를 진행 해봤고, 배포를 하기 위한 기본 세팅은 이해했지만 이번 과제에서는 어느 부분 배포에 대한 세팅이 되어있어서 그 부분까지는 깊게 알지는 못했다.(배포 후에 에러가 나서 한참 찾아봤는데 로컬스토리지 값이 남아 있어서 에러가 났었다.................잘 확인해보자) CI/CD

코드 품질

만족스러운 부분은 그냥 브라우저 이벤트를 사용한 것이 아닌 React의 이벤트 작동 방식을 이해하기 위해 SyntheticEvent를 구현해 봤던 것이 만족스러웠다. 이런 과정에서 element._vNode 라던지 이런식으로 임시 메모리를 사용해서 관리하는걸 처음 깨달았던것 같다. React 에서는 사용할 일이 없겠지만 이런 방식으로도 할 수 있구나 라는 걸 꺠달았다.

리팩토링이 필요한 부분은 전부 필요하다고 느끼지만! 큰 부분은 아니지만 나중에 고쳐보고 성능 테스트 해봐야지 생각했었는데 시간이 부족해서 못했다. createVNode 에 중첩 배열을 평탄화 시키기 위해 flat 을 사용했는데 flat이 v8 엔진에서 최적화가 안된다고 들어서 concat 을 활용하여 평탄화 시키는 방법들이 있었던 것 같은데 활용하지 못해서 아쉽다.

학습 효과 분석

가장 큰 배움은 JSX -> 실제 DOM 까지의 전반적인 흐름을 알 수 있었던게 가장 큰 배움이였고 React의 성능 최적화 전략인 이벤트 위임, 가상 DOM, diffing 등을 직접 구현하면서 코드 수준에서 체감할 수 있었다. 그리고 WeakMap에 대해 문서를 찾아봐서 이해도가 있었지만 어디 부분에서 써야 할지 강이 잘 안 왔다. 메모리 누수를 막기 위해 어떤 구조가 적합한지에 대해 추가 학습이 필요하다고 생각했다.

DOM을 깔아 끼우면서 여러 분기에 대해 어떤 상황들에서 걸러지는지 너무 헷갈려서 정리해봤다.

과제 피드백

JSX -> 실제 DOM 까지의 전체 흐름을 이해하는데 튜토리얼 같은 느낌으로 모듈이 잘 분리되어있어 재미있게 진행했습니다. 테스트까지 있으니 뭔가 여행다닐 때 가이드가 옆에서 같이 동행하는 느낌이였습니다ㅋㅋㅋ 실제 구현해야하는 부분에서는 스스로 고민할 수 있게 설계 하셨던 점이 좋았습니다.

리뷰 받고 싶은 내용

  • WeakMap 활용 방법이 궁금합니다. 저는 현재 일부분들을 el._vNode 이런식으로 임시로 연결하여 사용하고 있거나 Map으로 연결해서 사용하고 있지만 다른 분들의 코드를 보니 WeakMap을 많이 활용한 예시들이 있는데 정말 필요한건지 판단이 잘 안 섭니다.

과제 피드백

안녕하세요 상수님~ 2주차 과제 잘 진행해주셨네요 ㅎㅎ 고생하셨습니다. 다음에는 셀프체크에도 진행사항 표기 부탁드려요!

만족스러운 부분은 그냥 브라우저 이벤트를 사용한 것이 아닌 React의 이벤트 작동 방식을 이해하기 위해 SyntheticEvent를 구현해 봤던 것이 만족스러웠다. 이런 과정에서 element._vNode 라던지 이런식으로 임시 메모리를 사용해서 관리하는걸 처음 깨달았던것 같다. React 에서는 사용할 일이 없겠지만 이런 방식으로도 할 수 있구나 라는 걸 꺠달았다.

좋은 시도를 해주셨군요!! 멋있어요!!

WeakMap 활용 방법이 궁금합니다. 저는 현재 일부분들을 el._vNode 이런식으로 임시로 연결하여 사용하고 있거나 Map으로 연결해서 사용하고 있지만 다른 분들의 코드를 보니 WeakMap을 많이 활용한 예시들이 있는데 정말 필요한건지 판단이 잘 안 섭니다.

일단 "이런게 있어" 라는것만 인지해도 좋아요 ㅎㅎ 필요한 상황은 너무 가지 각색이라... 이런 API가 있다는걸 인지하고 있다면 문제가 발생했을 때 갑자기 생각나서 사용하는 경우들이 종종 있답니다.

이런 경험적인 측면이 강해서.... 저도 실무에서 쓰는 경우는 이번 과제랑 유사한데, native 이벤트를 별도의 메모리에 저장해서 관리하고 해제할 때 사용하고 있답니다! react와 연관되지 않은 코드다보니.. 직접 메모리 관리를 해줘야 해서요 ㅋㅋ

보통 라이브러리 같은거 만들 때 쓰일 수 있을 것 같네요!