[JS] audio 태그로 bgm 재생, play() failed because the user didn't interact with the document first 에러

jellyjw·2023년 11월 24일
3

개요

개발중인 프로젝트에 BGM 재생이 필요해서 오디오 컴포넌트를 만들어서 재생시켜 보았다. 그런데 재생시키는 과정이 순탄치 않아서 컴포넌트 생성 과정과 에러 해결 기록까지 순서대로 적어보고자 한다. :)

1. Audio 컴포넌트 및 전역변수 생성

recoil 을 이용해서 오디오 재생 상태를 관리할 변수를 하나 만들어주고,

// audio.ts
import { atom } from 'recoil';

export const audioPlayState = atom<boolean>({
  key: 'audioPlayState',
  default: false,
});

오디오 컴포넌트를 만들어서 bgm 파일을 import 해주고
HTMLAudioElement 타입과 useRef 로 오디오 객체를 하나 생성해준다.

import bgm_01 from 'assets/audio/bgm/bgm_01.mp3';
import bgm_02 from 'assets/audio/bgm/bgm_02.mp3';

export function Audio() {
  const [audioPlay, setAudioPlay] = useRecoilState(audioPlayState);
  const audioRef = useRef<HTMLAudioElement>(null);
  
    const onAudioPlay = () => {
    if (audioRef.current) {
      audioRef.current.play();
    }
  };

  const onAudioPause = () => {
    if (audioRef.current) {
      audioRef.current.pause();
    }
  };
  
  
 	// ...
}

그리고 배경음악을 두개로 반복재생하기 위해서 onended 이벤트 발생시 changeBgmTrack 함수가 실행될 수 있도록 해 track을 바꿔주었다.

이렇게 하면 1번트랙이 재생 종료되면 2번트랙이 재생되고 autoplay 속성으로 인해 1,2번 트랙이 반복 재생된다.

  const [bgmTrack, setBgmTrack] = useState(bgm_01)

  const changeBgmTrack = () => {
    setBgmTrack(prev => (prev === bgm_01 ? bgm_02 : bgm_01));
  };

  return <audio ref={audioRef} onEnded={changeBgmTrack} src={bgm} autoPlay></audio>;

2. Audio.d.ts 파일 생성

// Audio.d.ts
declare module '*.mp3' {
  const value: string;
  export default value;	
}

mp3 파일의 타입을 d.ts 파일에 선언해준다.

3. index.tsx 루트에 Audio 컴포넌트 import

특정 페이지에서 재생되는게 아닌 배경음악이었기 때문에, RecoilRoot 감싸져 있는 index.tsx 파일에 Audio 컴포넌트를 위치시키고 특정 페이지에서 아까 만든 audioPlay 라는 변수를 true로 바꿔주면, 배경음악이 재생된다.

// index.tsx
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <QueryClientProvider client={queryClient}>
    <RecoilRoot>
      <Audio />
      <RouterProvider router={router} />
      <ReactQueryDevtools initialIsOpen={true} />
    </RecoilRoot>
  </QueryClientProvider>,
);

play() failed because the user didn't interact with the document first 에러 해결

처음에 프로젝트 시작시 바로 배경음을 띄우려 해봤는데, 계속 참고 링크 와 함께 play() failed because the user didn't interact with the document first 이런 에러가 발생했다.

Chrome 브라우저에서 브라우저 진입시 바로 오디오를 자동재생시키는 것을 막는다는 내용이었다. 테스트해봤는데 크롬뿐만 아니라 edge같은 다른 브라우저에서도 동일한 에러를 띄우며 막고 있었다.

크롬에서 자동재생을 허용하는 경우는 다음과 같다. (출처 : 크롬 문서)

  • Muted autoplay is always allowed.
  • Autoplay with sound is allowed if:
    - The user has interacted with the domain (click, tap, etc.).
    - On desktop, the user's Media Engagement Index threshold has been crossed, meaning the user has previously played video with sound.
    - The user has added the site to their home screen on mobile or installed the PWA on desktop.
  • Top frames can delegate autoplay permission to their iframes to allow autoplay with sound.

음소거 되어있는 자동재생이나, 사용자가 Click / tab 등 상호작용이 일어났을때 등 자동재생이 허용되는 경우들을 설명해주고 있다.

내 프로젝트에는 초기화면에 모드 선택 기능이 있었는데 이때 무조건 유저의 클릭 이벤트가 일어나기 때문에 해당 이벤트가 일어나고나서 자동재생을 시켰더니 배경음악이 제대로 재생됐다.

  const onBgmPlay = () => {
    setAudioPlay(true);
  };

  const moveToPage = (path: string) => {
    // ... 모드 선택시 로딩과 함께 페이지를 이동시키는 함수
    onBgmPlay();
  };

이렇게 직접적인 유저의 click/tab 이벤트 말고, 화면중 어디를 클릭해도 재생시키도록 addEventListener 이벤트를 이용하고자 해도 브라우저가 같은 에러를 던지며 막는다.

구글링을 통해 여러 방법을 시도해봤지만, 전부 재생이 되지 않았다.

  • useEffect로 click, mousedown등의 이벤트를 페이지에 등록후 재생
  • 처음 진입시 음소거 처리 해놨다가, 바로 음소거 해제 등..

문제는 모드 선택시 클릭 이벤트가 일어나서 배경음 재생에 성공하더라도, 페이지에 진입했을 때 새로고침을 하면 다시 초기화되면서 브라우저에서 초기 진입시 자동재생 으로 인식해 재생을 막는다는 것이었다. 상단에 배경음악을 끌 수 있는 설정이 있긴 하지만, default 설정은 on 이었기 때문에 바로 재생될수 있도록 많은 시도를 해봤는데 막혀서 머리가 아파왔다.

내가 개발하고 있는 서비스는 기존 프로젝트에 iframe 으로 띄워지는 게임 모듈이어서 새로고침하면 게임 자체가 종료되기 때문에 새로고침 경우를 고려하지 않았지만,

기존에 운영되고 있는 서비스는 새로고침을 해도 자동재생이 잘 되길래 코드를 살펴봤는데, 분명 내가 적용한 방법과 크게 다를바가 없었다. 알고 넘어가고 싶어서 계속 알아봤는데,,

답은 아까 그 문서에 있었다. (문서를 잘 읽자 제발)

안내하고 있는 링크 에 들어가면 이런 표가 나오는데, 여기 is High 값이 Yes 인 Origin에서만 새로고침시 자동재생이 제대로 되었다.

이게 바로 위에서 자동재생을 허용하는 조건중 미디어 참여 지수 항목이었는데,

그렇다고 한다. 내가 왜 혼란스러웠냐면 운영중인 서비스에서 분명 코드는 동일한데 실서버에서는 새로고침시에도 자동재생이 되고 테스트서버에서는 자동재생이 되지 않았기 때문이었다.. 바로 이 미디어 참여 지수가 실서버의 경우 스코어가 높아 Yes 상태였고 나머지는 개발서버는 No 였기 때문이었다.

마치며

처음으로 오디오 태그를 이용해 음원, 음악을 재생시켜 봤는데 생각보다 어렵지 않았고 실제로 내가 넣은 파일이 재생되니까 재밌고 신기했다. 근데 자동재생 정책을 처음 겪어보니 ,, 새로고침시에도 자동재생 되도록 구현하려면 생각보다 까다롭고 구현하기 힘들 것 같았다. 어쨌든 배경음 재생 기능 추가 완료!

profile
남는건 기록뿐👩🏻‍💻

0개의 댓글