디자인으로서의 침묵

Silence as design

소리를 넣지 않기로 하는 의도적 선택. 모든 이벤트가 소리를 내면 결국 아무것도 안 들립니다.

다른 이름: Sound restraintNotification fatigue
···
html
<div class="wrap">
  <div class="col">
    <div class="cap">알림 폭탄 → <b>소리 남발</b></div>
    <div class="list" id="loud">
      <div class="item"><span class="skel"></span><span class="wave"></span></div>
      <div class="item"><span class="skel"></span><span class="wave"></span></div>
      <div class="item"><span class="skel"></span><span class="wave"></span></div>
    </div>
    <button class="play" id="playLoud" type="button" aria-label="play noisy sound"><span class="tri"></span></button>
  </div>
  <div class="col">
    <div class="cap">침묵 설계 → <b>배지만</b></div>
    <div class="list" id="quiet">
      <div class="item"><span class="skel"></span><span class="badge"></span></div>
      <div class="item"><span class="skel"></span><span class="badge"></span></div>
      <div class="item"><span class="skel"></span><span class="badge"></span></div>
    </div>
    <button class="play" id="playQuiet" type="button" aria-label="trigger silent badge"><span class="mute"></span></button>
  </div>
</div>
css
.wrap{position:relative;width:96%;height:88%;display:flex;gap:5%}
.col{position:relative;flex:1;border:1px solid var(--line);border-radius:12px;background:var(--surface);padding:8px;display:flex;flex-direction:column;gap:6px}
.cap{font:600 10px/1.3 monospace;color:var(--muted)}
.cap b{color:var(--fg)}
.list{display:flex;flex-direction:column;gap:6px;flex:1}
.item{position:relative;height:22%;border-radius:8px;background:var(--bg);border:1px solid var(--line);display:flex;align-items:center;padding:0 30px 0 10px}
.skel{display:block;width:60%;height:20%;border-radius:3px;background:var(--line);opacity:.6}
.wave{position:absolute;top:50%;right:10px;transform:translateY(-50%);width:8px;height:8px;opacity:0}
.wave::before,.wave::after{content:'';position:absolute;border:1.5px solid var(--accent-2);border-radius:50%;inset:0}
.wave::after{inset:-4px}
.item.go .wave{animation:pop .5s ease-out}
@keyframes pop{0%{opacity:.9;transform:translateY(-50%) scale(.6)}100%{opacity:0;transform:translateY(-50%) scale(1.3)}}
.badge{position:absolute;top:6px;right:6px;width:8px;height:8px;border-radius:50%;background:var(--line)}
.item.go .badge{background:var(--accent-3);animation:badgepulse .5s ease-out}
@keyframes badgepulse{0%{transform:scale(1.6)}100%{transform:scale(1)}}
.play{align-self:flex-end;width:26px;height:26px;border-radius:50%;border:1px solid var(--line);background:var(--bg);display:grid;place-items:center;cursor:pointer}
.play .tri{width:0;height:0;border-style:solid;border-width:5px 0 5px 8px;border-color:transparent transparent transparent var(--fg);margin-left:1px}
.play .mute{width:8px;height:8px;border-radius:50%;background:var(--fg)}
.play:active{transform:scale(.92)}
@media (min-width:460px){
  .col{padding:16px}
  .cap{font-size:14px}
  .item{border-radius:12px}
  .skel{height:16%}
  .play{width:40px;height:40px}
  .play .tri{border-width:8px 0 8px 12px;margin-left:2px}
  .play .mute{width:12px;height:12px}
}
js
const loudItems = document.querySelectorAll('#loud .item');
const quietItems = document.querySelectorAll('#quiet .item');
let idx = 0;
setInterval(() => {
  loudItems.forEach((it) => it.classList.remove('go'));
  quietItems.forEach((it) => it.classList.remove('go'));
  loudItems[idx].classList.add('go');
  quietItems[idx].classList.add('go');
  idx = (idx + 1) % loudItems.length;
}, 900);
function beep(ctx, t, freq) {
  const osc = ctx.createOscillator();
  const gain = ctx.createGain();
  osc.type = 'square';
  osc.frequency.value = freq;
  gain.gain.setValueAtTime(0, t);
  gain.gain.linearRampToValueAtTime(0.08, t + 0.008);
  gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.09);
  osc.connect(gain).connect(ctx.destination);
  osc.start(t);
  osc.stop(t + 0.1);
}
document.getElementById('playLoud').addEventListener('click', () => {
  const ctx = new (window.AudioContext || window.webkitAudioContext)();
  const now = ctx.currentTime;
  [880, 990, 830, 1040].forEach((f, i) => beep(ctx, now + i * 0.11, f));
  loudItems.forEach((it) => { it.classList.remove('go'); void it.offsetWidth; it.classList.add('go'); });
  setTimeout(() => ctx.close(), 700);
});
document.getElementById('playQuiet').addEventListener('click', () => {
  // 의도적으로 소리를 재생하지 않는다 — 이게 이 항목의 핵심이다.
  quietItems.forEach((it) => { it.classList.remove('go'); void it.offsetWidth; it.classList.add('go'); });
});

사운드 디자인에서 가장 어려운 결정은 종종 "이 이벤트에 소리를 넣을까"가 아니라 "넣지 않을까"입니다. 알림마다 소리가 울리면 사용자는 빠르게 전부 음소거하거나 앱을 지워버립니다 — 실제로 스마트폰 사용자 대다수가 대부분의 앱 알림음을 꺼둔 상태로 지냅니다. 침묵은 결핍이 아니라 "이건 지금 당장 신경 쓸 일이 아니다"라는 신호를 주는 적극적인 설계 도구입니다.

이벤트를 소리로 표시하기 전에 "이게 사용자의 주의를 지금 당장 끌 만큼 중요한가"를 먼저 물어야 합니다. 중요하지 않은 상태 변화(좋아요 수 갱신, 백그라운드 동기화 완료)는 조용한 배지·색 변화 같은 시각 신호만으로 충분합니다. 정말 중요한 소리(결제 실패, 보안 경고)의 효과는 평소에 소리를 아껴야만 유지됩니다 — 늘 울리던 알림음 속에서는 진짜 위급 신호도 묻힙니다.

알림 설정의 기본값 설계, 메시지 앱의 "무음 대화" 기능, 백그라운드 작업 완료 표시, 회의 중 방해금지 모드에 쓰입니다.

언제 쓰나

이벤트에 소리를 넣기 전에 "지금 당장 신경 쓸 일인가"를 먼저 물으세요. 아니라면 시각 신호만으로 충분합니다.