프레임워크 없이 SPA 만들기 - Part 2 9일차

anvel·2025년 3월 30일

항해 플러스

목록 보기
6/39

2주차 과제 시작

어제 발제시간부터 흥미로운 주제에 대하여 코딩의욕이 솟았습니다.
수년째 React를 사용하면서도, 내부적으로 어떤 방식으로 Virtual DOM을 구현하는 지, 어떤 방식으로 실제 HTML에 내용이 반영되는 건 지, 궁금해하다가도 결국 업무가 우선되어 보지 않았던 부분을 이해하기 쉬운 순서로 과제에 녹여내주셨다는 느낌이 들었습니다.

Virtual DOM

공부한 걸 기록하기 위해 쓰는 글은 아니지만 대략적으로 설명해보자면,
명령형으로 하나하나 조작하던 HTML의 DOM 구조를 모방한 객체구조로 메모리에 관리하면서, UI를 갱신하는 과정을 캡슐화하고, 변화가 완료된 상태를 선언형으로 작성하여, 개발의 편의성과 직관성을 높이는 방식
이라고 이해를 하게된 발제 시간이었습니다.

1. 각인된 예시

먼저, 발제 시간에 몇 년전에 React 공부하면서 찾아봤던 블로그 글이, 발제자인 코치님께서 작성하셨던 글인 걸 알게되어 한번 깜짝 놀랐고, 지금 머리속에 약간이나마 들어있는 개념들이 저 블로그 글을 공부하면서 학습했던 내용들이 박혀있던 것이었구나를 깨달았습니다.

https://junilhwang.github.io/TIL/Javascript/Design/Vanilla-JS-Virtual-DOM/#_1-%E1%84%91%E1%85%A1%E1%84%89%E1%85%B5%E1%86%BC

특히나, 내용 중에 있는 h함수 예시는 입사 이후에 바닐라만 사용하는 현 회사 상태에서 일부분에 참조하여 사용했던 코드였습니다.

// 원글의 참조 함수
function h(type, props, ...children) { ... }

// 회사 코드 스타일을 고려하여 업무에 반영한 함수
const $CE = (tag, id, styles, ...children) => { };
// tag가 특수문자(.)가 포함되지 않았다면 생성, 아니면 클래스 형태로 돔에 기입
// 이벤트는 아예 다른 곳에서 관리하여 id와 inline 스타일 속성만 할당

완전히 Virtual DOM으로 트랜스 파일링을 하는 것은 아니었고, 레거시 솔루션에 document.으로 직접 조작하는 부분이 너무 많아서 리팩토링하는 과정에서도 해당 블로그를 몇번씩은 봤던 터라, 이번 과제의 기초 부분은 거의 동일하게 완료가 가능하여 오늘 오전까지 미리 완료할 수 있었습니다.

주중에는 가상돔을 사용하는 가장 큰 이유인, 상태에 따른 랜더링 부분 구현에 집중할 것 같습니다.

2. 테스트 주도 개발..?

이번 과제는 발제시간에 주셨던 팁을 기반으로 테스트 하나하나 순서대로 개발을 하였습니다.
일부 코드는 이미 대략적인 예시로 설명해주셨었고, 또 이미 과정을 블로그로 몇번이나 봤던 터라 기초 부분은 발제 및 팀 토의가 끝나고, 거의 완료가되었었습니다.

완성된 형태에 대한 테스트 결과가 있다보니, 코드에 견고함에 집중할 수 있었습니다. 대부분의 개발을 구현된 모습과 필요한 기능은 주어지지만, 실제 동작할 때의 필요한 사항에 대하여선 어느정도 구현 이후에나 알수 있던 것과는 다르게, 단계별로 진행하면서 기능 하나하나의 요구 사항을 만족시키는 경험은 새로웠고, 뭔가 알 수 없는 성취감도 느껴졌습니다.

물론 이 과제가 TDD를 논하지도, 실제 개발에서도 이정도로 진행하는지도 학습을 더 해야만 알 수 있겠지만, 자주 들어온 목적을 위해서 테스트 코드를 잘짜는 개발자라는 개념이 어떤 것인 지 조금은 느끼게 되었습니다.
또한 테스트 코드에 매몰될 수도 있겠구나 하는 생각도 들었습니다.

3. 어김없는 오류

사실 어제 끝낼 수도 있던 기본 과제인데, 오늘 오전까지 골머리를 썩였던 테스트 코드가 있었습니다.
먼저 해결 방법에 대하여 설명하자면,


// 실패
export function setupEventListeners($root) {
  eventTypes.forEach((eventType) => {
    $root.addEventListener(eventType, (e) => { /* ... */ });
  });
}

// 성공
export function setupEventListeners($root) {
  eventTypes.forEach((eventType) => {
    $root.addEventListener(eventType, handleEvent);
  });
}
const handleEvent = (e) => { /* ... */ };

바로, addEventListener함수 두번 째 인자로 선언된 함수를 넣느냐, 익명함수를 넣느냐 가 문제였습니다.
이전과 마찬가지로, 이벤트리스너에는 익명함수를 쓰는 습관에 의한 것으로, 이 부분이 기능적으로 문제가 될 수도 있다는 것을 알게 된 오류였습니다.

해당 테스트 코드의 toHaveBeenCalledTimes 함수를 통해, 이벤트가 익명함수로 인해 중복되어 선언이 되어있을 수 있음을 알았고, 내일 출근하게 되면 그동안 사용했던 이벤트 리스너를 한번씩은 다시 돌아 봐야겠다는 생각을 하였습니다.

1주차와 마찬가지로 항해99를 수행하면서 테스트 도구에 대한 학습을 할 수 있고, 이런 툴들이 어떤 부분에서 제 코딩에 도움을 주는 지 체감하였습니다.

0개의 댓글