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

[12팀 김현진] [Chapter 1-2] 프레임워크 없이 SPA 만들기 #43

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

Conversation

hanghaehyunjin
Copy link

@hanghaehyunjin hanghaehyunjin commented Dec 26, 2024

과제 체크포인트

기본과제

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

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

이벤트 위임

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

심화 과제

1) Diff 알고리즘 구현

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

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

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

과제 셀프회고

기술적 성장

이번 과제를 통해 React의 Virtual DOM에 대한 이해가 더욱 깊어졌습니다.
이전에는 Virtual DOM이 단순히 변경된 DOM을 비교하고, 변경된 부분만을 실제 DOM에 반영하는 방식으로만 이해하고 있었으나,
이번에 React가 이를 효율적으로 관리하기 위해 diffing 알고리즘과 최적화 기법을 사용한다는 점을 명확히 알게 되었습니다.
Virtual DOM을 바닐라 자바스크립트로 구현하면서 이론적으로만 알았던 개념을 코드로 풀어낼 수 있었고,
React가 불필요한 렌더링을 최소화하는 방식에 대해서도 학습할 수 있었습니다.

이벤트 리스너에 대해서는 아직 깊은 이해가 부족하지만, 이번 과제에서 이벤트 리스너를 중앙에서 관리하고,
JSX 형태의 컴포넌트에서 onClick과 같은 이벤트를 태그에 직접 선언하여 사용할 수 있도록 구현하는 방식이 새로웠습니다.
이 과정에서 이벤트 처리 및 위임에 대한 기초적인 부분을 배우게 되었고, 이벤트가 어떻게 전달되고 처리되는지에 대한 이해가
조금씩 생기고 있습니다. 하지만 아직 이 부분에 대한 이해도가 더 필요한 상태임으로 앞으로 이벤트 리스너와 관련된 이해를 더 쌓고,
더 효율적인 방식으로 이벤트를 관리하는 방법에 대해 학습할 계획입니다.

코드 품질

이벤트 리스너를 업데이트하는 로직을 어디에 선언해야 할지 고민한 끝에 renderElement에서 선언하게 되었지만,
이로 인해 관심사 분리가 제대로 이루어지지 않은 것 같다는 느낌을 받았습니다.

updateEventListeners 함수

export function updateEventListeners(element, newProps, oldProps) {
  // 기존 이벤트 제거
  Object.keys(oldProps || {}).forEach((key) => {
    if (key.startsWith("on") && (!newProps || !(key in newProps))) {
      const eventType = key.slice(2).toLowerCase();
      removeEvent(element, eventType);
    }
  });

  // 새로운 이벤트 핸들러 추가
  Object.keys(newProps || {}).forEach((key) => {
    if (key.startsWith("on") && oldProps[key] !== newProps[key]) {
      const eventType = key.slice(2).toLowerCase();
      removeEvent(element, eventType); // 기존 이벤트 제거
      addEvent(element, eventType, newProps[key]); // 새로운 이벤트 추가
    }
  });
}

이 함수는 updateAttributes 함수 내부에서 호출되는데,
이때 updateAttributes는 일반 속성 처리와 이벤트 처리를 같이 담당하고 있습니다.

function updateAttributes(target, newProps, oldProps) {
  // 1. 이벤트 처리 로직 위임
  updateEventListeners(target, newProps, oldProps);

  // 2. 일반 속성 처리
  for (const [key, value] of Object.entries(newProps)) {
    if (!key.startsWith("on") && value !== oldProps[key]) {
      if (key === "className") {
        target.setAttribute("class", value);
      } else {
        target.setAttribute(key, value);
      }
    }
  }

  for (const key of Object.keys(oldProps)) {
    if (!(key in newProps) && !key.startsWith("on")) {
      target.removeAttribute(key);
    }
  }
}

현재 코드에서 forEach와 Object.entries를 많이 사용하고 있어 가독성에 어려움이 있고,
반복문을 통해 처리하는 로직이 많아 성능 저하를 일으킬 수 있는 우려가 있을것같아
리팩토링이 필요해 보이는 상황입니다.

학습 효과 분석

가장 큰 배움은 Virtual DOM과 실제 DOM의 상호작용을 직접 구현한 경험입니다. 특히 Diff 알고리즘을 통해 효율적인 DOM 업데이트 방식을 이해할 수 있었고, 이를 통해 실제 프레임워크들이 성능 최적화를 어떻게 구현하는지에 대해 깊이 있게 배울 수 있었습니다.

하지만 이벤트 관리 부분에 대해서는 아직 좀 더 이해가 필요한 상태입니다. 이벤트 리스너 처리 및 위임에 대한 추가적인 학습이 필요하다고 느꼈습니다. 이를 보완하기 위해 더 많은 학습을 진행할 계획입니다.

과제 피드백

리뷰 받고 싶은 내용

코드를 작성하는 과정에서 반복되는 코드가 많아지는 문제가 있었습니다.
특히, updateAttributes 함수가 createElement와 updateElement에서 각각 호출되고 있는데,
내부 로직을 살펴보면 비슷한 패턴이 반복되고 있습니다.
다만, 각 함수의 세부 구현이 조금씩 다르게 작성되어 있어 이런상황에서 어떻게 공통 함수로 추출할 수 있을지 고민이 됩니다.

반복 코드를 줄이고 재사용성을 높이기 위한 리팩토링 방안에 대해 조언을 부탁드립니다.
예를 들어, updateAttributes와 관련된 로직을 적절히 분리하거나, 공통화할 수 있는 함수로 만들 방법이 있을지 피드백을 받고 싶습니다.
또한, 이러한 리팩토링이 성능이나 가독성에 어떤 영향을 미칠 수 있을지도 궁금합니다.

추가적으로, 현재 코드 구조에서 관심사 분리가 잘 이루어졌는지, 개선할 점이 있다면 제안해 주시면 감사하겠습니다.

@9yurilee
Copy link

오호! 현진님 저도 두 updateAttributes의 로직이 유사하다고 생각해서 util함수로 분리했어요!
(updateElement의 로직이 createElement의 로직을 포함하고 있더라구용!)

하지만 분리하고나서도 현진님 생각처럼 Object.entries 코드가 반복되는게.. 리팩토링이 필요하다고 생각돼서 고민중이었거든요!
그래서 현진님의 리팩토링이 더욱 궁금해집니다! 추후 답변 들으시면 저도 리팩토링 과정 공유해쥬세요..ㅎ_ㅎ

@pangkyu
Copy link

pangkyu commented Dec 28, 2024

주석을 상세하게 달아주셔서 코드 읽을 때 너무 좋았습니다~! 👍👍👍

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.

3 participants