앱 사운드 디자인은(는) 무엇을 하나요?
앱에서 언제, 어떤, 어떻게 소리를 사용할지에 대한 디자인 결정을 내립니다. 오디오 엔지니어링(sound-engineer)이나 레트로 효과음(win31-audio-design)에 관한 내용이 아닙니다. 이 스킬은 UI 사운드에서 가장 어려운 질문인 '이 상호작용에 소리가 필요한가?' 에 답합니다. 그리고 필요하다면 어떤 소리여야 하는지도 알려줍니다.
사용 시기
이 스킬을 사용하는 경우:
- 웹 또는 모바일 앱에서 어떤 상호작용에 소리 피드백이 필요한지 결정할 때
- 알림, 확인, 오류에 적합한 사운드 특성을 선택할 때
- ElevenLabs, AudioCraft, Stable Audio로 AI 생성 UI 사운드를 만들 때
- Web Audio API로 절차적 사운드를 구현할 때
- iOS/Android에서 햅틱 피드백과 오디오를 페어링할 때
- 제품의 소닉 브랜드 아이덴티티를 구축할 때
- 기존 앱 사운드 디자인이 도움이 되는지 짜증나는지 감사할 때
사용하지 말아야 할 경우:
- 전문 오디오 믹싱/마스터링 -> sound-engineer 사용
- Windows 3.1 테마 시스템 사운드 -> win31-audio-design 사용
- 음악 작곡 또는 배경 음악 -> sound-engineer 사용
- 접근성 스크린 리더 오디오 -> accessibility-expert 사용
- 게임 오디오 시스템(3D 위치 음향, 동적 음악) -> sound-engineer 사용
근본적인 질문: 이 상호작용에 소리가 필요한가?
상호작용에 피드백이 필요한가?
|
+-- 사용자가 시작한 동작(탭, 클릭, 제출)?
| +-- 결과가 화면에 보이는가?
| | +-- 예, 명확함 --> 사운드는 선택 사항(보통 생략)
| | +-- 예, 하지만 미묘함 --> 사운드가 도움 됨(부드러운 확인음)
| | +-- 아니요(백그라운드 프로세스) --> 사운드 권장
| +-- 동작이 파괴적인가?
| +-- 예 --> 사운드 권장(뚜렷한 경고음)
| +-- 아니요 --> 사운드 선택 사항
|
+-- 시스템이 시작한 이벤트(알림, 경고)?
| +-- 사용자가 앱 안에 있는가?
| | +-- 예 --> 미묘한 인앱 사운드
| | +-- 아니요 --> 알림음(OS 규칙 따름)
| +-- 시간에 민감한가?
| +-- 예 --> 더 긴급하고 주목을 끄는 사운드
| +-- 아니요 --> 조용하거나 앰비언트 가능
|
+-- 앰비언트/분위기(백그라운드 상태)?
+-- 앱이 존재감을 표현하는 데 도움이 되는가? --> 미묘한 앰비언트
+-- 사용자가 작업/집중 중인가? --> 침묵
UI 사운드 어휘
모든 애플리케이션은 다음 카테고리의 사운드가 필요합니다. 모든 카테고리가 필요한 것은 아니며, 대부분의 앱은 3-5개가 필요합니다.
티어 1: 필수(대부분의 앱에 필요)
| 사운드 유형 | 목적 | 특성 | 지속 시간 | 예시 |
|---|---|---|---|---|
| 확인 | 동작 성공 | 밝고 상승하는 음정 | 100-300ms | Slack 메시지 전송 |
| 오류 | 동작 실패 | 낮고 평평하거나 하강하는 음정 | 200-400ms | 양식 유효성 검사 실패 |
| 알림 | 새 정보 도착 | 독특하고 중간 에너지 | 300-600ms | Slack 들어오는 메시지 |
티어 2: 다듬기(신경 쓰는 앱)
| 사운드 유형 | 목적 | 특성 | 지속 시간 | 예시 |
|---|---|---|---|---|
| 전환 | 뷰 간 탐색 | 미묘한 휙 소리 또는 탭 | 50-150ms | iOS 페이지 넘기기 |
| 토글 | 이진 상태 변경 | 선명한 클릭 또는 스냅 | 50-100ms | 스위치 켜기/끄기 |
| 호버 | 마우스 오버 피드백 | 거의 들리지 않는 틱 | 20-50ms | 메뉴 항목 호버 |
| 선택 | 목록에서 항목 선택 | 부드러운 팝 또는 플럭 | 50-150ms | 드롭다운 선택 |
| 삭제 | 파괴적 제거 | 텅 빈 thud 또는 휙 | 150-300ms | 휴지통 동작 |
티어 3: 즐거움(개성 있는 앱)
| 사운드 유형 | 목적 | 특성 | 지속 시간 | 예시 |
|---|---|---|---|---|
| 성취 | 이정표 도달 | 음악적, 축하하는 분위기 | 500ms-2s | Duolingo 레슨 완료 |
| 앰비언트 | 배경 존재감 | 반복, 분위기 있는 사운드 | 연속적 | Noisli, Forest 앱 |
| 브랜드 사운드 | 앱 열기/닫기 | 상징적, 기억에 남음 | 500ms-1.5s | Netflix 타-덤 |
| 이스터 에그 | 숨겨진 즐거움 | 장난기, 예상치 못함 | 변동 가능 | Konami 코드 보상 |
플랫폼 규칙
Apple (iOS / macOS)
Apple HIG는 특정 오디오 동작을 규정합니다:
- 시스템 사운드는 신성함: 시스템 경고음을 절대 재정의하거나 교체하지 마세요.
- 무음 스위치 존중: 물리적 무음 스위치가 켜져 있으면 미디어 재생을 제외한 앱 사운드가 중지됩니다.
- 오디오 세션 카테고리가 중요함:
ambient— 다른 오디오와 혼합되며 무음 스위치를 따름soloAmbient— 다른 오디오를 음소거하고 무음 스위치를 따름playback— 무음 스위치를 무시함(미디어 앱 전용)
- 햅틱과 사운드 페어링: 오디오와 함께
UIImpactFeedbackGenerator사용
Apple의 사운드 특성:
- 깨끗한 사인파와 필터링된 톤
- 최소한의 배음(순수하고 버즈 없음)
- 짧은 감쇠(사운드가 오래 남지 않음)
- 시스템 사운드와 일관된 볼륨
// iOS: 시스템 사운드 재생
import AudioToolbox
AudioServicesPlaySystemSound(1057) // 표준 키보드 클릭
// iOS: 햅틱 + 사용자 정의 사운드
let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred()
// 사용자 정의 사운드를 동시에 재생
Material Design (Android)
Google의 사운드 디자인 원칙:
- 기능 우선: 사운드는 장식이 아닌 정보를 제공해야 함
- 기본적으로 미묘함: UI 사운드는 미디어보다 조용해야 함
- Android에서는 햅틱 우선: 햅틱 피드백 선호; 햅틱만으로 부족할 때만 사운드 추가
- 의미 있을 때만 사운드 ON: 대부분의 상호작용에서 기본 상태는 무음이어야 함
Material 사운드 특성:
- 짧고 선명한 타악기 사운드
- 미묘한 하모닉 콘텐츠(순수 사인파 아님)
- Apple보다 약간 따뜻함(중간 주파수 더 많음)
- 저품질 폰 스피커에서도 잘 들리도록 설계됨
웹 앱
웹은 가장 약한 사운드 규칙을 가지며, 더 많은 자유를 의미하지만 더 큰 짜증 유발 위험도 있습니다.
- 절대 자동 재생하지 마세요 — 브라우저가 차단하고 사용자가 싫어합니다.
- 먼저 사용자 동작 필요: Web Audio API는 재생 전 사용자 상호작용이 필요합니다.
- 볼륨 컨트롤 제공: 항상 사용자가 음소거하거나 조절할 수 있게 하세요.
- 헤드폰 사용자 고려: 노트북 스피커에서 괜찮은 소리가 헤드폰에서는 거슬릴 수 있습니다.
AI로 사운드 만들기
ElevenLabs 음향 효과
최적 대상: 특정 특성 설명이 있는 사실적이고 고품질의 UI 사운드.
# 확인 차임 생성
curl -X POST "https://api.elevenlabs.io/v1/sound-generation" \
-H "xi-api-key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "짧고 밝은 상승 차임, 디지털, 깨끗한 사인파, 200ms, UI 확인 사운드",
"duration_seconds": 0.5
}'
ElevenLabs용 효과적인 프롬프트:
- 확인: "짧고 밝은 상승 2음 차임, 깨끗한 디지털, 따뜻함, 200ms"
- 오류: "낮고 텅 빈 thud, 약간의 버즈, 디지털, 300ms, 거칠지 않음"
- 알림: "부드러운 3음 실로폰 멜로디, 친근함, 400ms"
- 토글: "선명한 기계식 클릭, 플라스틱 스위치, 50ms"
- 삭제: "부드럽고 텅 빈 휙, 아래로 이동, 200ms"
- 성취: "밝고 상승하는 아르페지오, 반짝임, 축하하는 분위기, 1초"
Meta AudioCraft (오픈 소스)
최적 대상: 완전한 제어와 API 비용이 없는 개발자.
from audiocraft.models import AudioGen
from audiocraft.data.audio import audio_write
model = AudioGen.get_pretrained('facebook/audiogen-medium')
model.set_generation_params(duration=0.5)
# UI 사운드 생성
descriptions = [
"짧은 디지털 확인 비프, 상승하는 음정, 깨끗함",
"낮은 오류 버즈, 짧고, 거칠지 않음, 디지털",
"부드러운 알림 차임, 두 음, 기분 좋음",
]
wav = model.generate(descriptions)
for i, w in enumerate(wav):
audio_write(f'ui_sound_{i}', w.cpu(), model.sample_rate)
Stable Audio Open
최적 대상: 더 긴 앰비언트 사운드, 배경 질감, 브랜드 사운드.
import torch
from stable_audio_tools import get_pretrained_model
from stable_audio_tools.inference.generation import generate_diffusion_cond
model, model_config = get_pretrained_model("stabilityai/stable-audio-open-1.0")
# 앰비언트 배경 생성
conditioning = [{
"prompt": "차분한 앰비언트 디지털 질감, 따뜻한 낮은 허밍, 생산성 앱 배경에 적합, 반복 가능",
"seconds_total": 10
}]
후처리 체크리스트
AI 생성 사운드는 배포 전 정리가 필요합니다:
- 침묵 자르기 — 앞/뒤 여백 제거
- 볼륨 정규화 — 모든 UI 사운드가 일관된 인지 볼륨을 가져야 함
- 페이드 인/아웃 — 5-10ms 페이드로 클릭 아티팩트 방지
- 다이나믹 레인지 압축 — UI 사운드는 일관된 볼륨을 가져야 함
- WAV로 내보내기(네이티브용) 또는 OGG/MP3(웹용, 사운드당 50KB 미만)
- 대상 스피커에서 테스트 — 폰 스피커는 200Hz 이하 주파수 차단
Web Audio API: 절차적 사운드
웹 앱의 경우 사운드를 절차적으로 생성하면 오디오 파일이 필요 없습니다. 번들이 더 작아지고 즉시 사용 가능합니다.
class UISound {
private ctx: AudioContext | null = null;
private getContext(): AudioContext {
if (!this.ctx) {
this.ctx = new AudioContext();
}
return this.ctx;
}
/** 밝고 상승하는 확인음 */
confirm(): void {
const ctx = this.getContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.type = 'sine';
osc.frequency.setValueAtTime(600, ctx.currentTime);
osc.frequency.linearRampToValueAtTime(900, ctx.currentTime + 0.15);
gain.gain.setValueAtTime(0.15, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.2);
osc.start(ctx.currentTime);
osc.stop(ctx.currentTime + 0.2);
}
/** 낮고 하강하는 오류음 */
error(): void {
const ctx = this.getContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.type = 'triangle';
osc.frequency.setValueAtTime(400, ctx.currentTime);
osc.frequency.linearRampToValueAtTime(200, ctx.currentTime + 0.3);
gain.gain.setValueAtTime(0.12, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.35);
osc.start(ctx.currentTime);
osc.stop(ctx.currentTime + 0.35);
}
/** 토글 및 선택용 미묘한 클릭 */
click(): void {
const ctx = this.getContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.type = 'square';
osc.frequency.setValueAtTime(800, ctx.currentTime);
gain.gain.setValueAtTime(0.08, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.05);
osc.start(ctx.currentTime);
osc.stop(ctx.currentTime + 0.05);
}
/** 3음 알림 멜로디 */
notify(): void {
const ctx = this.getContext();
const notes = [523, 659, 784]; // C5, E5, G5
notes.forEach((freq, i) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.type = 'sine';
osc.frequency.setValueAtTime(freq, ctx.currentTime);
const start = ctx.currentTime + i * 0.12;
gain.gain.setValueAtTime(0, start);
gain.gain.linearRampToValueAtTime(0.12, start + 0.02);
gain.gain.exponentialRampToValueAtTime(0.001, start + 0.15);
osc.start(start);
osc.stop(start + 0.15);
});
}
}
// 사용법
const sound = new UISound();
document.querySelector('#submit')?.addEventListener('click', () => sound.confirm());
햅틱-오디오 페어링 (모바일)
모바일에서 햅틱과 오디오는 하나의 유닛으로 설계되어야 합니다. 햅틱이 먼저 도착하고(즉각적인 물리적 피드백), 사운드가 나중에 도착합니다(확인 및 특성).
페어링 가이드
| 상호작용 | 햅틱 | 사운드 | 타이밍 |
|---|---|---|---|
| 버튼 탭 | 가벼운 충격 | 짧은 클릭 | 동시 |
| 토글 스위치 | 중간 충격 | 선명한 스냅 | 햅틱이 사운드보다 10ms 먼저 |
| 당겨서 새로고침 | 당기는 동안 선택 틱; 완료 시 알림 | 미묘한 늘어나는 소리; 확인 차임 | 햅틱이 20ms 리드 |
| 스와이프 삭제 | 임계값에서 강한 충격 | 텅 빈 휙 | 커밋 지점에서 햅틱 |
| 길게 누르기 | 단단하고 연속적 | 낮게 올라가는 허밍 | 햅틱이 사운드와 함께 시작 |
| 성공 | 성공 패턴(세 번 탭) | 상승하는 아르페지오 | 비트에 맞춤 |
iOS 구현
import UIKit
import AVFoundation
class FeedbackManager {
static let shared = FeedbackManager()
private let lightImpact = UIImpactFeedbackGenerator(style: .light)
private let mediumImpact = UIImpactFeedbackGenerator(style: .medium)
private let heavyImpact = UIImpactFeedbackGenerator(style: .heavy)
private let notificationFeedback = UINotificationFeedbackGenerator()
func tapConfirm() {
lightImpact.impactOccurred()
SoundPlayer.play(.confirm)
}
func errorShake() {
notificationFeedback.notificationOccurred(.error)
SoundPlayer.play(.error)
}
func deleteCommit() {
heavyImpact.impactOccurred()
SoundPlayer.play(.delete)
}
}
만족스러운 사운드의 심리학
어떤 UI 사운드는 '옳게' 느껴지고 다른 사운드는 '싸구려'로 느껴지는 이유:
만족스러운 사운드 특성
| 특성 | 효과 | 예시 |
|---|---|---|
| 음정 상승 | 진행, 낙관, 완료를 신호 | 확인 차임은 올라감 |
| 짧은 감쇠 | 반응성 있고 제어된 느낌 | 닌텐도 메뉴 사운드 |
| 하모닉 풍부함 | 유기적이고 따뜻한 느낌 | Slack의 '노크' 사운드 |
| 일관된 볼륨 | 전문적이고 예측 가능한 느낌 | Apple 시스템 사운드 |
| 리듬 정렬 | 의도적이고 설계된 느낌 | Duolingo 연속 기록 카운터 |
짜증나는 사운드 특성
| 특성 | 이유 | 피해야 할 것 |
|---|---|---|
| 변화 없는 반복 | 인간은 적응한 후 짜증 냄 | 같은 사운드를 하루 100번 |
| 맥락에 비해 너무 큼 | 집중력 깨뜨림 | 갑작스러운 큰 소리 |
| 관련 없는 지속 시간 | 불안감 유발 | 길게 끄는 오류음 |
| 낮은 지각 품질 | 앱 전체가 싸구려로 보임 | 8비트 비프음 (의도하지 않은 경우) |
| 예측 불가능 | 시스템이 결함 있다고 느껴짐 | 무작위 타이밍 또는 음정 |
만족스러운 사운드의 3가지 법칙
- 용도에 맞음: 모든 사운드는 존재 이유가 있어야 함
- 때에 맞음: 사운드는 적절한 순간에만 재생되어야 함
- 정도에 맞음: 절제를 보여주는 앱은 더 신뢰받음
사운드 감사 체크리스트
기존 앱 사운드를 평가할 때:
- 모든 사운드에 의도적인 디자인 선택이 있었는가? 아니면 기본 시스템 사운드인가?
- 사용자가 전역 음소거할 수 있는가?
- 사운드가 시각적 피드백을 대체하는가, 보완하는가?
- 동일한 사운드 유형에 다양한 음정이 있는가? (반복 없음)
- 모든 사운드가 동일한 믹스에서 나오는 것처럼 들리는가? (일관된 음향 공간)
- 중요한 알림을 놓치지 않도록 오류 알림을 테스트했는가?
- 초기 사용자와 숙련된 사용자 모두에게 사운드를 테스트했는가?
- 마케팅 카피처럼 들리지 않는 브랜드 사운드가 있는가?