Mini프로젝트_오디오재생프로그램_플레이어 만들기 (react-h5-audio-player 라이브러리 사용)

이고운·2022년 10월 24일
0

기업협업

목록 보기
3/4

기업협업 3번째 과제는 오디오재생 프로그램을 제작하는 것이었다.
이번 프로젝트는 팀이 변경되어 인원이랑 구성원이 달라졌다.
총 3명이 진행하게 되었으며, 사람이 줄어들어서 1인당 작업해야하는 부분은 늘었지만 파트분배는 좀 더 수월해졌다.
과제가 오디오재생 /녹음 재생 이렇게 2개의 파트로 나뉘어 있었는데, 나는 오디오 재생쪽을 맡게 되었다.

1. 프로젝트 개요

1) 프로젝트 기간

2022.10.11 ~ 2022.10.14

2) 역할 및 기능 구현

  • 이고운 : 오디오플레이 리스트 및 사운드 바 구현
  • 구현 : 오디오 파형 및 재생파일 다운로드 구현, 반응형 구현
  • 유상호 : 음성 녹음 및 녹음 파일 다운로드

3) 협업 툴

4) 코드 컨벤션

  • css : Styled-Components

  • 클래스명: 케밥 케이스

  • 통신 : axios / async await

  • 파일 확장자는 모두 jsx로 통일

  • Github-flow (feature → main)

  • Commit Messages (ex Add: ) 주기적인 이슈 등록
    Document - 문서 작업
    UI - 레이아웃 추가 (UI)
    Add - 기능 추가 (비즈니스 로직)
    Fix - 버그 수정
    Modify - 코드 수정
    Remove - 코드 삭제
    Refactor - 코드 리팩토링
    Style - 스타일 (비즈니스 로직을 건드리지 않고 코드 형식 수정)
    Structure - 프로젝트 구조 변경 (폴더, 파일 이동 및 삭제)
    Test - 테스트 작업 (Mock 데이터 추가, 테스트 코드 작성)
    예시) 메인 브랜치로 머지할 때만 (그 외는 Feature: 회원가입 기능 추가 형태로 간단하게)

  • Merge 방법 : Squash and Merge

  • 컴포넌트 생성 : 하위 컴포넌트 폴더 생성 후 그 안에 index.jsx로 통일

2. 내가 구현한 기능 상세 설명 및 문제 발생

이번에 초기 세팅하면서 컴포넌트를 많이 분리해서 컴포넌트에 props 넘겨주는 것이 좀 어려웠다. 그런데 플레이어같은 것은 오디오랑 녹음에도 사용해야하기 때문에 재사용성을 생각하면 컴포넌트를 분리하는게 맞다고 생각한다.
컴포넌트는 아래와 같이 이루어져 있다.


리스트, 플레이, 다운로드 부분이 오디오랑 녹음에 공통으로 쓰인다. 추가로 파형도 마찬가지이다. 오디오 부분이 내 담당이긴 했지만 공통으로 쓰이기 때문에 이번 프로젝트는 과제 특성상 오프라인에서 모여서 같이 작업했다. 아무래도 props는 혼자 쓴다고 되는 것이 아니기 때문에 오프라인에서 즉각적으로 대응해가며 작업했던 방식이 도움이 많이 되었던 것 같다

1) Main에서 trackList 데이터 가져오기

tracklist는 오디오랑 녹음메뉴 둘 다 들어가기 때문에 메인에 가져왔다.
먼저 경로에 따라서 trackList JSON파일은 다르게 가져왔다. (딱히 이유는 없음. 녹음은 리스트를 밑에 계속 업데이트해야해서 2개만 json파일로 만듬)


const Main = () => {
  const location = useLocation();
  const [trackList, setTrackList] = useState([]);//오디오리스트
  const [trackNumber, setTrackNumber] = useState(0);//오디오트랙넘버(트랙 변경할때 사용)
  const [track, setTrack] = useState('');//오디오 트랙 (트랙정보)
  const [isRecord, setIsRecord] = useState(false);//녹음
  const [err, setErr] = useState(false);//에러
  
  useEffect(() => {
    const navTitle = location.pathname;
    let url = '/Data/Audio/';
    if (navTitle === '/') {
      fetchData(url + 'playList.json');
      setIsRecord(false);
    } else if (navTitle === '/record') {
      fetchData(url + 'audioRecord.json');
      setIsRecord(true);
    } else {
      setErr(true);
    }
  }, [location]);

location.pathname으로 기본 URL 설정해준다음, url에 따라 다른 json파일을 가져왔다.

 const fetchData = url => {
    (async () => {
      try {
        const {
          data: { lists },
        } = await axios({
          url,
          method: 'GET',
          headers: {
            'Access-Control-Allow-Origin': '*',
          },
        });
        setTrackList(lists);
      } catch (error) {
        console.log(error);
      }
    })();
  };

json파일은 axios으로 통신하여 가져왔다. (Access-Control-Allow-Origin 항목은 크롬 CORS 에러 때문에 넣었는데 이걸 넣어서 해결하지는 못함.)
json데이터는 trackList에 저장해주고 에러시에는 콘솔에 에러가 찍히도록 해줬다.

useEffect(() => {
    trackList.map(trackInfo => {
      if (trackInfo.id === Number(trackNumber)) {
        setTrack(trackInfo);
      }
    });
  }, [trackNumber]);

그리고 trackList를 map돌려서 id를 매칭시키게 했다.
id가 trackNumber랑 같으면 트랙정보를 track에 담아줬는데, 이건 리스트를 클릭했을 때 해당 트랙 오디오를 플레이시키기 위해 저장해준 값이다.

return err ? (
    <div>에러</div>
  ) : (
    <PlayScreenWrapper>
      <div className='audio-list-content'>
        <AudioList trackList={trackList} setTrackNumber={setTrackNumber} />
      </div>
      <div className='audio-detail-content'>
        <AudioPlay track={track} setTrackNumber={setTrackNumber} />
        {isRecord && <AudioRecord />}
      </div>
    </PlayScreenWrapper>
  );
};

AudioList 컴포넌트에는 trackList, setTrackNumber을 props로 넘겨주었고
AudioPlay 컴포넌트에는 track, setTrackNumber을 넘겨주었다.

2.AudioList 컴포넌트에서 리스트 출력하기(클릭시 해당 곡 재생)

const AudioList = ({ trackList, setTrackNumber }) => {
  
  return (
    <StyledContainer>
      <h1>- AUDIO LIST -</h1>
      <ul>
        {trackList.map(item => (
          <li
            key={item.title}
            id={item.id}
            onClick={e => {
              setTrackNumber(e.target.id);
            }}
          >
              {item.title}

trackList를 목록으로 만들기 위해 map을 사용했고 클릭 시
trackNumber에 id에 저장되도록 했다. 여기서 저장된 id가 메인 컴포넌트에서 trackInfo.id랑 일치하면 track에 곡 정보가 담기는 것이다.
여기서 담겨야 오디오 플레이에서 해당 곡이 플레이된다.

3) Audio Play에서 곡 정보 가져와서 플레이하기

일단 오디오 플레이어는 react-h5-audio-player 라이브러리를 사용했다. 이번에는 라이브러리 css 수정을 위해서 import 'react-h5-audio-player/lib/styles.css';css 파일도 임포트해서 사용했다.

const AudioPlay = ({ track, setTrackNumber }) => {
  const playBarRef = useRef(null);
  
  const hadleClickPre = () => {
    setTrackNumber(prev => Number(prev) - 1);
  };

  const handleClickNext = () => {
    setTrackNumber(prev => Number(prev) + 1);
  };
  
  return (
    <AudioPlayWrapper>
      <PlayBaraWrapper>
        <AudioPlayer ref={playBarRef} header={track.title} src={track.src} autoPlay={false} onPlay={onPlay} onClickPrevious={hadleClickPre} onClickNext={handleClickNext} />
      </PlayBaraWrapper>
    </AudioPlayWrapper>
  );
  
//다운로드 및 파형 관련된 부분 코드는 삭제함

여기서 기본 라이브러리에서 더해진 것은 다음곡, 이전곡 넘기기 기능이다.
이 기능 때문에 Main컴포넌트에서 setTrackNumber props를 넘겨온 것이다. prev임시값에 trackNumber를 한개씩 줄이고 늘릴 수 있게 값을 저장해줬다.

4) ❌ 문제 발생 ❌

내가 시간을 많이 소비한 부분이 몇 개가 있는데 그 중에 제일 시간을 많이 잡아먹은 것이 파형이 안나올 때였다.
웃긴 건 파형 담당자가 제대로 코드를 넣어서 본인 피씨에서는 파형이 잘 출력되었는데 나랑 다른 팀원 피씨에서는 파형이 나오지 않았다.
콘솔을 보니 CORS오류인 것 같아서 이것을 해결하려고 구글링하여 여러가지 코드를 추가적으로 넣었지만 결과적으로는 크롬에서 옵션을 아예 꺼서 해결했다.
당장 배포해서 프로젝트 마무리를 지어야하는데 코드 자체의 문제가 아니라서 어쩔 수 없었다..
터미널에 아래 코드 작성하면 새 크롬창이 뜨는데, 크롬에서 제공하는 보안기능을 끈 창인 것 같았다. 아주 간단하게 파형이 나왔다.😃

open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security

참고 사이트 : https://asecurity.dev/entry/Mac-Chrome-CORS-Disable-%EC%8B%A4%ED%96%89

더 검색보니 크롬 확장프로그램을 이용할 수도 있다고 하더라. (크롬웹스토어 CORS 프로그램)

5) 결과화면

3. 느낀점

사실 이번에는 props로 넘길 것도 많았고 컴포넌트 분리를 이전에 프로젝트랑 다른방식으로 처리해서 여기서 쓰는값이 어떤 값이고 어떤 걸 넘겨야하는 지
확인하는데 어려움이 많았다. 공통으로 쓰는 데이터들이 많아서 props 데이터가 제대로 넘어가야하는데 처음에는 6개씩도 넘겼었다. 컴포넌트를 정리하면서 정말 필요한 props들만 넘기는 것으로 수정했는데, 이 부분은 컴포넌트 분리를 맡아주신 다른 팀원 분의 도움이 컸다.
그리고 이번에 컴포넌트를 기존에 형식이랑 좀 다르게 했는데, 원래는
pages폴더에 Main.js, components폴더에 AudioPlay.js, AudioList.js 이런식으로 작업했다면 이번에는 components폴더 안에 AudioPlay폴더 그 안에 index.js파일을 생성하여 작업했다.
이렇게 하니 처음에는 어떤 게 어떤 파일인 지 너무 헷갈렸다..
다른 팀원분이 말씀하시길 이 방식이 현업에서 많이 사용하는 방식이라고 하셔서 사용했는데, 다른 동기들도 이 방식으로 많이 사용하는 것 같다.
내 생각에는 컴포넌트를 효율적으로 사용하려면 아래와 같이 컴포넌트 폴더 안에
js파일을 넣고 아래와 같이 import해 사용하는 게 낫지 않나 싶지만, 잘모르겠다.

import {A,B,C} from '../../component

과제 이후에 컴포넌트 분리에 대해서 좀 찾아봤는데, 사실 무턱대로 컴포넌트를 분리하는 것보다 일단 한 파일에서 작업하고 재사용성을 고려해서 컴포넌트를 나누는 것이 좋다는 의견도 많았다. 즉 컴포넌트부터 나눠서 작업하는 것이 아니라 일단 코드를 작성하고 그 코드에 맞춰서 컴포넌트를 분리하는 것이다.
내 생각에 둘 다 장단점이 있는 것 같은데, 일단 컴포넌트를 미리 나눌 때
미리 구조를 생각하고 작업하니까 체계적으로 기능을 나눌 수 있는 것 같다. 그런데 만약 수정해야할 사항이 있느면 다른 컴포넌트에서도 사용 중인 props로 넘긴 값을 다 수정해야한다는 단점이 있다.
또, 컴포넌트를 나중에 나눌 때는 일단 코드 먼저 쭉 작성하니까 수정할 때 간편하다, 다만 기능을 구분하거나 이렇게 팀 작업할 때는 코드가 꼬일 수 있어서 작업이 원활하게 진행되지 않을 수 있을 것 같다.
결과적으로 보면 팀 작업은 여러사람이 작업하다보니 꼬이지 않게 하기 위해서라도 컴포넌트는 분리하는 게 맞는 것 같다. 다만 내가 이번에 사용한 방식에 대한 장점은 좀 더 찾아봐야할 것 같다.😂

profile
자 이제 시작이야~ 내 꿈을~ 내 꿈을 위한 여행~~🌈

0개의 댓글