brunch

You can make anything
by writing

C.S.Lewis

by swimjiy Sep 21. 2022

함께 만드는 음악 플레이리스트 'Muzily' 개발기

주니어 프론트엔드 개발자가 사이드 프로젝트에서 새롭게 경험한 5가지

변하지 않고 안락한 환경에 오래 머물다 보면 나도 모르게 상황에 안주하게 되곤 합니다. 그도 그럴게 지금의 상황에 만족하는데 굳이 바뀔 필요가 없으니까요. 저 또한 예외는 아니라고 생각합니다.

그래서 의도적으로 새로운 상황에 던져지고자 노력하는 편입니다. 물론 초반에는 낯설고 괜히 시작했나 하는 생각도 들지만, 그래도 마무리는 늘 뭔가 하나쯤은 새롭게 얻어 갔기 때문이죠.

Muzily도 이러한 던져짐으로 시작한 프로젝트 중 하나입니다. 약 4개월 간의 길다면 길고 짧다면 짧은 기간 동안 만든 서비스는 무엇이며 기억에 남는 경험은 무엇인지, 이번 포스팅에서 하나씩 이야기하고자 합니다.




모두의 플레이리스트, Muzily

서비스에 대한 소개부터 하자면 뮤즐리(Muzily)는 카페, 여행, 동아리 등 함께 음악을 듣는 환경에서 모두가 원하는 노래를 들을 수 있는 플레이리스트 공유 서비스입니다.

http://muzily.app/

방을 생성하면 방장의 권한을 얻게 되며 곡 재생, 순서 변경 등 플레이리스트를 제어할 수 있습니다. 또한 함께 참여하길 원하는 사람들에게 공유 링크를 전달할 수 있으며, 링크로 접속한 유저들은 별다른 로그인 절차 없이 쉽게 방에 참여할 수 있습니다.

마지막으로 참여한 유저들이 자신이 듣고 싶은 노래의 유튜브/유튜브 뮤직 링크를 전달하면 방장이 이를 확인해 승인 혹은 거절하며 함께 플레이리스트를 꾸릴 수 있습니다.

* 사용 중인 유튜브 API가 모바일 환경에서는 자동 재생을 지원하지 않는 관계로 방장은 PC 환경에서 사용하시는 것을 추천드립니다.

서비스 화면 일부


시작은 단순하게

뮤즐리는 매시업(Mash-Up)이라는 IT 연합 동아리에서 진행한 프로젝트입니다.  기수에 디자이너, 개발자로 구성된 팀에 참여해 프로젝트를 진행하는데요. 저희 팀은  멋진 디자인을 준비해  디자이너 2, 든든한 서버 개발자 3, 그리고 제가 속한 프론트엔드 개발자 4명으로 이루어졌습니다. 기수 별로 활동 기간이 약간씩 차이가 있었는데, 이번 12기는 5월부터 9월까지  4개월 동안 기획부터 배포까지 진행했습니다.

초기에 다양한 아이디어가 나왔는데 그중 음악이라는 대중적인 분야이고, 또 팀원들이 실제로 사용할 법한 서비스를 만들어보자는 의견이 있어 지금의 아이디어를 선택하게 되었습니다.

동아리 활동이 마무리되어가는 지금 돌이켜보면 인상 깊은 경험이 많았다는 생각이 드는데요. 이제부터 하나씩 풀어보도록 하겠습니다.




UT로 사용성 개선하기

사용성 테스트(UT, Usability Test)는 개발을 제외한 프로젝트 전반에 있어 새로웠던 경험 2가지 중 하나입니다. 사용자가 서비스를 올바르게 인식하고 사용할 수 있는지에 관한 테스트로, 누구든지 어려움 없이 서비스의 기능을 이용할 수 있는지를 파악하는 것이 주된 목적입니다.

저희 팀에서는 핵심 기능 개발이 완료된 직후에 사용성 테스트를 진행했습니다. 테스트 자체는 디자인팀에서 주도했는데, 저는 어떤 식으로 테스트가 진행되는지 궁금해 중간에 게스트 형태로 참여했습니다.

사전에 모집한 테스터가 준비한 시나리오에 맞춰 기능을 실행하는 과정을 보며 많은 것을 느낄 수 있었는데요. 특히 이 기능이 왜 이 위치에 있는지 이해하지 못하는 상황들을 보며 내가 당연하다고 느낀 흐름이 실제 사용자에겐 당연하지 않다는 명제가 크게 다가왔습니다.

테스트는 디스코드로 진행했습니다.


사용성 테스트 결과 아래와 같이 많은 개선점이 생겼는데요. 반영하는 일이 고되긴 했지만 그만큼 이전 버전과 비교했을 때 좋은 방향으로 많이 바뀐 것 같아 재미있는 경험이었습니다.




단단한 서비스를 위한 QA

서비스의 완성도를 높이고자 프로젝트 마무리 약 한 달 전부터 따로 QA(Quality Assurance) 기간을 정해 진행했습니다.

보통 사이드 프로젝트는 규모가 작다 보니 QA를 짧게 실시간으로 하거나 버그를 제보하는 공간이 디자인, 개발 등 역할 별로 산재되어 있는 경우가 많았는데, 이번에는 노션의 보드 기능을 이용해 QA 목록을 한 데 모아 관리했습니다. 나름 동아리를 세 기수 동안 참여했었는데 이렇게 체계적으로 QA를 진행해 본 건 처음이라 새로운 경험이었습니다.

노션 보드로 관리했던 QA 목록

해결하면 생기고, 다시 해결해 놓으면 또 생기고... 데자뷔를 느낄 때가 종종 있었는데요. 앞서 사용성 테스트에서 느낀 것처럼 QA가 있었기에 더 단단한 서비스가 만들어진 것 같습니다. 개발은 누구나 할 수 있지만 좋은 서비스를 개발하는 일은 아무나 할 수 없음을 다시 한번 느낄 수 있었던 시간이었습니다.




팀 공통으로 진행한 부분 이외에도 실제 프론트엔드 개발을 하며 새롭게 경험한 것도 많았는데요. 그중 인상 깊었던 세 가지를 공유하고자 합니다.

그럼 이제부터 한 가지씩 살펴볼까요?

이해를 돕기 위해 프로젝트의 기본 환경을 간략히 설명하자면 TypeScript와 React를 기반으로 프로젝트를 구성했으며, Next.js를 사용해 서버 사이드 렌더링(SSR, Server Side Rendering) 환경을 구현했습니다.

보다 구체적인 내용은 프로젝트 깃허브에서 확인하실 수 있습니다.


상태 관리 구조에 대한 고민

먼저 이번 프로젝트에서는 개인적으로 상태 관리 구조를 설계하는데 고민을 많이 했습니다.

상태 관리 라이브러리는 러닝 커브가 낮고 훅 기반으로 되어 있어 사용이 편리한 Recoil을 기반으로, 서버 상태와 클라이언트 상태를 분리하기 위해 React Query를 함께 사용했는데요. 두 개를 함께 선택한 이유는 Recoil은 뒤에 설명할 웹 소켓의 데이터를 유연하게 담는 용도로, React Query는 웹 소켓을 제외한 일반 HTTP 데이터를 캐싱하고 관리하는 목적으로 사용하기 위해서였습니다. 가령 신청곡 목록은 실시간으로 받아와야 하니 웹 소켓, 유저 정보는 요청-응답 형태로 받아와도 무방하니 HTTP로 받는 식으로 말이죠.

각각 따로 써본 경험은 있었지만 이 둘을 함께 사용해 본 것은 처음이었기에 다양한 레퍼런스를 찾아봤고, 그 결과 디렉터리 구조부터 쉽게 호출하기 위한 커스텀 훅 추상화까지 한 땀 한 땀 구현했습니다.

마지막으로 팀원들의 꼼꼼한 코드 리뷰를 끝으로 지금의 구조를 설계할 수 있었습니다.




내겐 너무 낯설었던 웹 소켓

이번 프로젝트에서 가장 신선했던 기술을 꼽자면 주저 없이 웹 소켓을 선택할 것 같습니다. 그만큼 처음 경험해봤고 시행착오도 많았던 기술이었기 때문입니다. 곡 신청이나 이모지 보내기 등, 서비스의 특성상 방에 참여 중인 사용자 모두의 데이터가 변경되어야 하는 상황이 많았기에 중요도가 높은 기능이기도 했고요.

프로젝트에서는 기본적으로는 웹 소켓의 서브 프로토콜인 STOMP를 기반으로 통신했고, 이를 클라이언트에서 사용할 수 있게 돕는 라이브러리인 STOMP.js와 웹 소켓을 사용하지 못하는 경우를 위한 polyfill인 SockJS를 사용했습니다.

이번 프로젝트 전까지 웹 소켓은 양방향 통신이라는 이름만 들어봤지 실제로 어떻게 동작하는지는 몰랐었는데요. 계속 연결을 열어둔 상태로 유지하고, 또 포트처럼 정해진 엔드 포인트에서 실시간으로 데이터를 수신하는 점이 확실히 HTTP 통신과는 달라 신기했습니다.

웹 소켓을 이해한 것과 더불어 이를 프로젝트에서 어떻게 사용할지에 대한 고민도 많았습니다. HTTP 통신과 소켓 통신을 어떻게 저희 서비스 구조에 맞게끔 함께 가져갈 수 있는지에 대한 감이 잘 잡히지 않았거든요. 그래서 이 부분 또한 많은 레퍼런스를 찾아봤고, 그 결과 React의 Context API를 활용해 하나의 방에 대한 소켓 통신을 총괄하는 Provider를 생성한 뒤에 안에서 변경되는 정보는 앞서 만들었던 Recoil에서 관리하는 구조로 설계했습니다.

앞서 설명한 Context API와 Recoil, 그리고 React Query까지 전반적인 상태 구조를 정리하자면 아래 그림과 같습니다.

이렇게 구조를 설계한 결과 HTTP 통신과 소켓 통신을 적절히 나누어 관리할 수 있게 되었고, 실제 개발 단계에서도 각 기능의 성격에 맞는 커스텀 훅을 호출해 편리하게 사용할 수 있었습니다.


마지막으로 앞서 설명한 Recoil, React Query과 웹 소켓에 대한 사용 가이드를 깃허브 위키 문서에 정리해서 처음 구조를 접하는 팀원들도 쉽게 이해할 수 있도록 내용을 공유했습니다. 스스로 한 번 더 개념을 복기하는 유용한 시간도 되었고요.

이처럼 프로젝트에서 통신과 상태 관리에 대한 개발 환경을 설계하며 단순 구현이 아닌 거시적인 관점에서 코드를 바라볼 수 있었고, 더불어 좋은 개발자 경험(DX, Developer Experience)을 제공하는 방법에 대해 고민해 본 계기가 되었습니다.




Next.js 잘 사용하기

예전에 작업했던 서비스인 카트 세이버에서도 오픈 그래프(OG, Open Graph) 태그를 생성해 카카오 미리보기나 검색엔진 최적화(SEO)를 진행한 경험이 있었는데요. 이번에는 태그에 담을 정보를 서버 API에서 불러와 동적으로 보여주어야 하는 상황이 생겼습니다.

그 이유는 방 링크를 공유할 때 방 이름이 미리보기에 보여야 하기 때문이었는데요. 아래 붉은 영역으로 표시된 부분이 방 이름으로, API로 받아와야 하는 값이었습니다.

그냥 원래 API를 호출하던 대로 하면 되지 않을까 생각했는데 생각보다 잘 되지 않았고, 팀원이 얘기한 힌트를 토대로 원인을 찾아본 결과 Next.js의 특징을 이해해야 해결할 수 있는 문제였습니다.

Next.js는 표준 서버 사이드 렌더링과 달리 아예 처음부터 완성된 HTML DOM을 제공하는 것이 아니라 자바스크립트 코드는 포함하지 않는 상태로 렌더링해 전달합니다. 이를 Pre-Rendering이라고 하죠. 이렇게 서버가 정적으로 생성된 HTML과 빌드된 자바스크립트 파일을 각각 클라이언트에 전달하면, 이후에 자바스크립트가 DOM 요소 위에서 한 번 더 렌더링을 거치며 비로소 온전한 화면을 볼 수 있는 거지요. 이 과정을 Hydrate라고 부릅니다.

이 DOM 렌더링 과정과 앞서 보았던 방 이름의 상태를 매칭 시켜본다면 아래 그림과 같습니다.

실패했던 코드의 경우 아직 방 정보 API를 호출하지 않은 상태에서 정적으로 생성된 HTML 값을 보여주다 보니 원하는 대로 방 이름을 보여줄 수 없었던 거죠.

이를 해결하기 위해 Next.js에서 제공하는 getServerSideProps를 사용했습니다. 이 함수 안에 작성된 코드는 앞서 설명한 Pre-Rendering 단계에서 동작을 하기 때문에, 아래와 같이 함수 안에서 방 정보 API를 호출하면 미리 보기 단계에서 성공적으로 방 정보를 불러올 수 있었습니다.

그 결과 원활하게 동적으로 방 제목을 카카오톡 미리보기에 추가할 수 있었고, Next.js의 렌더링 과정도 조금 더 깊이 이해할 수 있었습니다.




그래서 결론은

4개월 동안 팀원 각자가 맡은 역할에 최선을 다한 것과 더불어, 매주 팀 전체 회의와 플랫폼 회의를 진행하는 등 밀도 높은 협업을 통해 동아리 활동 기간 안에 무사히 1.0.0 버전을 론칭할 수 있었습니다.

더불어 서비스를 좋게 봐주신 분들 덕분에 프로젝트 발표에서 최종 1위를 수상할 수 있었습니다.

상금 사용 계획은 아직 미정입니다.


갈길이 구만리

앞에서는 많은 일을 해낸 것처럼 설명했는데, 사실 지금까지 개발한 내용은 저희가 기획 단계에서 정한 기능 중 최소한인 MVP입니다. 그 말은 즉 앞으로 구현할 기능이 한참 남았다는 거죠. 따라서 채팅 기능을 포함한 추가 기능 구현과 더불어 아직 해결하지 못한 자잘한 버그도 개선할 예정입니다.

또 프로젝트를 시작하기 전에 가장 해보고 싶었던 게 테스팅이었는데 기본 골자만 짜 놓고 실제로 사용해 보지는 못한 점도 아쉬웠기에 이 또한 앞으로 차차 구현하고 싶은 욕심이 있습니다.




좋은 사람들과 함께 경험하며 성장할 수 있다는 점이 사이드 프로젝트의 큰 매력이라고 생각합니다. 저 또한 그 점이 좋아서 빠듯한 일정 속에서도 꾸준히 활동을 지속할 수 있었던 것 같고요.

애착이 많은 서비스인 만큼 앞으로 뮤즐리가 무럭무럭 잘 크길 바라며 이번 포스팅을 마칩니다.


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari