q1Lim 님의 상세페이지[9팀 임규원] Chapter 1-2. 프레임워크 없이 SPA 만들기 (2)

과제 체크포인트

배포 링크

https://q1lim.github.io/front_6th_chapter1-2/

기본과제

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

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

이벤트 위임

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

심화 과제

Diff 알고리즘 구현

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

과제 셀프회고

1주차 과제를 완성하지 않았지만, 가상 DOM 구현이라는 부분은 다시 새로운 시작이라고 생각하고 과제를 진행했습니다.

  1. 기본 과제 : Virtual DOM에 대해서는 책에서 그려진 트리 구조의 이론적인 부분만 생각했는데, 실제로 구현하는 부분에 대해 머릿속으로 정확히 그려지지 않았습니다. 함수를 하나씩 작성하면서 몰랐던 타입이나 함수에 대해 확인하는 작업을 문서에 작성하고 추후에 딥다이브 할 파트에 대해 리스트업하는 과정을 반복적으로 진행했습니다. (큰 틀의 흐름은 AI에게 문의함)
  2. 심화 과제 : 기본과제의 AI 의존도가 40%정도라면, 심화 과제의 경우 AI 의존도가 90%로.. 좋지 않았습니다. AI가 작성한 코드를 이해하는데 급급했고, 일부 테스트를 통과하지 못해 로컬에만 남았습니다.. :(

기술적 성장

  • 평소에 사용하는 jsx가 createVNode -> normalizeVNode -> createElement -> renderElement로 변환/매핑되는 흐름을 단계적으로 확인할 수 있었습니다. jsx는 객체 구조로 변환 & 재구성 되어야함을 다시 한번 깨닫게 되었습니다.

코드 품질

  • 리팩토링이 필요한 부분 : 반복적으로 타입을 체크하는 과정이 있었습니다.
  // 함수형 컴포넌트 오류 발생 에러 추가
  if (typeof vNode === CHECK_TYPES.FUNCTION) {
    throw new Error('컴포넌트는 createElement로 처리할 수 없습니다.');
  }
  // null, undefined, boolean값에 빈 텍스트 노드로 반환
  if (vNode === null || vNode === undefined || vNode === true || vNode === false) {
    return document.createTextNode('');
  }
  // 숫자에 대해 텍스트 노드로 반환
  if (typeof vNode === CHECK_TYPES.STRING || typeof vNode === CHECK_TYPES.NUMBER) {
    return document.createTextNode(String(vNode));
  }

유사하게 노드의 타입을 체크하는 부분이 있었는데, 이 부분을 함수로 만들어서 공통적으로 적용할 수 있는 방향으로 만들고 싶었습니다.

normalizedVNode.js

  // null, undefined, boolean 값은 빈 문자열로 변환되어야 한다.
  if (vNode === null || vNode === undefined || vNode === true || vNode === false) {
    return '';
  }
  // 문자열과 숫자는 문자열로 변환되어야 한다.
  if (typeof vNode === CHECK_TYPES.STRING) {
    return vNode;
  }
  if (typeof vNode === CHECK_TYPES.NUMBER) {
    return String(vNode);
  }
  • String() vs toString() : 개인적으로 어떤 함수를 쓰는 게 더 좋을지를 잘 몰라 AI를 통해 물어보곤 하는데, toString()을 자주 쓰다가 이번에는 안정적인 방향으로 가고자 String()을 썼는데, 이미 null / undefined를 구분해서 분기 상 안전적인 상태였던 걸 이후 코드를 보고나서야 생각했습니다(!) 이후 디스코드를 보니 toString()이 성능상 더 이점이 있다고해서 이 부분은 toString으로 수정해도 될 것 같다는 생각을 했습니다.

만족스러운 구현..은 아니고 다행이었던 부분! constants.js

// 자주 사용하게 되는 문자열 정리
export const CHECK_TYPES = {
  STRING: 'string',
  NUMBER: 'number',
  BOOLEAN: 'boolean',
  FUNCTION: 'function',
  OBJECT: 'object',
};

export const ATTR = {
  CLASSNAME: 'className',
  STYLE: 'style',
  CLASS: 'class',
};

export const BOOLEAN_ATTR = ['checked', 'selected', 'readOnly', 'disabled'];

export const EVENT_TYPES = ['click', 'focus', 'blur', 'mouseover', 'keydown', 'keyup', 'change'];

  • 처음에 basic test만 수행하다가, 나중에 e2e를 수행하면서 정상적으로 작동하는지, 확인 중이었는데 상품 정렬이 가격 높은 순으로 설정하면, 정상적으로 url과 목록이 바뀌지 않는 것을 보고 당황했습니다. 알고보니 EVNET_TYPES에 'change'를 추가하지 않아 발생했던 것으로.. 가벼운 이슈지만 그래도 따로 리스트를 모아두어서 바로 고칠 수 있었던 부분이 시간 절약상 다행이라고 생각했습니다. 하지만 과제로 보았을 때 변수명이 명확하지 않다면 다른사람이 코드를 볼 때 직관적이지 않을 수도 있겠다라고 반면에 들기도 했습니다.

학습 효과 분석

  • 추가 학습이 필요한 영역 : 반복적으로 수행하는 재귀함수 부분에 대해 연습하기 / Map() - WeakMap() 차이점과 효율적으로 쓰는 방법 공부하기 / 함수형 컴포넌트에 대한 이해 높이기

과제 피드백

  • 과제에서 좋았던 부분 : 함수를 단계적으로 구현하면서 흐름을 알 수 있게 구성이 되어있던 부분이 좋았고, 특히 renderElement에서 퍼즐조각이 맞춰지는 느낌이 들었습니다.

리뷰 받고 싶은 내용

과제 피드백

고생하셨어요 규원님! 관련해서 회고를 읽어봤는데요.

constants에서 사용중인 상수같은것들은 여유가 되신다면 명확하게 타입을 가져와 쓰거나 선언해서 쓰신다면 문제를 찾고 방지하는데 더 좋지 않을까 싶네요 ㅎㅎ

추가로 AI에 의존을 바로 쉽게 하기보다는 나만의 흐름을 만들고 명확하게 코드 작성에 들어가면 좋을 것 같아요! (사실 나만의 흐름을 만드는 것도 AI의 도움을 받아 명확하게 머리에서 정리할 수 있겠죠? 다이어그램을 만든다거나 작업의 절차를 주석 또는 체크리스트로 만든다거나 하면서요)

아쉽게 심화과제를 마무리 하지 못하셨는데 꼭 마무리 잘하시고 담주도 잘 해내시길 바랍니다. 화이팅입니다!