
장바구니 담기 버튼을 눌렀을 때 화면 절반을 가리는 모달이 뜨면, 사용자는 본능적으로 "왜 자꾸 방해하냐"고 느낍니다. 저도 직접 겪었습니다. 이커머스 모바일 앱 외주에서 같은 이유로 한 달에 12건의 컴플레인이 들어왔고, 모달을 스낵바로 교체한 뒤 컴플레인이 0건이 됐습니다. 스낵바 하나가 UX 전체를 바꿀 수 있다는 걸 그때 처음으로 실감했습니다.
모달 대신 스낵바를 쓰는 이유
모달(Modal)이란 사용자의 현재 작업을 강제로 중단시키고 확인을 요구하는 전면 팝업 방식입니다. 정보 전달보다 사용자의 행동을 차단하는 쪽에 가깝기 때문에, 단순 확인 메시지에 모달을 쓰면 불필요한 인지 부하가 발생합니다.
반면 스낵바(Snackbar)는 화면 하단에 잠시 떴다가 사라지는 비침입형 알림 패턴입니다. 사용자가 하던 작업을 멈추지 않아도 됩니다. 제가 진행한 외주 프로젝트에서 모달을 스낵바 + Undo 액션으로 바꿨을 때, 의도치 않은 장바구니 담기 후 즉시 취소 비율이 8%에서 11%로 올랐습니다. 수치가 올라간 게 좋은 신호냐고 의아할 수 있는데, 이건 사용자가 "실수해도 되돌릴 수 있다"는 안도감 때문에 오히려 적극적으로 담고 취소하는 패턴이 자리 잡은 겁니다. 사용자 신뢰와 직결되는 지표입니다.
구글의 머티리얼 디자인(Material Design) 가이드라인에서도 스낵바의 표시 시간을 4~10초로 권장하며, 한 번에 하나만 노출하는 단일 큐 원칙을 명시하고 있습니다(출처: Google Material Design). 실무에서도 이 원칙을 그대로 적용하는 게 가장 무난합니다.
컴포넌트 직접 구현하는 방법
라이브러리 없이 순수 HTML, CSS, JavaScript만으로도 완성도 높은 스낵바를 만들 수 있습니다. 제가 실제로 구현해본 방식을 기준으로 설명하겠습니다.
핵심 구조는 세 가지입니다. 먼저 id="toast-box"인 컨테이너 div를 position: absolute, bottom: 30px, right: 30px으로 고정합니다. 여기서 position: absolute란 부모 요소를 기준으로 정확한 위치에 요소를 배치하는 CSS 속성입니다. flex-direction: column으로 설정하면 알림이 쌓일 때 세로로 정렬됩니다.
각 알림 카드는 JavaScript에서 document.createElement('div')로 동적으로 생성하고, classList.add('toast')로 클래스를 부여합니다. 아이콘은 Font Awesome의 i 태그를 innerHTML로 삽입하면 되고, success면 초록, error면 빨강, warning이면 주황으로 각각 다른 클래스를 조건부로 추가합니다.
애니메이션은 두 단계로 구성됩니다. 진입 시에는 translateX(100%)에서 translateX(0)으로 이동하는 CSS 키프레임(keyframe)을 적용합니다. 키프레임이란 애니메이션의 시작과 끝 상태를 정의하는 CSS 규칙으로, 브라우저가 그 사이를 자동으로 보간해 줍니다. 하단 프로그레스 바(progress bar)는 ::after 의사 요소로 구현하고, width: 100%에서 width: 0으로 5초간 줄어드는 keyframes를 별도로 정의합니다. setTimeout으로 6초 후 해당 요소를 remove()하면 알림이 자연스럽게 사라집니다.
이렇게 만든 컴포넌트를 저는 바로 Figma Variants로 옮겼습니다. success, error, warning, info 4종 변형을 만들어 두고, 현재 외주 5건에서 동일한 디자인 토큰(Design Token)으로 재사용 중입니다. 디자인 토큰이란 색상, 간격, 타이포그래피 등 디자인 속성을 변수로 정의해 코드와 디자인 파일 양쪽에서 일관성을 유지하는 방식입니다.
실무에서 가장 까다로운 큐 관리
단순 데모에서는 알림이 하나씩 뜨니까 문제가 없어 보입니다. 그런데 실제 서비스에서는 사용자가 버튼을 빠르게 여러 번 누르거나, 비동기 요청이 동시에 완료되면서 알림이 한꺼번에 쏟아지는 상황이 생깁니다. 이 부분이 제 경험상 스낵바 구현에서 가장 까다로운 지점입니다.
큐잉(Queuing) 전략이란 여러 알림 요청이 동시에 들어왔을 때 순서대로 하나씩 처리하거나, 우선순위에 따라 노출 순서를 정렬하는 방식입니다. 기본적으로 고려해야 할 전략은 다음 세 가지입니다.
- 단일 큐(FIFO): 먼저 들어온 알림이 먼저 노출되고 사라진 뒤 다음 알림이 표시됩니다. 가장 단순하고 예측 가능합니다.
- 우선순위 큐: error는 즉시 치고 올라오고, info는 뒤로 밀리는 방식입니다. 사용자가 놓치면 안 되는 알림을 먼저 보여줄 때 씁니다.
- 중복 방지(Dedup): 동일한 메시지가 이미 떠 있으면 새 알림을 무시하거나 기존 알림의 타이머를 리셋합니다.
React 환경에서는 react-hot-toast 라이브러리가 이 세 가지를 기본으로 지원합니다. 저는 이 라이브러리를 4초 표시, 하단 위치, 1개 액션 디폴트로 래핑한 커스텀 훅을 하나 만들어 두고, 신규 프로젝트마다 그대로 붙여 씁니다. 매번 바닥부터 짜는 것보다 훨씬 일관성이 높습니다.
순수 JavaScript 구현에서는 큐 배열을 전역으로 관리하고, 현재 노출 중인 알림 수를 카운팅해서 한계치(보통 3개)를 초과하면 신규 요청을 배열에 쌓아 두는 방식이 현실적입니다.
한국 환경에서 추가로 챙겨야 할 것들
글로벌 스탠다드를 그대로 따르면 되는 줄 알았는데, 국내 앱을 쓰다 보면 미묘하게 다른 점이 느껴집니다. 특히 토스(Toss)나 카카오페이 같은 핀테크 앱의 알림은 라운드 처리가 훨씬 강하고, 배경에 블러(blur) 효과가 들어갑니다. 제 경험상 이 스타일이 한국 사용자에게는 Material 3 기본 스타일보다 더 친숙하게 느껴지는 것 같습니다.
닐슨노먼그룹(Nielsen Norman Group)의 연구에 따르면 알림 피로(Notification Fatigue)는 사용자가 알림을 무시하거나 아예 꺼버리는 원인이 되며, 불필요한 알림을 줄이는 것이 앱 재사용률과 직결된다고 밝히고 있습니다(출처: Nielsen Norman Group). 알림 피로란 너무 많은 알림에 노출된 사용자가 점차 알림 자체를 무시하게 되는 현상입니다.
한국 시장을 겨냥한 스낵바 가이드라인을 만든다면, 저는 다음 세 가지를 반드시 한 묶음으로 다뤄야 한다고 봅니다.
- 알림 우선순위 4단계 × 표시 형태 5종 매트릭스: 어떤 상황에서 어떤 알림 형태를 쓸지 기준을 명확히 해야 합니다.
- 큐 관리와 중복 방지: 동시 다발 알림 상황에서의 처리 전략을 사전에 정의해 두어야 합니다.
- 다크 모드 컬러 토큰 매핑: 다크 모드에서 success 초록이나 error 빨강이 충분한 대비를 유지하는지 별도로 검증해야 합니다. 라이트 모드 기준으로 고른 색이 다크 배경에서 가독성이 떨어지는 경우가 실무에서 꽤 자주 있습니다.
이 세 가지가 빠진 채 컴포넌트를 만들면, 나중에 디자인 시스템이 커질수록 예외 케이스가 쌓여서 결국 처음부터 다시 정리하게 됩니다. 제가 직접 겪은 일입니다.
스낵바는 작고 단순해 보이지만, 잘 만들어 두면 모든 프로젝트에서 그대로 꺼내 쓸 수 있는 자산이 됩니다. 라이브러리 없는 순수 구현으로 내부 동작 원리를 한 번 파악해 두고, 실무에서는 react-hot-toast처럼 검증된 라이브러리를 적절히 래핑해서 쓰는 방식이 가장 효율적입니다. 큐 관리와 다크 모드 대응까지 고려한 컴포넌트를 하나 잘 만들어 두면, 그게 곧 여러분만의 디자인 시스템의 첫 벽돌이 됩니다.