실전 프로젝트에선 게임을 진행하다보니 게임 진행 시 효과음과 음악을 넣는 것이 좋을거라는 의견을 받았다.
소리를 추가해본 경험은 없기 때문에 이번에 추가해보고 싶었고, 구글링을 했더니 리액트에 소리를 넣는 라이브러리로 Howler.js
라는 라이브러리를 많이 사용한다는 것을 알아냈다.
냉큼 github 문서와 다른 사람들이 어떻게 사용하는지 찾아보고 적용시켜보았다.
// BGM Section
const range = useSelector((state) => state.bgmVolume.volume);
// 1. 리덕스 툴킷을 이용해서 볼륨 조절을 전역 상태로 관리했다.
// 1-1. range라는 상수에 volume 값을 할당하였다.
const sound = new Howl({
// 2. sound라는 상수에 new Howl 생성자 생성하고 원하는 옵션을 추가한다.
src: [bgm],
// 2-1. 사용할 배경음 src에 추가
loop: true,
// 2-2. 반복재생값 true로 설정 (반복재생 on)
volume: 0.1,
// 2-3. 기본 볼륨은 0.1로 설정 (최소 0, 최대 1의 값을 가질 수 있다)
});
const soundStop = () => sound.stop();
// 3. soundStop이라는 함수가 실행되면 sound가 멈추도록 설정한다.
useEffect(() => {
sound.play();
// 4. 화면이 렌더링될 때 sound,play()를 통해 배경음악을 실행시킨다.
sound.on('play', () => {
const history = createBrowserHistory();
// 4-1. history라는 상수에 createBroserHistory() 함수를 할당해서 history 객체를 확인할 수 있게 한다.
history.listen(({ action }) => {
// 4-2. history 객체를 이용해 브라우저의 action을 감지하고,
if (action === 'POP') {
// 4-3. 감지한 브라우저의 action이 POP(이동)인 경우,
sound.stop();
// 4-4. bgm이 멈추도록 설정했다.
}
});
});
return soundStop;
// 4-5. sound.on() 두번째 매개변수인 익명 함수의 리턴값은 soundStop으로 설정한다.
// 4-6. loop을 true로 설정했기 때문에 soundStop이 실행될 일은 없을 듯.
}, []);
useEffect(() => {
if (range) {
// 5. 전역 상태로 관리하고 있는 range의 값이 있는 경우,
if (range === 0) {
// 5-1. range의 값이 0일때
sound.mute();
// 5-2. sound는 음소거가 된다.
} else {
// 5-3. range의 값이 0이외의 값일때
Howler.volume(range);
// 5-4. sound의 볼륨은 range의 값으로 설정된다.
// 5-5. Howl 생성자로 실행될 음악을 초기에 설정하고, 일반적으로 play될 땐 그대로 sound를 이용한다.
// 5-6. 하지만 global volume을 변경하게될땐 sound나 howl이 아닌 holwer를 사용해야한다.
}
}
}, [sound, range]);
// 5-7. sound와 range의 값이 변경될때마다 useEffect 내부의 내용이 재실행되어 볼륨 조절이 가능하게된다.
// End BGM Section
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
volume: 0.03,
// 1. 볼륨의 초기값을 0.03으로 설정하여 소리가 너무 크지 않게 들리도록 하였다.
};
const soundSlice = createSlice({
name: 'bgmVolume',
// 2. slice 파일의 식별 이름을 설정한다.
initialState,
// 2-1. 위에 선언했던 초기값을 두번째 인자로 설정한다.
reducers: {
// 2-2. 세번째 인자로 initialState의 값을 변경할 리듀서 함수를 설정한다.
setVolume: (_, action) => ({
// 2-3. 해당 파일은 volume의 값만 변경하는 용도로, 변경할 값도 volume뿐이기 때문에 리듀서 함수도 1개가 된다.
// 2-4. setVolume이라는 리듀서 함수를 생성해서, 변경내용 (action)을 매개변수로 설정한다.
// 2-5. 그리고 setVolume 함수가 volume의 값을 action의 payload의 값으로 변화시킨다.
volume: action.payload,
}),
},
});
export const { setVolume } = soundSlice.actions;
// 3. setVolume 함수를 외부로 export하여 외부에서 setVolume을 import 하고 volume의 값을 변경할 수 있게 한다.
export default soundSlice.reducer;
//4. store에 reducer를 등록하기 위해 soundSlice.reducer를 외부로 export한다.
sound.volume()
의 매개변수로 설정함으로서 직접적으로 볼륨 조절이 가능하게된다.function BGMTab({ setting }) {
const sound = useSelector((state) => state.bgmVolume);
// 1. seSelector를 이용하여 soundSlice의 state 중에서도 bgmVolume에 해당하는 값에 접근한다.
// 1-1. 해당 값을 sound라는 상수에 할당한다.
const [range, setRange] = useState(sound.volume);
// 2. range, setRange 변수를 선언하여 useState의 초기값으로 initialState의 volume 값을 할당한다.
const dispatch = useDispatch();
// 3. 리듀서 함수를 사용할 수 있도록 dispatch라는 상수에 useDispatch() 함수를 할당한다.
useEffect(() => {
dispatch(setVolume(range));
// 4. soundSlice 파일로부터 import해온 setVolume 리듀서 함수의 매개변수로 range를 설정한다.
// 4-1. dispatch를 이용해서 range라는 액션 객체를 넘겨주어 volume의 상태를 업데이트한다.
}, [dispatch, range]);
// 4-2. dispatch가 일어나고 range의 값이 변경될때마다 useEffect 내용이 실행된다.
return (
<StBGMTab>
사운드 조절 기능
<StSubTitle>BGM 음량 조절</StSubTitle>
<StVolumeCon>
<StVolume
className="rangeInput"
type="range"
min={0}
max={0.1}
step={0.001}
value={range}
// 5. type이 range인 인풋 필드의 value 값을 range로 설정한다.
onChange={(e) => setRange(e.target.value)}
// 5-1. onChange 이벤트를 감지하고, 해당 이벤트로 변경되는 value를 setRange의 값으로 할당한다.
// 5-2. setState 함수를 통해 range의 값이 변경되고, 변경된 range의 값이 volume 값으로 변한다.
/>
</StVolumeCon>
<StBtnBox />
</StBGMTab>
);
}
import { Howl } from 'howler';
function useEffectSound(src, volume = 1) {
// 1. useEffectSound라는 함수를 선언하고, 매개변수로 src와 volume을 지정한다.
let sound;
// 1-1. sound라는 변수를 선언하고 값은 따로 할당하지 않는다.
const soundEffect = (src) => {
// 1-2. 중첩함수로 soundEffect 화살표 함수를 생성한다.
// 1-3. 해당 함수의 매개변수는 src로 설정한다.
sound = new Howl({ src });
// 1-4. new howl 생성자를 만들고 sound라는 변수에 할당한다.
sound.volume(volume);
// 1-5. sound.volume의 매개변수는 useEffectSound의 인자인 volume과 동일하게 설정한다
};
soundEffect(src);
// 1-6. useEffectSound 내부에서 soundEffect 함수를 호출한다.
return sound;
// 1-7. useEffectSound의 리턴값을 sound로 설정하여 soundEffect의 결과물이 sound에 할당되도록 한다.
}
export default useEffectSound;
const startEffect = useEffectSound(startSound, 1);
const endEffect = useEffectSound(endSound, 1);
// custom hook을 호출하고, 특정 상수에 해당 custom hook을 할당한다.
// 사용하는 효과음이 2개라면 2개를 생성하고, useEffectSound의 첫번째 매개변수인 src를 다르게 설정한다.
// 이때 두번째 매개변수는 숫자로, useEffectSound의 volume값이 된다.
startEffect.play()
endtEffect.play()
// 효과음을 사용하고자 하는 곳에 .play()로 효과음이 플레이되게 만든다.
앞으로 변경할 값이 전역적으로 필요한 경우에 대해 조금 감이 잡힌것 같다.
구글링을 통해 새로운 것을 배우고, 또 원하는 기능을 써먹을 수 있는 코드를 발견해서 좋았다.
볼륨 조절이나 효과음 재생 부분을 직접 생각해냈다면 더 좋았겠지만, 찾아낸 코드를 이해하고 사용해보면서 배우는 것도 있을 것이라고 생각한다.
출처: