Final project - Dev8

Jaemin Jung·2021년 9월 13일
0

Final Project

목록 보기
8/27

MusicPlayer

Redux의 로직도 어느정도 정리 되었고,
오늘은 본격적으로 내가 맡은 음악 재생 기능을 Visualizer 페이지에서 구현 해봤다.

playList state redux 적용

우선 연습 차원에서 해당 페이지에서 필요한 state를 Redux에 적용 시켰다.

playList(객체를 요소로 갖는 배열)를 inittialState에 저장하였고,
reducer도 만들어서 root reducer에 등록 해줬다.
이후로 Visualizer 컴포넌트에서 useSelector를 사용해 state를 가져왔다.

const playList = useSelector(state => state.playListReducer);

받아온 playList 값을 현재 재생중인 곡을 나타내는 state인
crrentMusic이라는 state에 초기 값을 저장했다.
이는 useState로 저장하였다.

const [crrentMusic, setCrrentMusic] = useState(playList[0]);

Redux가 있는데 굳이 useState를 사용하는 이유는
어제 설명 들은것 처럼 전역 위치에서 여러 컴포넌트에 사용되는 state가 아니라,
오직 Visualizer 컴포넌트에서만 사용할 것 이기 때문에, useState를 사용했다.

state값 받아 렌더

제목, 아티스트 명, 가사, 앨범 커버등 crrentMusic에 저장된 값들을 렌더시켰다.
이 중 재생 목록은 모든 리스트가 나와야 하기에 playList 상태를 이용해
따로 컴포넌트로 작성하였고, map함수를 이용해 값을 각각 props로 전달했다.
그리고 해당 List를 클릭할시, crrentMusic이 변경되도록 구현했다.
state lifting을 이용해 set함수를 핸들러 함수로 감싸 props로 전달했다.

<div className='play-list-box'>
   <ul className='play-list'>
      {
        playList.map((el, idx) => {
        return <PlayList key={idx} num={idx} music={el} handleChangeMusic={handleChangeMusic} />;
        })
      }
   </ul>
</div>

이전곡 다음곡 버튼 기능 구현

react-h5-audio-player에는 showSkipControls라는 UI/UX Props가 존재한다.
이는 흔히 사용하는 이전곡 다음곡 버튼을 말하며, 버튼을 표시만 해줄뿐, 아무 기능은 없다.
대신 onClickPrevious, onClickNext라는 EventProps가 있다.

onClickPrevious, onClickNext 내부에 로직을 짜 보았다.

onClickNext

첫 번째로 onClickNext는 playList의 다음 곡을 재생 시켜줘야 한다.
crrentMusic의 값이 playList의 몇번째 인덱스 인지 추출 해내고, 그 값에 +1을 해준다.
playList에 현재곡 인덱스 +1 해준 값의 인덱스 값을 setCrrentMusic의 인자로 넣어주면 끝.

 onClickNext={() => {
 	setCrrentMusic(playList[playList.indexOf(crrentMusic) + 1]);
 }

인줄 알았지만, 끝난게 아니었다.
계속 다음곡 버튼을 누를시, playList배열이 가진 index 값을 초과해버린다.
이 때문에 에러가 발생하였다.

그래서 나는 유효성 검사 함수를 만들고, 이를 통과 하였을때와 통과 못하였을때를 나눴다.

function isValid (index) {
    if (!playList[index]) {
      return false;
    } else {
      return true;
    }
  }

우선 앞서 설명한 로직대로 계산된 인덱스를 유효성 검사 함수를 통해 체크한다.
playList의 길이를 벗어나면 false, 반대는 true를 리턴하도록 했다.
유효성 검사 결과에 따라 true면 이전 로직대로 진행하고,
false면 playList의 끝까지 곡을 들었다는 것이기에, 처음 곡을 재생 하도록 했다.

수정된 코드

 onClickNext={() => {
    if (isValid(playList.indexOf(crrentMusic) + 1)) {
   		setCrrentMusic(playList[playList.indexOf(crrentMusic) + 1]);
    }else {
        setCrrentMusic(playList[0]);
    }
 }

이는 onEnded에도 동일하게 적용되었다.

onClickPrevious

앞서 설명한 로직의 반대이다.
crrentMusic의 값이 playList의 몇번째 인덱스 인지 추출 해내고, 그 값에 -1을 해준다.
계산된 인덱스를 유효성 검사 함수를 통해 체크하고 true라면
playList에 현재곡 인덱스 -1 해준 값의 인덱스 값을 setCrrentMusic의 인자로 넣어줌,
false면 playList의 끝 곡을 재생 하도록 했다.

onClickPrevious={() => {
    if (isValid(playList.indexOf(crrentMusic) - 1)) {
    	setCrrentMusic(playList[playList.indexOf(crrentMusic) - 1]);
    } else {
        setCrrentMusic(playList[playList.length - 1]);
    }
}

랜덤 재생

react-h5-audio-player가 갖출건 다 갖췄다고 생각했지만, 가장 중요한 기능이 빠진거 같다.
그건 랜덤 재생기능 아마 대부분의 사람들이 스트리밍 서비스를 이용할때, 필수적으로 사용하는 기능인것 같다.

내가 못 찾은건지는 모르겠지만, 이에 대해서 UI/UX Props도 없고 Event Props도 없었다.
결국 랜덤 재생은 따로 버튼을 만들어 구현했다.

현재 crrentMusic은 playList의 인덱스 값이다.
그렇다면 setCrrentMusic에 랜덤한 값을 인자로 주면 해결된다.

Math.random()으로 랜덤한 number값 생성하기

Math.random은 0부터 1까지 숫자중에서 임의의 숫자를 리턴한다.
MDN의 최댓값을 포함하는 정수 난수 생성하기를 참고했다.

최댓값을 포함하는 정수 난수 생성하기

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min; //최댓값도 포함, 최솟값도 포함
}

참고하여 작성한 함수

function getRandomNumber (min, max) {
    return parseInt(Math.random() * ((Number(max) - Number(min)) + 1));
  }

랜덤 재생 On 상태에서는 다음곡, 이전곡은 위 함수로 인덱스 값을 얻어 set함수에 적용할것이다.

랜덤 재생 구현

우선은 랜덤 재생을 누른 상태인 isRandom state에 boolean값을 저장했다.
랜덤 재생 버튼을 누를때마다, 현재 boolean값의 반대를 set함수로 변경 하도록 했다.

onEnded, onClickPrevious, onClickNext에 모두 동일하게
isRandom이 true일때와 false일때를 구분하여 로직을 짰다.

isRandom이 true일때는 getRandomNumber함수에 최소 인덱스값, 최대 인덱스값을 넣어 랜덤한 숫자값을 얻고 이를 setCrrentMusic에 값으로 넣었다.

onClickNext={() => {
   if (!isRandom) {
     if (isValid(playList.indexOf(crrentMusic) + 1)) {
       setCrrentMusic(playList[playList.indexOf(crrentMusic) + 1]);
     } else {
       setCrrentMusic(playList[0]);
     }
   } else {
       setCrrentMusic(playList[getRandomNumber(0, playList.length - 1)]);
   }
}}

여기서 중요한것은 이전곡은 어떤 음악을 들었는지를 기억해야한다.
순차 재생일때에는 인덱스 -1씩 하면 됐는데,
랜덤 재생일때에는 순차적이지 않다보니 이전 음악을 저장할 공간이 필요하다.
계획은 배열을 이용해서 stack 알고리즘으로 구현할 생각이다.

발견한 에러

한참 코딩을 하다가 렌더 화면을 보기위해 npm run start를 하였다.
그런데 다음과 같은 에러가 발생했다.

Too many re-renders. React limits the number of renders to prevent an infinite loop.

리렌더링이 너무 많으며, React는 무한 루프를 방지하기 위해 렌더 수를 제한한다고 한다.

이는 랜덤 재생 버튼에 onClick 이벤트에 함수를 곧바로 실행해서 발생한 에러였다.

<button onClick={setIsRandom(!isRandom)}>
   {isRandom?'현재 랜덤재생 ON':'현재 랜덤재생 OFF'}
</button>

props로 전달 해주는 함수는 Callback으로 감싸 주는 것이 성능 최적화에 도움된다고 한다.
다음과 같이 set함수를 함수 내부에서 실행 하도록 하였더니 에러가 사라졌다.

<button onClick={() => { setIsRandom(!isRandom); }}>
  {isRandom?'현재 랜덤재생 ON':'현재 랜덤재생 OFF'}
</button>

때마침 나 말고 다른 팀원이 같은 에러로 삽질중이었다.
바로 배운것을 써먹어줬다ㅎㅎ


오늘은 여기까지..

작성한 코드를 보면 꽤 간단한 로직인데 생각해내고 구현하는데 꽤 시간이 많이 걸렸다.
아무래도 2주 프로젝트때 거의 백엔드를 맡았어서 프론트엔드적 감각이 많이 죽었다.
알고있었는데 잊어버린 메소드도 많았고, state를 다루는 센스도 많이 떨어졌다.
redux도 아직 완전히 내것이 된게 아니라서 프로젝트중에도 짬내서 공부할 시간을 마련해야겠다.

참고사이트

https://juhi.tistory.com/23
https://www.npmjs.com/package/react-h5-audio-player
https://velog.io/@ppby/Error
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Math/random#%EB%91%90_%EA%B0%92_%EC%82%AC%EC%9D%B4%EC%9D%98_%EB%82%9C%EC%88%98_%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0
https://dasima.xyz/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-math-random-%EB%B2%94%EC%9C%84-%EC%9E%90%EB%A6%BF%EC%88%98/
https://velog.io/@mnmm/js-parseint-mathfloor

profile
내가 보려고 쓰는 블로그

0개의 댓글