두 파이프라인 — REPL 내 토스터 큐와 OS 레벨 터미널 알림.
Claude Code의 알림 시스템은 두 개의 독립적인 파이프라인으로 구성됩니다. 하나는 REPL 내부에서 동작하는 토스트(Toast) 큐이고, 다른 하나는 운영체제 레벨의 터미널 알림입니다. 이 두 파이프라인은 서로 다른 사용 사례를 지원하며, 사용자 주의를 효과적으로 관리합니다.
notifications/toastQueue.ts → notifications/osNotifier.ts → hooks/useNotification.ts → notifications/channels.ts
핵심 설계 원칙:
Toast Queue는 REPL 내부에 표시되는 일시적 알림 메시지를 관리합니다. 새 알림이 추가되면 우선순위에 따라 정렬되고, 지정된 시간 후 자동으로 사라집니다.
// Toast Queue — 우선순위 정렬과 자동 소멸
interface Toast {
id: string
message: string
priority: number // 높을수록 먼저 표시
duration: number // ms 단위 표시 시간
foldable: boolean // 접기 가능 여부
category: string // fold 그룹 키
}
class ToastQueue {
private queue: Toast[] = []
push(toast: Toast): void {
this.queue.push(toast)
this.queue.sort((a, b) => b.priority - a.priority)
this.fold() // 같은 카테고리 알림 접기
this.scheduleExpiry(toast)
}
}
각 토스트에는 우선순위가 할당됩니다. 에러 알림은 높은 우선순위를, 정보성 알림은 낮은 우선순위를 가집니다. 큐는 항상 우선순위 내림차순으로 정렬되어 가장 중요한 알림이 먼저 표시됩니다.
같은 카테고리의 알림이 여러 개 쌓이면 접기(fold)가 적용됩니다. 예를 들어 "파일 저장 완료" 알림이 5개 연속으로 들어오면 "파일 저장 완료 (x5)"로 접혀서 표시됩니다. 이를 통해 반복적인 알림이 화면을 가리는 것을 방지합니다.
// Fold 메커니즘 — 같은 카테고리 알림 접기
private fold(): void {
const groups = groupBy(this.queue, t => t.category)
for (const [category, toasts] of groups) {
if (toasts.length > 1 && toasts[0].foldable) {
const folded = {
...toasts[0],
message: `${toasts[0].message} (x${toasts.length})`,
id: foldedId(category)
}
this.queue = this.queue.filter(t => t.category !== category)
this.queue.push(folded)
}
}
}
알림 시스템은 14개의 React 훅을 제공하여 다양한 컴포넌트에서 알림을 쉽게 사용할 수 있습니다. 각 훅은 특정 사용 사례에 최적화되어 있습니다.
useToast() — 기본 토스트 알림 표시useErrorToast() — 에러 메시지를 높은 우선순위로 표시useSuccessToast() — 성공 알림을 잠깐 표시useProgressToast() — 진행률 표시가 포함된 알림useOsNotification() — OS 레벨 알림 발송useNotificationPreferences() — 사용자 알림 설정 관리useToastDismiss() — 프로그래밍 방식으로 토스트 해제useBackgroundTaskToast() — 백그라운드 작업 상태 알림useMcpNotification() — MCP 채널 알림 처리useNotificationQueue() — 큐 상태 구독useNotificationHistory() — 알림 이력 조회useNotificationBadge() — 미확인 알림 뱃지 카운트useNotificationSound() — 알림 사운드 제어useNotificationChannel() — 채널별 알림 필터링터미널 에뮬레이터마다 알림을 지원하는 방식이 다릅니다. OS Notifier는 현재 터미널을 감지하고 최적의 채널을 선택합니다.
// OS 알림 채널 선택 로직
function selectNotificationChannel(): NotificationChannel {
if (isITerm2()) return 'osc9' // iTerm2 OSC 9 프로토콜
if (isKitty()) return 'kitty' // Kitty 전용 프로토콜
if (isGhostty()) return 'ghostty' // Ghostty 전용 프로토콜
return 'bel' // 범용 BEL (0x07) 폴백
}
\x1b]9;알림 메시지\x07 — 제목과 본문을 포함하는 리치 알림\x07 — 모든 터미널에서 지원하는 최소한의 벨 소리 알림장시간 실행되는 작업(파일 인덱싱, MCP 서버 연결 등)은 토스트가 축소되어 상태 표시줄에 작은 인디케이터로 표시됩니다. 작업이 완료되면 OS 알림이 발송되어 사용자가 터미널 밖에 있어도 알 수 있습니다.
MCP(Model Context Protocol) 서버에서 오는 알림은 6단계 보안 검증을 거칩니다:
Q1. Toast Queue의 fold 메커니즘이 해결하는 문제는 무엇인가요?
Q2. OS 알림에서 BEL 채널이 폴백으로 사용되는 이유는?
\x07)은 가장 기본적인 터미널 알림으로, 거의 모든 터미널 에뮬레이터에서 지원됩니다. iTerm2 OSC9, Kitty, Ghostty 전용 프로토콜을 사용할 수 없는 터미널에서도 최소한의 알림을 제공합니다.Q3. 두 알림 파이프라인(Toast Queue, OS Notifier)이 분리된 이유는?
Q4. MCP 채널 알림의 6레이어 보안에서 "콘텐츠 필터링"이 하는 일은?
Q5. 백그라운드 태스크의 토스트가 축소되는 시점은 언제인가요?