앱인토스 WebView에서 백그라운드/포그라운드 전환 시 사운드 제어 방법 문의

안녕하세요, 앱인토스로 게임을 개발 중인 개발자 단테입니다.

현재 PIXI.js 기반의 웹 게임을 앱인토스 환경에서 서비스하고 있는데, 백그라운드/포그라운드 전환 시 사운드 제어와 관련하여 문의드립니다.

현재 상황:

  • 웹 환경에서는 visibilitychange, pageshow/pagehide 이벤트로 백그라운드 전환을 감지하여 사운드를 제어하고 있습니다

  • 앱인토스 WebView 환경에서는 다음과 같은 이슈가 발생하고 있습니다:

발생 이슈:

1. Android: 앱이 백그라운드 상태일 때 사운드가 계속 재생되는 문제

2. iOS: 백그라운드 전환 후 미니앱에 재접속하면 사운드가 재생되지 않는 문제

질문:

1. 앱인토스 WebView 환경에서 앱의 백그라운드/포그라운드 전환을 정확하게 감지할 수 있는 이벤트나 API가 있을까요?

2. @apps-in-toss/web-framework에서 제공하는 focus/blur 관련 이벤트 핸들러가 있는지 궁금합니다

3. 혹시 사운드/오디오 제어를 위한 별도의 가이드라인이나 베스트 프랙티스가 있다면 공유 부탁드립니다

현재 저희가 시도해본 방법은:

  • document.visibilitychange 이벤트 리스너

  • window.pageshow/pagehide 이벤트 리스너

  • Web Audio API의 AudioContext 상태 관리

위 방법들이 일반 웹 환경에서는 잘 동작하지만, 앱인토스 WebView에서는 기대한 대로 동작하지 않고 있습니다.

조언 부탁드립니다. 감사합니다!

안녕하세요. 방금 visibilitychange 이벤트 리스너 테스트시 정상 동작을 확인하였는데요 ..!
따로 web api를 막지 않기 때문에 visibilitychange 이벤트로 감지해주시면될 것 같아요.

아래 샘플 코드 전달드려요 :grinning_face:

import { Button, Text } from '@toss-design-system/mobile';
import { useCallback, useEffect, useRef, useState } from 'react';


declare global {
  interface Window {
    _bgmAudio?: HTMLAudioElement;
  }
}
export default function GoogleAdmobRewardedAdPausePlay() {
  const [isSoundOn, setIsSoundOn] = useState(false);
  const [needManualResume, setNeedManualResume] = useState(false);
  const [log, setLog] = useState<string[]>([]);
  const audioRef = useRef<HTMLAudioElement | null>(null);

  const addLog = (msg: string) => {
    setLog((prev) => [...prev, `[${new Date().toLocaleTimeString()}] ${msg}`]);
  };


  useEffect(() => {
    if (!window._bgmAudio) {
      const audio = new Audio('/bgm.mp3');
      audio.loop = true;
      audio.volume = 1;
      audio.preload = 'auto';
      window._bgmAudio = audio;
      addLog('🎧 오디오 초기화 완료');
    } else {
      addLog('♻️ 기존 오디오 객체 재사용');
    }

    audioRef.current = window._bgmAudio;

    const audio = audioRef.current;
    if (audio && !audio.paused) {
      setIsSoundOn(true);
    }
  }, []);

  // ✅ 백그라운드/포그라운드 진입 시 BGM 제어
  useEffect(() => {
    const handleVisibilityChange = () => {
      const audio = audioRef.current;
      if (!audio) return;

      if (document.visibilityState === 'hidden') {
        audio.pause();
        setIsSoundOn(false);
        addLog('🌙 백그라운드 진입 - BGM 정지');
      } else if (document.visibilityState === 'visible') {
        audio
          .play()
          .then(() => {
            addLog('🌞 포그라운드 복귀 - BGM 자동 재생 성공');
            setIsSoundOn(true);
          })
          .catch((err) => {
            addLog(`⚠️ 포그라운드 복귀 재생 실패: ${err.name}`);
            setNeedManualResume(true);
          });
      }
    };

    document.addEventListener('visibilitychange', handleVisibilityChange);
    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, []);

  const toggleSound = () => {
    const audio = audioRef.current;
    if (!audio) return;

    if (isSoundOn) {
      audio.pause();
      setIsSoundOn(false);
      addLog('🔇 사운드 중지');
    } else {
      audio
        .play()
        .then(() => {
          setIsSoundOn(true);
          addLog('🔊 사운드 재생 시작');
        })
        .catch((err) => {
          addLog(`❌ 재생 실패: ${err.name}`);
        });
    }
  };

  return (
    <div style={{ padding: '2rem' }}>
      <p>배경음악 상태: <strong>{isSoundOn ? '재생 중' : '중지됨'}</strong></p>
      <Button title="BGM Toggle" onClick={toggleSound}>
        {isSoundOn ? '🔇 사운드 끄기' : '🔊 사운드 켜기'}
      </Button>

      {needManualResume && (
        <p style={{ color: 'red', fontWeight: 'bold' }}>
          👉 광고 종료나 앱 재진입 후 사운드를 들으려면 화면을 터치하세요.
        </p>
      )}

      <h3>📜 로그</h3>
      <pre
        style={{
          background: '#000',
          padding: '1rem',
          height: '200px',
          overflowY: 'scroll',
          fontSize: '12px',
          color: '#0f0',
        }}
      >
        {log.slice().reverse().join('\n')}
      </pre>
    </div>
  );
}

1개의 좋아요