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 만들기 Part 2 #5

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

Conversation

creco-hanghae
Copy link

@creco-hanghae creco-hanghae commented Dec 22, 2024

과제 체크포인트

기본과제

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

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

이벤트 위임

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

심화 과제

1) Diff 알고리즘 구현

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

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

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

과제 셀프회고

  • 단순히 기술 구현 뿐만 아니라 리액트는 어떻게 최적화를 진행할까에 대해서도 고민하면서 해보고자 했습니다.

기술적 성장

이번 과제에서는 리액트의 key 방식을 사용하는 배경에 대해서 좀 알게 되었습니다.

  • Dom 의 변경을 감지하는 방식에는 어떤 걸 검사해야하는지 어떤 기준으로 변경이 되었다고 하는지를 고민하게 되었습니다.
    • 이를테면, VNode 의 Type 이 바뀌면 아예 바뀐 것이므로 Remove -> Create (REPLACE) 동작이 되도록 합니다. 그렇지 않다면 properties 만 업데이트하게 합니다.
    • 중요한 점은 Children 들에 대해서 모두 index 기반으로 검사하기 때문에 순서가 맨 앞에 추가되는 Post 게시글 추가 방식에 대해서는 항상 모든 자식이 리렌더링이 되는 문제가 있을 수 있다는 것을 알게 되었습니다.
    • 그래서 key 기반의 변경 감지를 채택하게 되었습니다. (key 만 바뀌면 업데이트한다는게 아니라, oldNode 와 newNode 의 자식 중 key 를 기반으로 찾은 같은 형상의 Node 를 비교하게 되었습니다.)
    • 덕분에 변경되지 않은 나머지 자식들은 렌더링이 되지 않도록 최적화할 수 있었습니다.
    • 이를 통해 배운 점은 리택트에서도 배열을 렌더링할 때 key 를 입력하지 않으면 경고문구가 뜨곤 했는데, 이게 바로 리렌더링을 최적화하기 위함을 이번 계기를 통해 알게 되었습니다.

코드 품질

  • TypeScript 를 도입해서 VNode / NormalizeVNode 를 구분 하는 등 각종 VNode 다루는 함수에서 빌드타임에서 버그를 미리 방지할 수 있도록 타이핑해두었습니다.

  • updateElement 파일에 있는 calculateUpdateOperation 를 통해 newNode 와 oldNode 를 비교해 추가/삭제/변경인지 판단해서 동작에 대한 구분을 더 명시적으로 할 수 있도록 처리했습니다.

최적화 구현

  • 컴포넌트에 key 방식을 도입하냐 안하냐에 리렌더링이 최적화 되는지 안되는지를 알 수 있게 로깅을 추가해두었습니다.
    • key 방식을 도입하지 않았다면, index 기반으로 변경감지를 하기 때문에 맨 앞에 추가되는 VNode 때문에 모든 자식 VNode 이 모두 변경되었다고 판단하여 모두 렌더링됩니다.
코드 결과 최적화여부
image image
image image ⭕️

리팩토링이 필요한 부분

  • key 를 쓰지 않으면 index 기반의 전체 리렌더링이 되기 때문에 문제가 되지 않던 이슈가 하나 있습니다.
  • key 를 쓰게 되는 순간 전체 리렌더링이 아니기 때문에 앞에 추가할지 뒤에 추가할지를 updateElement 가 알아야 했습니다.
    • 그래서 일단 Post 가 추가되는 방식이 맨 앞에 추가되는 방식이다보니 VNode 의 순서가 맨 앞에 추가되냐 맨 뒤에 추가되냐에 대한 로직을 코드상에서 data attributes 를 통해 data-add-type 으로 prepend 를 할지 아닐지를 결정할 수 있게 열어두었습니다.
image image
  • 다만 이러한 방식은 당장 최적화를 위해 현 프로젝트에서 기능으로 요구되는 "맨 앞 노드 추가"를 대응하기 위해 적용해둔 맥락이 강결합된 코드이고, 이러한 로직이 updateElement 안에 들어가 있다는 것 자체가 문제가 될 여지가 있다고 봅니다.
  • 그래서 이런 부분은 updateElement 가 몰라야된다고 생각합니다. (지금은 문제가 많은 코드인 것 같습니다)

리뷰 받고 싶은 내용

  • 리팩토링이 필요한 부분에서 언급한 내용처럼 요구사항에 따라 달라지는 dom 추가 방식(append or prepend) 에 대한 로직과 변경감지 후 Virtual DOM 을 실제 DOM 에 적용하는 로직이 강결합되지 않으려면 대략적으로 어떤 구조를 가져가면 좋을까요?

@creco-hanghae creco-hanghae marked this pull request as ready for review December 27, 2024 00:58
@9yurilee
Copy link

진짜.. 전 왜 여러번 리렌더링 되는걸 당연하게 생각했을까요..😭!!
석호님이 어떻게 리팩토링 하실 지 너무 궁금합니다...

handler: EventListener;
}

export const eventManager = (() => {

Choose a reason for hiding this comment

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

eventManager를 캡슐화해서 관리하니까 로직이 깔끔하고 가독성이 훨씬 좋아지는군요!! 덕분에 많이 배워갑니다!👍🏻👍🏻

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