본 프로젝트는 저 혼자서 진행한것이 아닌 빅테크 기업에서 Engineering Manager로 재직중인 멘토님으로부터 멘토링을 받으며 함께 진행하였습니다.
프론트엔드 개발자로의 길을 선택한 후, React를 배우지 않으면 취업이 힘들다는 것을 곧 깨달았습니다. 그 이후로는 프로젝트를 진행할 때마다 망설임 없이 React를 기술 스택으로 사용했습니다. 그러던 어느 날, React 없이 프로젝트를 진행하는 것이 정말 어려운지 궁금해졌습니다. 그래서 이번 기회에 JavaScript만으로 SPA 프로젝트를 진행해보기로 마음먹었습니다.
기능을 구현하기 전헤 이 기능을 어떻게 구현할 것인지 기술적으로 풀어 설명하고, 제안하는 글
저는 정당성, 목표, 목표가 아닌것, 폴더 구조, 데이터 구조, 컴포넌트, 상태 관리 등은 어떻게 할것인지 미리 사전에 고민하고 문서화 해보는 시간을 가졌습니다. 처음에 귀찮게 생각했던 문서화는 결국 나중에 되어 코딩과 별도의 일이 아니라는 것을 프로젝트나 끝난후 깨달았습니다. 테크 스펙을 써 보면, 어떻게 만들 것인지 줄줄이 작성하면서 이미 웬만한 수준의 코딩이 들어가게 됩니다. 결과적으로 테크 스펙을 다 작성하고 나면 테크 스펙에서 작성했던 코드를 실제 프로덕트에 붙이는 것만으로 작업이 끝나기도 합니다. 즉, 문서화는 더이상 코딩과 별도의 일이 아니게 되었다는것을 느꼈습니다.
실제 작성한 테크스펙 입니다.
대다수의 개발자들은 개발을 하기 전에 본인이 무엇을 개발할것인지, issue들을 생성합니다. 그리고 그것들을 각각 하나의 PR로 만들고 해당 범위 안에서 개발을 하는것으로 알 고 있습니다. 그렇다 보니 종종 회사 채용 공고를 보면 프로젝트를 수행하는 데 편리한 협업 툴로써 Jira를 사용하는것을 볼 수 있었습니다. 그래서 저는 이번 프로젝트에 jira와 비슷한 맥락인 Linear를 사용하였습니다. 미리 사전에 개발할 부분을 기능 단위로 나누어 각각 하나의 티켓으로 만드는 것입니다. 그리고 그 하나의 티켓이 추후 프로젝트를 진행할때 하나의 PR이 되는것입니다. 그래서 저는 본 프로젝트를 진행하기 앞서 23개의 티켓을 작성하였습니다.
작성한 티켓 예시입니다.
이번 프로젝트는 제가 혼자 진행하는 것이 아니라 멘토님의 코드 리뷰를 받으며 진행됩니다. 따라서 PR 리뷰 요청을 드리기 전에 해당 PR의 내용을 한눈에 파악할 수 있도록 하는 것이 리뷰어에 대한 배려라는 것을 깨달았습니다. 그래서 이번 기회에, PR을 올리기 전에 미리 정의한 템플릿을 활용하기로 결정하고, PR 템플릿을 작성했습니다.
실제로 작성한 PR템플릿 입니다.
개발을 하기로 마음먹고 진행하던 가운데 한시간도 되지않아 첫 번째 에러와 마주쳤습니다. 지금 돌아보면 당연히 발생할 수 있는 에러였지만, 프로젝트를 진행하던 당시에는 프론트엔드 실력이 매우 부족한 시점이었습니다. 사실 프론트엔드 개발을 공부하면서 번들러라는 존재는 미리 전부터 많이 들어보았으며 webpack을 사용해본적이 있습니다. 하지만 에러가 발생한 것은 이 기술들의 원리를 전혀 이해하지 못하고 사용했음을 깨닫게 해주는 계기였습니다. 이 참에 다시한번 번들러가 무엇이고 왜 필요한지 부터 시작하여 동작 방식까지 하나하나 알아보았습니다. 간단한 에러라도 직접 경험하고 해결하는 과정이 길어질수록, 그 기억이 더 오래 남는다는 것을 깨달았습니다. 지금 돌이켜보면 번들러가 당연히 필요하다는 것을 알지만, 당시에는 그 이유도 모른 채 적용하려다 3일 동안 고생했던 경험이 지금까지 기억에 남아 개발에 적용된 것 같습니다.
리액트로 프로젝트를 할 때는 전혀 신경 쓰지 않았던 라우터 문제를 이번 기회에 깊이 고민해보았습니다. 한상 숨쉬듯이 사용했던 npm install react-router-dom 명령어 없이 순수 JS 만으로 라우터를 구축해보려 하니 너무 머리가 복잡하였습니다. 그렇게 다양한 방법을 알아보전 중 두 가지 접근 방식인 프래그먼트 식별자 기반으로 접근하거나 히스토리 API 기반으로 접근할 수 있다는것을 알았습니다. 결국 둘다 목적은 페이지가 전환되는데 있어 리로드 없이 URL에 따라 다른 콘텐츠를 표시한다는 점이었습니다.하지만 차이점으로는 프래그먼트 식별자 같은 경우 URL의 해시 부분을 사용하서 라우팅을 구현하지만 히스토리 API 같은 경우 히스토리 API 기반으로 라우팅을 구현한다는 점이었습니다. 좀 더 쉽게 말해 우리가 흔이 마우스로 뒤고 가기 또는 앞으로 가기 버튼 같은 경우 history.back() history.forward() 메서드를 사용하여 수행된다는 뜻입니다. 저는 라우팅 시스템을 구축하기 위해서 히스토리 API를 사용하기로 결정하였습니다. 사실, 어느 쪽을 선택해야 할지에 대해 많은 고민을 했습니다. 하지만 제가 늘 쓰는 네이버 주소창을 보고 서는 빠른 판단을 내릴수 있었습니다. 예를 들어 'http://example.com/home' 같은 주소는 자주 보지만 'http://example.com/#/home' 같은 주소는 보기 드물고 사용자가 직관적으로 이해하기 어려울 것입니다. 그 후로는 라우팅 시스템을 클래스로 만들어 필요한 기능들을 메서드로 구현하였습니다. 여기서는 처음에는 생성자 함수를 선택했지만, 코드의 가독성을 고려하여 나중에 클래스로 변경했습니다. 마지막으로는 무엇을 메서드로 넣어야 할지 많은 고민이 있었습니다. 해당 프로젝트에서는 메인 페이지, 상세 페이지, 그리고 잘못된 URL로 접근했을 때 보여주는 페이지를 고려하여 이 세 가지 경우에 대한 메서드를 적용했습니다.



라우팅을 개발하는 과정에서 고려해야 할 사항들이 많고 다양하지만, 모든 것을 완벽하게 고려하지 못하더라도 개발하면서 깊이 있는 고민이 필요하다는 것을 느꼈습니다.
프론트엔드에서 중요한 역할 중 하나인 데이터 패칭은 보통 백엔드와 협력하여 이루어집니다. 하지만 본 프로젝트는 프론트엔드 혼자서 진행할 예정이라 데이터를 직접 만들고 사전에 미리 mocking 하여 내부에서 프론트 요청에 따라 데이터를 받아와야만 하였습니다. 이 부분에 대하여 어떻게 할까 고민을 하다가 MSW 라는 기술을 찾아보았습니다. MSW는 브라우저와 Node.js 화경에서 네트워크를 가로채고 모킹할수 있는 라이브러입니다. 원래 MSW의 본 목적은 초기 단계에서 백엔드 서비스가 준비되지 않았을 때, 실제 백엔드 서비스 없이도 프론트엔드 애플리케이션의 동작을 시뮬레이션 할 수 있게 도와주는 기술 입니다. 하지만 본 프로젝트에서는 실제 서버를 구축하지 않을 예정이라, MSW만으로도 데이터 패칭이 충분히 가능하다고 판단했습니다. 그래서 저의 설계는 아래와 같습니다.

실제 서버에 요청하기 전에, axios의 인터셉터 기능을 사용하여 mock 서버로 요청을 보내는 방식입니다.
이 방법을 사용하면 실제 프로덕션 환경에서도 MSW를 통해 데이터 패칭이 가능합니다.
리액트에서는 상태 관리를 위해 Redux를 사용했지만, 바닐라 JS에서는 어떤 방법을 이용하여 상태관리를 해야할가 고민하였는데 소프트웨어 엔지니어링의 디자인 패턴중 하나인 MVC (Model-View-Controller) 패턴을 활용해 상태 관리를 진행했습니다. 제가 고민해서 만든 설계는 아래와 같습니다.

이 방식으로 프로젝트를 진행하면 유지 보수와 확장성을 염두에 두고 설계할 수 있다고 생각했습니다.
본 프로젝트에서는 5개의 컴포넌트로 나뉘어 화면에 연결해주었습니다.
헤더

에러

배너

1개의 컨테이너와 3대 7비율로 나누어 그 안에 이미지 로고와 섹션으로 분리하였습니다.
리스트

콘텐트

컴포넌트를 만들면서도 여러 가지 고민을 하지 않을 수 없었습니다. 예를 들어, px 크기를 짝수로 할지 홀수로 할지, 컨테이너 크기 조절을 상위 컴포넌트가 담당할지 하위 컴포넌트가 담당할지, 이미지 크기를 자유롭게 지정해도 되는지 등 다양한 요소들을 고려해야 했습니다. 결국 제가 결정한 방법은 px크기를 짝수 단위로 지정한것입니다. 짝수 단위로 지정한 이유중에 가장 큰 이유는 비대칭 때문입니다. 홀수 픽셀 값은 디자인에서 비대칭성을 초래할 수 있습니다. 이런 문제는 시각적으로 불균형해 보일 수 있습니다. 컨테이너의 크기 조절은 상위 컴포넌트가 담당하기로 결정했습니다. 그러한 이유는 관심사 분리 때문이었습니다. 상위 컴포넌트에서는 레이아웃과 전체적인 구조를 담당하고, 하위 컴포넌트는 자신의 콘텐츠와 스타일링에 집중할 수 있습니다. 이렇게 관심사 분리를 할 경우 추후에 유지보수가 용이 해질것이라 판단이 들어 상위 컨네이너에서 조절하였습니다. 끝으로 이미지 크기또한 같은 맥락으로 보았을때 우선 100%로 설정하고, 크기 조절은 상위 컨테이너에서 담당하는 것이 더 적절하다고 판단했습니다. 만약 컴포넌트를 깊이 고민하지 않고 구현했다면 이러한 글들은 나오지 않았을 것입니다. 이 외에도 고려해야 할 사항들이 무궁무진하다는 것을 깨달았습니다. 앞으로 컴포넌트를 구현할 때 더 깊이 고민하여 더 나은 결과를 만들어낼 계획입니다.
저는 HTML 공부를 하였을때 분명히 <header> <footer> 등 태그 들을 배운적이 있고 이것들의 역할또한 알고 있었습니다. 하지만 실제 제가 작성한 코드들을 보면 모두 습관적으로 <div> 태그 만으로 작성한것을 알 수 있었습니다. 그때 제 머릿속에 떠로은 생각은 <div> 태그 만으로 작성을 할거면, 왜 굳이 시맨틱 태그를 배웠는지, 그리고 왜 웹에서 중요한지에 대한 의문이 들기 시작하였습니다. 그래서 고민 끝에 하나의 결론에 도달했습니다. 그 결론은 바로 '웹 접근성'이라는 키워드였습니다. 간단히 말해, 특수 장애가 있는 분들이 웹을 사용할 때 불편함을 겪지 않도록, 스크린 리더 같은 도구를 활용해 보다 쉽게 웹에 접근할 수 있도록 돕는 것입니다. 그래서 시맨틱 웹 관점을 반영하여, 모든 태그를 <div>로만 작성해서는 안된다는 결론에 도달했습니다.
블로그를 작성하면서, 처음에는 간단할 것이라고 여겼던 Vanila JS SPA 프로젝트가 생각보다 훨씬 복잡하고, 고려해야 할 사항들이 많다는 것을 깨닫게 되었습니다. 그래서 제가 신중하게 고민했던 부분들을 정리하여 글로 작성해 보았습니다. 이번 프로젝트를 통해 깊이 있는 고민만이 코드의 품질을 향상시키고 확장성을 높일 수 있다는 점을 깨달았습니다.