이건 아마, "프론트엔드"라는 분야를 관통하는 질문일 것이다.
데이터를 다루는 모든 서비스의 가장 핵심 과제일 만큼 중요한..
그 중요성을 간과한 나는 결국 한참 개발이 진행되었을 때 앱의 데이터 플로우를 한번 갈아엎는 🚧대공사🚧를 해야만 했다.
실제로 사용자가 사용해야 하는 규모의 프로젝트는 처음이어서, 처음 구조를 짤 때는 별 생각이 없었던 것 같다.
모든 데이터를 하나의 배열[]에 담아서 넘기고 받으면,
클라이언트-서버 간 API통신을 최소화할 수 있지 않을까?
였다. 꽤나 그럴듯해 보이지만, 상당히 비효율적인 생각이었다.
태그를 만들든, 북마크를 하든, 메모를 삭제하든 똑같은 API통신 로직을 이용해서 전체 데이터를 새로 받자는 생각인데, 만들어야 하는 API는 하나로 통일할 수는 있겠으나 한 번 통신에 담아야 하는 데이터가 갈수록 무거워지는 구조였다.
또, 어차피 통신은 사용자가 메모를 조작하는 모든 경우(추가, 수정, 삭제 등)에 일어나기 때문에 요청-응답이 일어나는 횟수는 같으나, 항상 payload로 모든 데이터를 주고받는다면 원래 의도와는 달리 손해만 보는 구조였다.
사실 처음에는 API요청이 일어나는 시점을 유저의 모든 조작 시가 아닌, 앱을 열었을 때 (pre-load) 그리고 앱을 닫을 때로 생각하고 있었기도 했고, 데이터를 다 서버에 저장하는 것이 아닌 유저 디바이스의 저장소에 저장하는 경우도 염두에 두고 있어서 이런 기획이 나온 부분도 있으나..
결국에 처음 한 생각을 기반으로 짠 코드에는 🚧칼을 대게 되었다🚧.
모든 데이터를 하나의 배열에 담아 넘기고 받을 생각이었기 때문에, 어떤 컴포넌트에서든 이 하나의 배열에 접근할 필요성이 생겼다.
하나의 배열..? 하나의 store..? 이건...리덕스다
순수한..아니 순진한 나는 하나의 리듀서만을 사용해 모든 상태를 간편하게 관리할 수 있을 것이라고 믿기 시작했고, 실행에 옮기기 시작했다 ST..AY...
아래는 내가 그렇게 믿던 시절 '하나의 배열'을 어떻게 구성할지 워드에 정리해 놓은 부분이다.
배열은 하나인데, 태그와 메모에 대한 내용을 모두 담아야 하기 때문에, 배열의 0번 index의 요소에 태그 정보를 담고자 했다.
태그에 관한 정보가 필요하면 State[0]을 추출해서 쓰고, 메모에 관한 정보가 필요하면 0번째 index에 존재하는 태그 정보 배열을 빼고 slice해서 쓸 생각이었다.
특정 태그에 어떤 메모들이 존재하는지 알아야 하기 때문에, 태그 정보를 담고 있는 배열(State[0]) 안에
memos라는 멤버를 두어 해당 태그에 존재하는 메모들의 id를 배열로 관리해 주겠다고 생각했다.
그 결과..
배열 안에 배열 안에 배열이 있는 구조가 완성되었다..끔찍하다
개발 기한이 짧았기 때문에 단순히 이해하기에 복잡하다는 것이 문제점이었다면 이대로 진행했을 테다.
하지만 서비스 특성상 메모를 수정하거나, 삭제하는 등의 동작 수행이 빈번하게 일어날 것인데,
그렇게 되면 해당하는 동작이 일어날 때마다 map 안에서 map을 하고, 또 안에서 map을 하는.. 눈물 없이는 볼 수 없는 상황을 내 손으로 연출시켜야 했다. 3중 반복문 안돼..멈춰..
결국
내가 이해하기 힘든 건 참을 수 있지만 성능 저하는 참을 수 없다.
의 마인드로 코드 공사 결정을 내리게 되었다.
지금 와서 뒤돌아보면, 코드 가독성과 성능 모두를 위해서라도 저 때의 결정은 필연적이었다.
⚒️API 설계
먼저, 백엔드 팀원들과 회의를 통해 API를 어떻게 분리하면 좋을지 의논하고,
해당하는 역할을 수행하는 API와 그에 따른 요청/응답을 설계했다.
아래는 그렇게 세분화된 우리의 API(태그, 메모 관련만)이다.
여기까지 오는 과정에서, 정말 많은 고민을 했다.
로직을 서버 측에 추가해 클라이언트 측 부담을 줄이는 대신 통신에 대한 소요를 감수할지,
클라이언트 측 부담을 늘리는 대신 통신의 부담을 최소화해 디바이스에 최적화된 앱을 만들지..
쉽게 정할 수 없는 🤔딜레마🤔였으나, 적절한 타협점을 찾으려고 노력하면서 개발을 이어갔다.
카카오톡은 도대체 어떻게 서비스를 운영하는지 정말 궁금했다. 외계인 납치해서 개발시키나?
다행히도, 앱을 빌드하고 나서 테스트해 보니 JSON 형식으로 데이터를 주고받는 통신 속도가 생각보다 잘 나와 주었고, 클라이언트와 서버(보다는 통신 과정) 양 측의 부담을 최소화시키는 방향으로 버벅임 없는 MVP를 만들어 내는 데 성공했다😁
✂️Reducer 분리
원래 하나의 배열으로 모든 데이터를 관리했기에, 클라이언트 측 상태관리도 하나의 큰 리듀서가 다였는데, API를 분리하며 자연스레 서브리듀서를 추가하여 상태를 관리하게 되었다. 태그 리듀서, 메모 리듀서, 인증 리듀서 이렇게 세 개의 서브리듀서를 binding하여 사용했다.
🏗️보수공사..
코드의 구조를 바꾸었기 때문에, 새로 만든 구조에 맞추어 기존에 짰던 코드에 대한 보수공사를 진행했다.
"이렇게 만들면 되겠다!" 하고 생각만 하는 것과, 실제로 만들어 보는 것은 상당히 다르다.
세상에는 내 생각이 미치지 않는 영역이 무궁무진하게 많고, 개발 쪽에서도 다르지 않다.
이 사실이 항상 나를 더 겸손하게 만드는 것 같다.
정말 많은 고민을 한 만큼, 많이 성장하게 된 것 같아 기쁘다😉
주니어도 아닌 뉴비 개발자가 쓰는 글인 만큼,
앱에서 데이터를 어떻게 관리해야 할 것인가? 라는 중요한 질문에 대해 섣불리 정답을 내놓기는 어렵다.
다만, API요청을 줄여 통신 부담을 줄이면서도 클라이언트 측의 부담도 최소한으로 하는 방향으로, 우리가 만드는 것의 효율적인 동작을 위해 끊임없이 고민하다 보면 질문의 해답에 한 발짝 더 다가갈 수 있지 않을까 싶다🤗
뻔한 답이라 실망했는가?
원래 수능공부는 교과서 위주로 집중해서, 공부할 땐 공부하고 놀 때 노는 사람이 제일 잘 한다.
긴 글 읽어 주셔서 감사하고, 좋은 하루 되길 바란다.😏
👍🏻👍🏻👍🏻