버튼 탭에 효과음 하나 넣으려고 오디오 라이브러리를 찾다가 알게 됐다. 늘 쓰던 expo-av가 SDK 53부터 deprecated 됐고, 오디오는 expo-audio, 비디오는 expo-video로 쪼개졌다는 것.
expo-av의 Audio.Sound.createAsync는 명령형이라 로딩·재생·언로드를 직접 챙겨야 했다. 언로드를 깜빡하면 사운드 인스턴스가 쌓여서 메모리가 새기도 했고. expo-audio는 훅 기반이라 이 뒷정리를 알아서 해준다.
설치
npx expo install expo-audio
app.json에 플러그인도 넣어준다. 마이크 권한이 필요 없는 재생 전용이면 옵션은 비워도 된다.
{
"plugins": ["expo-audio"]
}
효과음 재생
핵심은 useAudioPlayer 훅이다. 소스를 넘기면 플레이어를 만들어주고, 컴포넌트가 언마운트될 때 알아서 해제한다.
import { useAudioPlayer } from 'expo-audio';
import { Pressable, Text } from 'react-native';
export default function TapButton() {
const player = useAudioPlayer(require('./assets/pop.mp3'));
const onPress = () => {
player.seekTo(0); // 연타해도 처음부터 다시 재생
player.play();
};
return (
<Pressable onPress={onPress}>
<Text>탭</Text>
</Pressable>
);
}
여기서 seekTo(0)가 포인트다. play()만 부르면 이미 끝까지 재생된 사운드는 다시 안 울린다. 효과음은 연타되는 경우가 많으니 매번 커서를 앞으로 되감아줘야 소리가 난다. 이거 몰라서 "왜 한 번만 나오지" 하고 한참 봤다.
배경음 반복 재생
배경음은 화면 상태를 구독해서 반복 여부를 켜준다. useAudioPlayerStatus로 재생 상태도 읽을 수 있다.
import { useAudioPlayer, useAudioPlayerStatus } from 'expo-audio';
import { useEffect } from 'react';
function Bgm() {
const player = useAudioPlayer(require('./assets/bgm.mp3'));
const status = useAudioPlayerStatus(player);
useEffect(() => {
player.loop = true;
player.volume = 0.4;
player.play();
}, [player]);
return null;
}
무음 스위치에서도 소리 내기
iOS는 기기 무음 스위치가 켜져 있으면 앱 사운드도 막는다. 게임 효과음처럼 무음이어도 나와야 하는 소리는 오디오 모드를 따로 잡아준다.
import { setAudioModeAsync } from 'expo-audio';
await setAudioModeAsync({ playsInSilentMode: true });
정리
expo-av에서 넘어오는 마이그레이션은 생각보다 간단했다. createAsync + 수동 unloadAsync 패턴을 useAudioPlayer 훅 하나로 바꾸니 언로드 코드가 통째로 사라졌다. 효과음은 seekTo(0) 되감기, 배경음은 loop, iOS 무음은 playsInSilentMode — 이 셋만 기억하면 대부분 커버된다.