-
Notifications
You must be signed in to change notification settings - Fork 7
๐ชต 7. ํจ์จ์ ์ธ ์ฌ์ด๋ ๊ด๋ฆฌ๋ฅผ ์ํ ์ฑ๊ธํค ํจํด ์ ์ฉ๊ธฐ: ๊ฒ์ ์ฌ์ด๋ ์์คํ ์ต์ ํ
๊ฒ์ ๊ฐ๋ฐ ์ค ์ฌ์ด๋ ๊ด๋ฆฌ ์์คํ ์ ๊ตฌํํ๋ฉด์ ๋ง์ฃผ์น ๋์ ๊ณผ์ ๋ค๊ณผ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ์ ๊ทผ ๋ฐฉ๋ฒ์ ๊ณต์ ํ๊ณ ์ ํฉ๋๋ค.
๋ธ๋ผ์ฐ์ ๋ค์ ์ฌ์ฉ์ ๊ฒฝํ ๋ณดํธ๋ฅผ ์ํด ์๊ฒฉํ ์๋ ์ฌ์ ์ ์ฑ ์ ์ ์ฉํฉ๋๋ค. ํนํ, ์ฌ์ฉ์๊ฐ ๋ช ์์ ์ผ๋ก ์ํธ์์ฉํ์ง ์์ผ๋ฉด ์ค๋์ค ์ฌ์์ด ์ฐจ๋จ๋ฉ๋๋ค. ์ด๋ ๊ฒ์ ์ง์ ์ ํ์ํ ์ฌ์ด๋์ ์๋ ์ฌ์์ด ๋ถ๊ฐ๋ฅํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ๋
๋ฆฝ์ ์ผ๋ก Audio
๊ฐ์ฒด๋ฅผ ๊ด๋ฆฌํ๋ฉด ์ค๋ณต๋ ์ธ์คํด์ค ์์ฑ์ผ๋ก ์ธํด ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ๋ฐ์ํฉ๋๋ค. ์ด ๋ฌธ์ ๋ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง๋๋ค.
// โ ์๋ชป๋ ๊ตฌํ ์์: ๊ฐ ์ปดํฌ๋ํธ๊ฐ ๋์ผํ ์ฌ์ด๋๋ฅผ ๊ฐ๋ณ ๊ด๋ฆฌ
const Component = () => {
const audio = new Audio(soundFile); // ์ปดํฌ๋ํธ๋ง๋ค ์๋ก์ด Audio ๊ฐ์ฒด ์์ฑ
const playSound = () => {
audio.play().catch(console.error);
};
};
๊ฐ๋ณ์ ์ผ๋ก Audio
๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ์ฝ๋๋ ์ค๋ณต์ด ๋ง์์ง๊ณ , ํ์ฅ์ฑ์ด ๋ถ์กฑํฉ๋๋ค. ์์ปจ๋ ์๋ก์ด ์ฌ์ด๋๋ฅผ ์ถ๊ฐํ ๋๋ง๋ค ๊ฐ ์ปดํฌ๋ํธ์์ ์์ ์ด ํ์ํฉ๋๋ค.
SoundManager
๋ ์ฑ๊ธํค ํจํด์ ์ฌ์ฉํด ๋ชจ๋ ์ปดํฌ๋ํธ์์ ๋์ผํ ์ธ์คํด์ค๋ฅผ ๊ณต์ ํฉ๋๋ค.
// โ
SoundManager ํด๋์ค: ์ฑ๊ธํค ํจํด ๊ธฐ๋ฐ
export class SoundManager {
private static instance: SoundManager;
private audioMap: Map<string, HTMLAudioElement> = new Map();
private constructor() {} // ์ธ๋ถ์์ ์ธ์คํด์ค ์์ฑ ๋ฐฉ์ง
static getInstance(): SoundManager {
if (!SoundManager.instance) {
SoundManager.instance = new SoundManager();
}
return SoundManager.instance;
}
}
์ด๋ฅผ ํตํด ์ฌ์ด๋ ๊ด๋ฆฌ๋ฅผ ์ค์ํํด ์ค๋ณต ๊ฐ์ฒด ์์ฑ์ ๋ฐฉ์งํฉ๋๋ค.
์ฌ์ด๋๋ฅผ ๋ฏธ๋ฆฌ ๋ก๋ํ์ฌ ์ฌ์ ์ ์ง์ฐ์ ์ค์ ๋๋ค.
// โ
์ฌ์ด๋ ํ๋ฆฌ๋ก๋ฉ ๋ฉ์๋
preloadSound(id: string, src: string): void {
if (!this.audioMap.has(id)) {
const audio = new Audio(src);
audio.load(); // ์ฌ์ด๋ ํ์ผ ๋ฏธ๋ฆฌ ๋ก๋, ์ฌ์ ์ง์ฐ ์ต์ํ
this.audioMap.set(id, audio);
}
}
์ด์ฐจํผ ์๋ ์ฌ์์ด ๋์ง ์์ผ๋ ๋ฏธ๋ฆฌ ์ฌ์ด๋๊ฐ ๋์ค๋ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ์ ๋ฏธ๋ฆฌ ๋ฃ์ด๋์ ์ ์๋ ๊ธฐ๋ฅ์ ํ์ฉํด ๋ฏธ๋ฆฌ ์ฌ์ด๋๋ฅผ ๋ถ๋ฌ์ต๋๋ค.
์ฌ์ฉ์๊ฐ ๋ช ์์ ์ผ๋ก ์ํธ์์ฉํ๊ธฐ ์ ์๋ ์ฌ์ด๋ ์๋ ์ฌ์์ด ์ฐจ๋จ๋ฉ๋๋ค. ์ด๋ฅผ ๋ช ์ํ๊ธฐ ์ํด ์ค๋ฅ๋ฅผ ์๋ณํ๊ณ ์ฌ์ฉ์์๊ฒ ๋ช ํํ ์๋ฆฝ๋๋ค.
// โ
์๋ ์ฌ์ ์ ์ฑ
์ฐํ ๋ฐ ์๋ฌ ์ฒ๋ฆฌ
async playSound(id: string, volume = 1): Promise<void> {
const audio = this.audioMap.get(id);
if (!audio) {
console.warn(`์ฌ์ด๋ ID(${id})๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.`);
return;
}
try {
audio.volume = volume;
await audio.play(); // ์ฌ์ฉ์ ์ํธ์์ฉ ์์ ๊ฒฝ์ฐ ์คํจ ๊ฐ๋ฅ
audio.currentTime = 0; // ์ฌ์ ์๋ฃ ํ ์ฒ์์ผ๋ก ๋๊ฐ๊ธฐ
} catch (error) {
if (error instanceof Error) {
if (error.name === 'NotAllowedError') {
console.info('๋ธ๋ผ์ฐ์ ์ ์ฑ
์ ์ํด ์ฌ์ด๋ ์๋ ์ฌ์์ด ์ฐจ๋จ๋์์ต๋๋ค.');
} else {
console.error(`์ฌ์ด๋ ์ฌ์ ์ค๋ฅ: ${error.message}`);
}
}
}
}
- ์ฑ๊ธํค ํจํด์ผ๋ก Audio ๊ฐ์ฒด ์ฌ์ฌ์ฉ
- ์ฌ์ด๋ ๋ฆฌ์์ค์ ์ค๋ณต ๋ก๋ฉ ๋ฐฉ์ง
- ์ฌ์ด๋ ํ๋ฆฌ๋ก๋ฉ์ผ๋ก ์ฌ์ ์ง์ฐ ์ต์ํ
- ํจ์จ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ
- ๋ธ๋ผ์ฐ์ ์๋ ์ฌ์ ์ ์ฑ ๋์
- ์ฌ์ด๋ ์ฌ์ ์คํจ์ ๋ํ ์๋ต์ ์ฌ์ฉ์ or ๊ฐ๋ฐ์์๊ฒ ์๋ฆผ - ์ฐ์ํ ์ฒ๋ฆฌ
์ฌ์ด๋๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ID์ ๊ฒฝ๋ก๋ฅผ ์์๋ก ์ ์ํฉ๋๋ค. ์ด๋ค ID๊ฐ ์ค๋์ง์ ๋ํ ๋ช ์๋ฅผ ํ๊ณ , ๊ด๋ฆฌ ์ผ๊ด์ฑ์ ์ ์งํฉ๋๋ค.
// ์ฌ์ด๋ ID ์ ์
export const SOUND_IDS = {
ENTRY: 'entry',
BUTTON_CLICK: 'button_click',
} as const;
export const SOUND_PATHS = {
[SOUND_IDS.ENTRY]: '/sounds/entry-sound.mp3',
[SOUND_IDS.BUTTON_CLICK]: '/sounds/button-click.mp3',
};
์ปดํฌ๋ํธ๊ฐ ๋ก๋๋ ๋, ํ์ํ ์ฌ์ด๋๋ฅผ ๋ฏธ๋ฆฌ ๋ก๋ํฉ๋๋ค.
import { SoundManager } from './SoundManager';
import { SOUND_IDS, SOUND_PATHS } from './constants';
const GameEntryComponent = () => {
useEffect(() => {
const soundManager = SoundManager.getInstance();
soundManager.preloadSound(SOUND_IDS.ENTRY, SOUND_PATHS[SOUND_IDS.ENTRY]);
}, []);
const handlePlayerJoin = () => {
const soundManager = SoundManager.getInstance();
void soundManager.playSound(SOUND_IDS.ENTRY, 0.8); // ๋ณผ๋ฅจ ์กฐ์ ํฌํจ
};
return <button onClick={handlePlayerJoin}>Join Game</button>;
};
๋์ ์ผ๋ก ์๋ก์ด ์ฌ์ด๋๋ฅผ ์ถ๊ฐํ๊ณ ์ฌ์ํ ์ ์๋ ์ ์ฐ์ฑ์ ์ ๊ณตํฉ๋๋ค.
const handleDynamicSound = (id: string, path: string) => {
const soundManager = SoundManager.getInstance();
soundManager.preloadSound(id, path);
void soundManager.playSound(id, 1.0);
};
<button
onClick={() =>
handleDynamicSound('dynamic_alert', '/sounds/dynamic-alert.mp3')
}
>
Play Alert
</button>;
์ด๋ฌํ ๊ตฌํ์ ํตํด ํจ์จ์ ์ด๊ณ ์์ ์ ์ธ ์ฌ์ด๋ ์์คํ ์ ๊ตฌ์ถํ ์ ์์์ต๋๋ค. ์ฑ๊ธํค ํจํด์ ์ ์ฉ์ผ๋ก ๋ฆฌ์์ค ๊ด๋ฆฌ๊ฐ ์ฉ์ดํด์ก๊ณ , ๋ธ๋ผ์ฐ์ ์ ์ ์ฝ์ฌํญ๋ ์ ์ ํ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋์์ต๋๋ค.
- 1. ๊ฐ๋ฐ ํ๊ฒฝ ์ธํ ๋ฐ ํ๋ก์ ํธ ๋ฌธ์ํ
- 2. ์ค์๊ฐ ํต์
- 3. ์ธํ๋ผ ๋ฐ CI/CD
- 4. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด Canvas ๊ตฌํํ๊ธฐ
- 5. ์บ๋ฒ์ค ๋๊ธฐํ๋ฅผ ์ํ ์์ CRDT ๊ตฌํ๊ธฐ
-
6. ์ปดํฌ๋ํธ ํจํด๋ถํฐ ์น์์ผ๊น์ง, ํจ์จ์ ์ธ FE ์ค๊ณ
- ์ข์ ์ปดํฌ๋ํธ๋ ๋ฌด์์ธ๊ฐ? + Headless Pattern
- ํจ์จ์ ์ธ UI ์ปดํฌ๋ํธ ์คํ์ผ๋ง: Tailwind CSS + cn.ts
- Tailwind CSS๋ก ๋์์ธ ์์คํ ๋ฐ UI ์ปดํฌ๋ํธ ์ธํ
- ์น์์ผ ํด๋ผ์ด์ธํธ ๊ตฌํ๊ธฐ: React ํ๊ฒฝ์์ ํจ์จ์ ์ธ ์น์์ผ ์ํคํ ์ฒ
- ์น์์ผ ํด๋ผ์ด์ธํธ ์ฝ๋ ๋ถ์ ๋ฐ ๊ณต์
- 7. ํธ๋ฌ๋ธ ์ํ ๋ฐ ์ฑ๋ฅ/UX ๊ฐ์
- 1์ฃผ์ฐจ ๊ธฐ์ ๊ณต์
- 2์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- 3์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- 4์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- 5์ฃผ์ฐจ ๋ฐ๋ชจ ๋ฐ์ด
- WEEK 06 ์ฃผ๊ฐ ๊ณํ
- WEEK 06 ๋ฐ์ผ๋ฆฌ ์คํฌ๋ผ
- WEEK 06 ์ฃผ๊ฐ ํ๊ณ