Skip to content

[4팀 이유진] Chapter 1-2. 프레임워크 없이 SPA 만들기 (2)#35

Open
Elli-Lee wants to merge 15 commits into
hanghae-plus:mainfrom
Elli-Lee:main
Open

[4팀 이유진] Chapter 1-2. 프레임워크 없이 SPA 만들기 (2)#35
Elli-Lee wants to merge 15 commits into
hanghae-plus:mainfrom
Elli-Lee:main

Conversation

@Elli-Lee
Copy link
Copy Markdown

@Elli-Lee Elli-Lee commented Jul 14, 2025

과제 체크포인트

배포 링크

https://elli-lee.github.io/front_6th_chapter1-2/

기본과제

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

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

이벤트 위임

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

심화 과제

Diff 알고리즘 구현

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

과제 셀프회고

이번 과제의 가장 어려웠던 점은 각 함수들을 구현하는 과정에서 이 함수가 왜 필요한지, createVNode가 object를 return하는데 왜 normalizeVNode에서 한번 더 null / undefined를 처리하는지, 함수 내에서 각 분기처리를 그렇게 처리하는 이유 등등의 납득의 과정이었습니다. 과제의 목표를 명확히 이해하고 과제를 시작하고자 노력했고, 이를 위해 Virtual DOM이 필요한 이유, 동작 원리, Virtual DOM이 해결하고자 한 문제를 이해하기 위해 Vue 공식문서와 github 레포에 해당부분을 찾아보기도 하고, AI와 대화도 많이 나누는 등, 시간을 많이 투자했습니다. 이러한 노력이 과제 수행 시 덜 헤매는데 도움이 되었다고 생각합니다.
그 다음으로 어려웠던 점은 재귀의 늪이었습니다... 원하는 결과를 내고 싶어도 이를 재귀로 구현하는게 익숙하지 않아서 여러번 테스트를 돌리고 콘솔도 찍으면서 방향을 잡았습니다. 알고리즘 학습의 필요성을 느끼기도 했습니다.
여전히 코드를 깔끔하게 짜고 함수를 분리하고.. 등등은 매우 부족합니다. 그래도 최대한 이해하며 과제를 수행하려고 노력했다는 점에서는 저번주보다는 과제를 조금 더 만족스럽게 수행한 것 같습니다.

기술적 성장

새로 학습한 개념:

  • Virtual DOM의 내부 구현 메커니즘과 diff 알고리즘의 핵심 원리
  • 이벤트 위임 패턴을 활용한 효율적인 이벤트 관리 시스템

코드 품질

이벤트 관리자의 깔끔한 구조: WeakMap 기반의 계층적 이벤트 저장과 자동 정리
updateElement의 체계적인 분기 처리: 각 케이스별 명확한 로직 분리

가장 큰 배움이 있었던 부분

Virtual DOM의 동작 원리를 직접 구현하면서 이해하다보니, Vue등의 SPA 프레임워크들이 해결하고자 했던 문제가 조금은 보이는 것 같습니다.
이번 과제를 통해 Virtual DOM의 내부 구현 메커니즘과 diff 알고리즘의 핵심 원리에 대해 명확히 알게 되었습니다.
이번주에 실무에서 급하게 해결해야 했던 이슈 중 하나가 사실
"이미 A페이지에 위치한 사용자가 알림 등을 클릭해서 다른 페이지로 router push가 발생할 때, 만약 이동하려는 페이지가 이미 사용자가 위치한 A 페이지라면, 화면상에서는 아무런 렌더링이 발생하지 않아 사용자 입장에서는 클릭 이벤트에 버그가 있어서 router를 이동하는 것에 실패했다고 여긴다." 였습니다.
이번 과제를 수행하면서 해당 이슈를 접하니, 아무런 렌더링이 발생하지 않는 이유가 router 이동이 있어도 이동 전 후에 달라진 점이 없기 때문에 Vue의 Virtual DOM이 실제 DOM 업데이트를 진행하지 않아서임을 금방 이해할 수 있게 되었습니다.
덕분에 문제를 빨리 해결할 수 있었고, 이렇게 근본적인 동작 원리까지 아는 것의 중요성을 다시 한번 느낄 수 있었습니다.

추가 학습이 필요한 영역

  • 아직 이벤트 위임에 대해 스스로 누군가에게 설명할 정도로의 이해를 하지 못했습니다. 이번주 내용을 복습하고 여러번 읽으면서 추가학습을 할 예정입니다.
  • WeakMap 자료구조를 사용하는 것이 좋을거라고 추천받아 일단 사용하긴 했으나, 어떤 점에서 좋은지, Map과의 차이점은 무엇인지에 대해 아직 학습하지 못해서 추가학습이 필요합니다.
  • 아직은 배포가 쉽지 않습니다.. 왜 그렇게 수정하는지 이해하지 못한채로 AI가 시키는대로 배포를 수정하다보니 어떤 걸 수정해서 배포를 성공한건지 모르겠습니다..

과제 피드백

  • Virtual DOM의 동작 원리를 직접 구현하다보니 Virtual DOM의 필요성에 대해 더 잘 이해할 수 있었고, 저번 과제와 비교하여 virtual DOM 덕분에 실제로 필요한 부분만 렌더링 됨을 직접 알 수 있어서 좋았습니다.
  • 촘촘한 테스트케이스로, 제가 처음 구현할 때 생각하지 못한 케이스들을 테스트를 통해 찾아서 계속 추가로 구현 할 수 있어서 좋았습니다.

리뷰 받고 싶은 내용

불리언 속성 처리 방식 중 checked, readonly, selected 같은 불리언 속성을 property only로 다루고 있는데, 이런 처리 방식이 올바른 접근인지 궁금합니다.

updateAttributes에서 속성 처리 순서나 우선순위가 적절한지 궁금합니다.
className → 이벤트 → boolean → 나머지 순으로 처리하고 있는데, 이런 순서가 DOM 업데이트 시 예상치 못한 사이드 이펙트를 막는 데 적절한지 궁금합니다.

리뷰 받고 싶은 포인트를 찾기 위해 전체적으로 코드를 되돌아보다보니, createElement와 updateElement의
const booleanProps = ["checked", "disabled", "selected", "readonly", "multiple", "autofocus", "required"];
const propertyOnlyBooleanProps = ["checked", "selected"];
const propToAttributeMap = {
readOnly: "readonly",
};
부분을 공통으로 쓸 수 있을것 같아 보입니다. 과제 제출 이후 수정하겠습니다!

@Elli-Lee
Copy link
Copy Markdown
Author

  1. updateAttributes의 하는 역할이 많은 것 같은데, 팀원분들께서는 어떻게 분리하셨는지 궁금합니다!
    하는일들: class 처리
    이벤트 바인딩
    boolean 속성 처리
    속성 제거/추가

@eveneul
Copy link
Copy Markdown

eveneul commented Jul 19, 2025

안녕하세요, 유진 공주님. 접니다.
updateAttributes는 일단 저도 if문 떡칠했습니다.

for (const [key, value] of Object.entries(props))

로 일단 props를 반복해 주면서

    if (key === "style") {
      Object.assign($el.style, value);
      continue;
    }

    if (key === "className") {
      $el.setAttribute("class", value);
      continue;
    }

했습니다. if문 싫어잉~

@nimusmix
Copy link
Copy Markdown

nimusmix commented Jul 19, 2025

저는 아래처럼 set과 remove로 나눠주었습니다!

const updateAttributes = (target, originNewProps, originOldProps) => {
  const newProps = originNewProps || {};
  const oldProps = originOldProps || {};

  setAttributes(target, newProps, oldProps);
  removeAttributes(target, newProps, oldProps);
};

@adds9810
Copy link
Copy Markdown

adds9810 commented Jul 19, 2025

if-else로 각 속성 타입별로 다르게 처리했습니다.

function updateAttributes($el, props) {
  Object.entries(props).forEach(([key, value]) => {
    if (key.startsWith("on")) {
      // 이벤트 바인딩
    } else if (key === "className") {
      // class 처리
    } else if (isBooleanAttribute(key)) {
      // boolean 속성 처리
    } else {
      // 속성 제거/추가
    }
  });
}

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.

4 participants