원신 뮤직 플레이어!

Lenny·2022년 1월 6일
1
post-thumbnail

만든 이유?

나는 애니메이션, 게임, 영화를 컨셉으로 웹을 꾸미거나, 웹 앱을 만드는걸 좋아하는데, 원신 캐릭터들이 귀염뽀짝하면서 포스가 있기도하고, 미호요가 음악을 잘뽑기도하고.. 어쨌든 리액트를 공부했으니, 뮤직플레이어를 한번 그동안 공부한걸 이용해서 만들고싶어서 시작한 간단한 토이프로젝트이다!

완성품

무엇을 사용해서 만들었나?

Firebase(음악파일, 이미지 저장용도), localforage, React 이 세가지를 메인으로 사용하고, 나머지 자잘한 기능들은 다른 라이브러리의 도움을 받았다.

폴더 내부가 아닌 파이어베이스에 음악이나 이미지를 저장해놓고, blob 을 url형태로 받아와서 그 url주소를 localforage에 저장하여 localforage 안에 있는 데이터를 이용한다.

구조

Routes는 두 개이다.
MusicList 컴포넌트는 완성품 이미지처럼, 음악 목록을 보여주는 Route이고
MusicPlay 컴포넌트는 음악을 재생하는 Route이다.

common 폴더에 있는 컴포넌트들은, 부품처럼 사용할 수 있는 컴포넌트들의 모음이다. (버튼같은)

Characters 폴더에 있는 컴포넌트들은, 캐릭터들의 정보를 담고있는 컴포넌트이다.
이 컴포넌트들이 MusicList에 깔려있는 구조이다.

각 캐릭터 박스 컴포넌트들을 리스트에 이런식으로 배치해놓은 구조이다.

그리고 재생버튼을 누르면, 그 캐릭터 테마 음악, 이미지들을 로딩 후 MusicPlay 컴포넌트에서 뿌려준다!

프로젝트를 진행하면서 고생했던 부분

리스트화면에서 음악 재생을 누르면 /play로 이동하는데, 데이터를 로딩시키는 부분에서 상당히 애를 먹었었다.

음악이나, 이미지 파일들을 가져오는데 시간이 걸리고, localforage에 저장하는데에도 시간이 걸리고, 불러오는데에도 시간이 걸린다. 그런데 Link를 누르면 이 데이터를 가져오는 시간을 기다려주지 않는다. 바로 이동한다. 이 부분을 어떻게 고쳐야할까 고민하는 시간이 되게 길었던 것 같다.

그래서 처음에는 setTimeout 함수를 이용해서, 내가 임의적으로 로딩시간을 만들어줬었다.
하지만 캐릭터마다 파일크기가 달라서 로딩시간이 제각기 다르다. 그래서 이 방법을 사용했을때 오류는 나지 않지만 내가 원하는데로 동작하지 않았다.

어떻게 해결했는지

처음에 내가 생각한 로직은 캐릭터박스 컴포넌트에서 음악에 대한 정보를 localforage에 set하고, /play 라우트에서 get하는 방식이었다.

근데 이게 상당히 애매한 방식이었다. 왜냐면, set 하고 get 해야하는데 이렇게 해버리면 따로논다.

최초 1회는 괜찮은데, 그 이후에 따로논다.
내가 재생할 음악을 고르고 그 음악에 맞는 이미지나 음악을 렌더링해야하는데, set하는 시간과 get 하는 시간이 동시에 진행되서 가끔 get 하는 시간이 더 빠르면, 그 전에 있던 음악정보를 화면에 뿌린다던지..? 그런 사태가 발생한다.
그래서 이 부분을 해결하기위해서 set 이후에 get 하도록 코드를 짜서, 데이터를 저장하고, 불러오는 작업이 따로놀지 않게끔 해결했다! 근데 문제는 이 작업을 캐릭터 컴포넌트에서 한다는거.. 그러면 이 데이터를 플레이 페이지에 어떻게 뿌릴거냐..? 이게 문제였다. state는 각 컴포넌트 내부에서만 유효하다.
그리고 페이지 이동은 Promise를 기다려주지 않는다. 그러면 캐릭터 컴포넌트에서 Promise 를 모두 수행하고 그 데이터들을 공유하게끔 만들어줘야한다! 이 부분을 엄청 고민한 끝에 생각한 방법이 Context API 를 사용하는것이었다. state를 Context로 공유하면? 캐릭터 컴포넌트에서 작업이 끝나고 나온 데이터들을 해당 state에 넣고, 이 state는 Context로 공유되고 있으니깐 플레이 페이지에서도 이용할 수 있는것이다! 로딩을 구현하는건 쉬웠다. 로딩에 관여하는 state들도 Context를 이용하니깐 금방 구현 할 수 있었다.

근데 여기서 또 문제가 발생했다.

이건 되게 최근에 해결한 문제인데 플레이페이지에서 유저가 새로고침을 하면 무한로딩이 되는 현상이 발생한다. 왜냐? 플레이페이지에서 새로고침을 하면 state들이 모두 초기화된다. 그래서 loading 관련 state들도 초깃값으로 되돌아가서 무한 로딩 현상이 발생하는것이다.

또 현재 재생중인 음악 데이터도 불러오지않는다.
내가 원하는 기능 구현들을 거의 다 했는데 이부분에서 막히니까 어질어질했다.

여기에서 완전히 막혀있는 상태였는데, 어느날 갑자기 아이디어가 떠올라서 되게 간단하게 풀린 문제였다.
새로고침을 해도 state만 초기화되지, localforage에 저장되있는 데이터들은 그대로 유지된다.

즉, localforage에 데이터가있다면? 컴포넌트가 마운트 될 때 state에 다시 그 데이터를 넣어주는 함수를 구현하면 된다!
또 친절하게 localforage는 이 문제를 해결하게끔 해주는 적합한 메서드도 있었다.
keys라는 메서드인데, 현재 localforage 저장된 데이터들의 리스트들을 반환하는 메서드였다.
내가 이용하는 저장소는 3개 인데, 컴포넌트가 마운트 됬을 때 localforage의 길이가 3이라면 그 저장소 안에 있는 데이터를 불러오는 로직을 짜면 되는것이다!

내가 이 토이프로젝트를 하면서 특별히 다루기 어려웠던 점을 적어봤다.
그리고 이 문제들을 해결하면서 Link를 눌렀을때 데이터를 넘겨줘야하는 상황이 생겼을 때, 어떤 식으로 전달하면 될까 하는 방법을 한 가지 배운것같다!

개선할 사항

기능 구현에 급급해서 코드를 작성해서, 코드가 너무 지저분하다. state들도 객체로 만든게 아니라 하나하나씩 따로따로 만들어놔서 코드가 상당히 복잡하다. 그래서 리팩토링을 진행해서 코드를 좀 가꿔줄 필요가 있다.
최적화 문제도 생각해야 할 것 같은데, 이 문제도 공부하면서 천천히 적용시켜봐야 할 것 같고 또 Context 말고 다른 방법이 있는지도 고민해봐야 할 것 같다. 왜냐면 Context에 넣는 값이 너무많다..

마무리

지금 적어놓은 내가 생각한 개선사항 말고도 여러부분이 있을것같다.
그래도 이번 프로젝트를 하면서 공부가 된 부분도 많은것같다.
그리고 다음엔 React-native로 이 프로젝트를 옮기는 작업도 진행해보고싶다.
React-native도 자바스크립트로 뮤직플레이어 커스텀이 가능할까..? 이 부분이 마음에 걸린다. 아마 로직짜는건 리액트보다 더 간단할 수도 있을것같다!

깃허브 : https://github.com/pjj186/genshinplayer

profile
🧑‍💻

0개의 댓글