프로그래머스 MIL#4

genTe·2024년 1월 23일
post-thumbnail

중간 프로젝트를 진행했습니다.
이번 MIL에서는 어떤 프로젝트를 만들었고, 어떤 문제가 있었으며,
문제 해결 방법 및 앞으로 어떻게 할지에 대해 말해보려고 합니다.

중간 프로젝트

모모 (모두의 모임)

모임을 모집하기위해 다른 사람들에게 시간 언제 되냐고 일일이 물어봐야되는 페인포인트에서 착안하여 서로 간편하게 약속을 잡을 수 있게 해주는 서비스를 기획하게 되었습니다.

물론 아이디어는 다른 팀원분이 제시해 주신거지만 ㅎㅎ;;

어쨌든 처음 시작은 순조로웠습니다.
개발 환경 세팅부터 깃허브 컨밴션, 유저 플로우 및 와이어프레임을 작성하고 공통으로 쓰일 컴포넌트들을 추려서 storybook으로 개발하기까지 완벽한 흐름이었습니다.

문제는 우리 프로젝트의 주요 기능인 "타임테이블"을 제가 맡으면서 시작됐습니다...


무슨 문제가 있었나?

드래그로 원하는 시간, 날짜를 투표할 수 있는 타임 테이블

타임테이블은 다른 사람들이 투표한 일정표를 보면서 내 일정을 투표할 수 있는 사용자 친화적 UI입니다.
투표하는 날짜와 시간을 사용자가 손쉽게 정할 수 있도록 표 형태를 띄며 클릭과 드래그를 통해 일정을 투표할 수도, 취소할 수도 있어야 합니다.

처음 이 기능을 맡기로 했을때 든 생각은 "재밌겠다" 였습니다.
이벤트 위임을 통해 테이블이 모든 셀들의 배경색을 상호작용에 따라 변경하면 되겠다는 아이디어 하나만으로 쉽게 생각해버렸습니다.

기능개발을 시작하고 하루가 지난 시점에 뭔가 심상치 않음을 느꼈습니다.
"리액트에선 이벤트 위임을 어떻게 하지?",
"위임을 하더라도 배경색을 바꾸는건 결국 DOM을 조작해야 되는거 아닌가?",
"마우스 드래그 영역이 줄어들면 줄어든 영역만 변경사항을 취소시키는 로직은 도대체 어떻게 짜야돼?",
"아니 그것보다 내가 지금 어떤 데이터를 다뤄야 하는거지?"
... ...

꼬리에 꼬리를 무는 의문들로 UI 설계부터 발목이 잡혔습니다.
그래서 일단 생각을 정리하기위해 필요한 데이터와 가용한 데이터를 구분했고
그걸 바탕으로 UI 구조를 재정립했습니다.

여기까지는 늘 해오던 일이라 그러려니 했지만 이벤트와 관련된 문제는 도저히 정리가 되지 않았습니다.
사실 이 시점에서 멘토님이나 다른 팀원들에게 도움을 구했어야 했지만 그 당시의 저는 뭔가에 홀린것마냥 "좀만 더 하면 되겠는데?"라고 자만하고 있었습니다.

GPT에게 폭풍 질문을 하며 제가 구상하던 첫 UI가 나왔고 결과는 처참했습니다.
어찌저찌 의도된데로 동작은 하는데 마우스를 조금만 빠르게 움직여도 엉뚱한 셀들이 선택되고, 브라우저가 극한까지 프레임을 짜내는가 하면, 일정 선택 후 다른 상호작용이 발생하면 선택된 셀들이 초기화 되는 등...

여기서 가장 큰 문제는 이 버그들이 왜 발생하는지 원인을 모른다는 것이었습니다.
원인을 모르니 지금 현상과 전혀 상관없는 투표 선택 로직에 문제가 있다는 잘못된 판단을 해버렸습니다.

그리고 지금까지 했던 실수 중에서 가장 큰 실수를 저지르고 말았습니다...

다른 팀원분이 찾아주신 라이브러리를 "이거 곧 완성돼요"라며 거절하고 말았습니다 ㅠㅠ

그 당시에는 일부 성능 이슈를 제외하곤 쓸만한 상태라고 생각했던 것 같습니다..
근데 이후에 투표 결과에 마우스 호버했을시 참여자 목록을 툴팁으로 보여주는 기능에서 선택된 셀들이 초기화 돼버리는 문제가 발생하면서 제가 근본적으로 틀린 방법을 사용하고 있었다는걸 깨닳았습니다.


무엇이 원인이었나?

엉뚱한 셀들이 선택되는 문제는 key값을 제대로 설정하지 않아서 (new Date()를 썼습니다..) 리액트가 변경사항을 반영하지 못한 것이었고,

브라우저가 힘들어했던 이유는 모든 셀들이 자신의 상태와 상관없이 리렌더링되는 최적화 문제였고,

다른 상호작용시 초기화됐던 건 셀들을 가상 DOM이 아닌 일반 DOM으로 조작했기 때문이었습니다.

일반 DOM은 리액트의 상태 관리에 영향을 받지 않기 때문에 리렌더링이 발생해도 변경되지 않습니다.
이 문제가 초기화와 연관이 있는 이유는 리액트가 테이블을 그릴 당시의 상태만을 기억하기 때문입니다.
실제 DOM 조작으로 화면에서 배경색이 변경되어도 다른 상호작용으로 상태가 변경되면 리렌더링이 발생하면서 리액트가 기억하는 초기 배경색으로 돌아가는 것입니다.

이렇게 원인을 적고 보니 굉장히 단순한 이유들이네요.

해결 방법은 문제가 발생할만한 지점에 일일이 console.log를 찍어서 원인을 찾는 노가다였기 때문에...
더 자세히 적진 않겠습니다.


문제가 왜 발생했는가?

왜 이런 문제가 발생했는지 생각을 해보면 크게 3가지인 것 같습니다.

1. 세부적인 기능명세를 작성하지 않고 머릿속으로 그려가며 구현하였습니다.

첫째로 세부적인 기능명세를 작성하지 않아 오로지 제 직감에 의존한 코드를 짜고 있었습니다.
간단한 작업의 경우 이 방식으로 빠르게 끝낼 수 있겠지만
지금과 같은 규모의 기능은 오히려 직관적이지 않은 스파게티 코드가 만들어지고
문제 발생 시 어느 부분에서 문제가 발생하였는지 찾기가 매우 어려웠습니다.

2. 리액트에서 상태로 리렌더링이 발생한다는 사실을 간과하였습니다.

둘째로 리액트를 잘 안다고 자만하였습니다.
리액트가 가상돔으로 변경된 상태를 감지한다는 것을 머리로만 알고
실제 개발 환경에서 어떤 영향을 끼치는지 전혀 모르고 있었습니다.
타임테이블 version 1의 경우 각 요소를 DOM api를 통해 접근하여 (childNodes)
그 정보를 2차원 배열에 저장하고 style을 직접 변경하는 식으로 구현하였는데
실제 DOM에서 변경된 정보를 가상 DOM이 감지하지 못하다보니 앞서 말씀드린 초기화 문제가 발생했습니다.

3. 문제를 팀원들과 공유하는 것에 소홀했습니다.

저의 가장 큰 문제점이라고 생각하는 부분입니다.
문제가 발생하면 어떻게든 제가 해결을 하려고 고집을 부립니다.
이전까지는 이렇게 끈질기게 맡은 일을 해내는 역량이 필요하다고 생각하였고
지금도 어느정도는 필요하다는데에 동의하지만
기간이 정해진 일을 할 때에는 이런 성향이 오히려 독이 되는 것 같습니다.

또, 어떤 부분에서 문제가 발생했는지 확실하게 설명할 수 없다보니
다른 팀원분들이나 멘토님께 도움을 구하기가 어려웠던 것도 있습니다.

그리고 저희가 협업을 하는 이유가 혼자서는 불가능한 결과물을 만들어내기 때문이라는 것을 까맣게 잊어버리고 있었습니다.
각자 맡은 일만 하는건 협업의 의미를 퇴색시킨다는 것을 이번 실수를 통해 깨닳았습니다.


앞으로 할 일

다음에 똑같은 문제가 발생하지 않도록 위에서 언급한 문제점들을 되새기면서 리팩토링 해보려고 합니다.

그리고 제가 구현한 다른 기능들도 자세히 기록하고 싶습니다.

유저의 드래그에 따라 투표 선택 영역을 정하는 로직이나
행 헤더는 좌우 스크롤에, 열 헤더는 상하 스크롤에 영향을 받지 않고 고정되게 sticky를 적용한 방법,
webWorker를 사용하여 알림 목록을 갱신함으로써 메인 스레드가 블록킹 되지 않게 최적화한 방법 등등

저 개인적으로 사용해보고 싶었던 기술들을 적용할 수 있어서 굉장히 만족스러운 프로젝트였습니다.

다만 제가 타임테이블에 시간을 너무 많이 할애하는 바람에 팀 기여도가 낮은 감이 없잖아 있어서 팀원분들께 죄송한 마음입니다...

다음에 또 기회가 있다면 더 발전된 모습으로 뵙겠습니다!

마음 맞는 팀원분들과 멘토님 덕분에 프로젝트 기간 내내 즐겁게 소통하고 코딩할 수 있어서 너무 좋았습니다!

1개의 댓글

comment-user-thumbnail
2024년 1월 23일

고생많으셨어요~!

답글 달기