Skip to content
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

[10팀 이송이] [Chapter 1-2] 프레임워크 없이 SPA 만들기 #36

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

2Estella
Copy link

@2Estella 2Estella commented Dec 26, 2024

과제 체크포인트

기본과제

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

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

이벤트 위임

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

심화 과제

1) Diff 알고리즘 구현

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

2) 포스트 추가/좋아요 기능 구현

  • 비사용자는 포스트 작성 폼이 보이지 않는다
  • 비사용자는 포스트에 좋아요를 클릭할 경우, 경고 메세지가 발생한다.
  • 사용자는 포스트 작성 폼이 보인다.
  • 사용자는 포스트를 추가할 수 있다.
  • 사용자는 포스트에 좋아요를 클릭할 경우, 좋아요가 토글된다.

과제 셀프회고

어설프게 알고 있던 VirtualDom에 대하여 자세하게 파헤치며 학습할 수 있어서 좋았습니다. 기본적인 보일러플레이트가 제공되어 시간을 단축할 수 있었지만, 처음부터 구현 했다면 기간 안에 완료하기 쉽지 않았을 것 같습니다.
심화 과제 보다는 순서대로 가상 돔을 구현 해야 하는 기본 과제가 더욱 어렵게 느껴졌습니다.

기본적으로 제가 지키려고 했던 과제의 컨셉(?)과 조건은 아래와 같습니다.

  1. 기존 프로젝트에 기능만 추가하는 느낌으로, 요구사항에 명시 되어 있지 않은 컴포넌트는 손대지 않기
  2. 재사용성을 고려하고, 타인을 위한 코드 작성하기
    • export 하는 함수에는 주석을 달아 사용 되는 곳에서 바로 로직을 파악할 수 있게 작성하기
    • 컴포넌트 간 로직에 대해 고민해보기

기술적 성장

  • DOM과 VirtualDOM의 차이에 대해서 알게되었습니다.

    • Virtual DOM은 DOM의 가상 복제본을 메모리 내에서 관리하고, 변경 사항만 실제 DOM에 최소한으로 적용
    • DOM 변경 시 전체 트리를 다시 그려야 할 수도 있는 상황에서, Virtual DOM은 최소한의 업데이트가 가능
  • VirtualDOM을 구현하는 순서를 정확하게 알게 되었습니다. 또한 각 순서 별 해당 로직이 필요한 이유에 대해 알게 되었습니다!

    • createVNode > normalizeVNode > createElement > render > diffing > update

    • 특히 노멀라이즈 부분에서 삽질을 하며…. 해당 로직이 필요한 이유와 이점에 대하여 알게되어 좋았습니다.

      if (!is노멀라이즈) {
      	return ['입력 데이터의 형태가 다양하면 렌더링 과정에서 처리 로직이 복잡해질 수 있음', 
      	'예외 처리를 반복해야 하므로 코드가 난잡해질 위험이 있음']
      }
      
      if (is노멀라이즈) { 
      	return ['예측 가능한 데이터 구조 유지', 'DOM 변경 시 중복 작업을 최소화하고 빠른 비교 연산이 가능함'] 
      }
  • 이벤트 매니저를 구현하며 어떤 타이밍에 요소에 이벤트 위임을 해야할지 고민하며 코드를 작성해 볼 수 있었습니다.

  • updateElement 함수를 구현하며 기존 노드와 새로운 노드를 비교하여 처리하는 diff 알고리즘을 적용해 볼 수 있어서 좋았습니다.

코드 품질

  • 👍😎 : WeakMap 적용 - 메모리 관리 최적화와 자동 가비지 컬렉션을 활용하기 위함
    (WeakMap은 키로 사용된 객체가 메모리에서 제거되면, 자동으로 참조가 해제됨)

    // src/lib/eventManager.js
    
    /**
     * 요소와 이벤트 타입에 핸들러 추가
     * @param {Element} element - 이벤트를 받을 요소
     * @param {string} eventType - 이벤트 타입
     * @param {Function} handler - 이벤트 핸들러
     */
    export function addEvent(element, eventType, handler) {
      if (!eventHandlers[eventType]) {
        eventHandlers[eventType] = new WeakMap();
      }
    
      const elementHandlerMap = eventHandlers[eventType];
      elementHandlerMap.set(element, handler);
    }
  • 👎🤔 : 요소 업데이트 시 이벤트 위임이 잘 적용되고 있는지.. 이벤트 제거와 추가 로직에 대해 고민이 필요함

    // src/lib/updateElement.js
    
    /**
     * 요소의 속성을 업데이트하는 함수
     * @param {Element} target - 속성을 업데이트할 DOM 요소
     * @param {Object} originNewProps - 새로운 속성
     * @param {Object} originOldProps - 기존 속성
     */
    function updateAttributes(target, originNewProps, originOldProps) {
      // 이전 속성의 이벤트 삭제
      if (originOldProps) {
        Object.keys(originOldProps).forEach((key) => {
          if (key.startsWith("on")) {
            const eventType = key.slice(2).toLowerCase();
            removeEvent(target, eventType);
          }
        });
      }
    
      // 새로운 속성 처리
      if (originNewProps) {
        Object.entries(originNewProps).forEach(([key, value]) => {
          const oldPropsValue = originOldProps ? originOldProps[key] : undefined;
    
          if (key === "className") {
            if (value !== oldPropsValue) {
              target.classList = value;
            }
          } else if (key.startsWith("on")) {
            const eventType = key.slice(2).toLowerCase();
    
            addEvent(target, eventType, value);
          } else {
            target.setAttribute(key, value);
          }
        });
      }
    }
  • Todo: 성능 최적화 , 타입스크립트 적용 등등…..

과제 피드백

  • VirtualDOM 구현을 통해서 DOM에 대해 더 잘 이해할 수 있는 계기가 되었습니다. 프레임워크를 항상 사용하면서도 어떤 방식으로 동작하는 지 잘 알지 못했는데 이번 과제를 하며 학습할 수 있어서 좋았습니다.

리뷰 받고 싶은 내용

  • 이벤트 위임 패턴을 사용하여 이벤트 핸들러를 관리하는 방식으로 구현 했는데 혹시 보완해야 하는 부분이 있는 지 궁금합니다. 사실 이미 등록된 경우의 중복 등록 방지 로직을 추가 하려다가 안했는데(테스트에서 이상없어서…), 이런 방어적인 코드는 우선 작성하는 것이 좋을까요?
  • 컴포넌트 간 로직을 나누는 방식에 대해서도 고민을 해봤습니다.
    저는 이번 게시글 좋아요 기능을 작업하며 Post 컴포넌트(자식 컴포넌트)가 아닌 HomePage(부모 컴포넌트)에서 로직을 구현하여 props로 넘겨주는 방식으로 작업했습니다.
    저는 유지보수성과 재사용성을 위해 작은 단위의 컴포넌트에서는 최소한의 로직 또는 UI 구현을 하는 편인데, 이런 식으로 구현해도 될까요? 또한 코치님께서는 어떤 방식을 선호하시는 지와 그 이점이 무엇인지 궁금합니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map 메서드만 떠올리고 있었는데, flatMap을 활용하신 부분에서 정말 깜짝 놀랐습니다! 덕분에 더 깔끔하고 효율적인 접근 방식이라는 점을 깨닫게 되었습니다!

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