Toasts
Toasts (토스트)
가볍고 쉽게 커스터마이즈할 수 있는 알림 메시지인 toast로 방문자에게 푸시 알림을 보내는 방법을 다룹니다.
출처: 문서
본문
Toast는 모바일과 데스크톱 운영체제에서 대중화된 푸시 알림을 흉내 내도록 설계된 가벼운 알림입니다. flexbox로 만들어져 정렬과 위치 지정이 쉽습니다.
개요 (Overview)
toast 플러그인을 사용할 때 알아둘 것들:
- 성능상의 이유로 toast는 opt-in 방식이므로 직접 초기화해야 합니다.
autohide: false를 지정하지 않으면 toast는 자동으로 숨겨집니다.
이 컴포넌트의 애니메이션 효과는
prefers-reduced-motion미디어 쿼리에 의존합니다. 접근성 문서의 reduced motion 섹션을 참고하세요.
예시 (Examples)
기본 (Basic)
확장 가능하고 예측 가능한 toast를 권장하기 위해 헤더와 본문을 추천합니다. Toast 헤더는 display: flex를 사용하므로 마진과 flexbox 유틸리티 덕분에 콘텐츠를 쉽게 정렬할 수 있습니다.
Toast는 필요에 따라 유연하며 필요한 마크업이 매우 적습니다. 최소한 "toasted" 콘텐츠를 담을 단일 요소가 필요하며, 닫기 버튼을 강력히 권장합니다.
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
이전에는 스크립트가 .hide 클래스를 동적으로 추가해 toast를 완전히 숨겼습니다(opacity:0만이 아니라 display:none으로). 지금은 더 이상 필요하지 않습니다. 하지만 하위 호환성을 위해 스크립트는 다음 메이저 버전까지 계속 클래스를 토글합니다(실질적인 필요는 없지만).
라이브 예시 (Live example)
아래 버튼을 클릭하면 기본적으로 숨겨져 있는 toast(우측 하단에 유틸리티로 배치)를 표시합니다.
<button type="button" class="btn btn-primary" id="liveToastBtn">Show live toast</button>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
</div>
라이브 toast 데모를 트리거하기 위해 다음 JavaScript를 사용합니다:
const toastTrigger = document.getElementById('liveToastBtn')
const toastLiveExample = document.getElementById('liveToast')
if (toastTrigger) {
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample)
toastTrigger.addEventListener('click', () => {
toastBootstrap.show()
})
}
반투명 (Translucent)
Toast는 아래에 있는 것들과 어울리도록 약간 반투명합니다.
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small class="text-body-secondary">11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
쌓기 (Stacking)
toast 컨테이너로 toast를 감싸면 세로로 간격을 추가하며 toast를 쌓을 수 있습니다.
<div class="toast-container position-static">
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small class="text-body-secondary">just now</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
See? Just like this.
</div>
</div>
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small class="text-body-secondary">2 seconds ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Heads up, toasts will stack automatically
</div>
</div>
</div>
커스텀 콘텐츠 (Custom content)
하위 컴포넌트를 제거하거나, 유틸리티로 조정하거나, 자신의 마크업을 추가해 toast를 커스터마이즈하세요. 여기서는 기본 .toast-header를 제거하고 Bootstrap Icons의 커스텀 숨기기 아이콘을 추가하고 몇 가지 flexbox 유틸리티를 사용해 레이아웃을 조정해 더 단순한 toast를 만들었습니다.
<div class="toast align-items-center" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
Hello, world! This is a toast message.
</div>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
또는 toast에 추가 컨트롤과 컴포넌트를 추가할 수도 있습니다.
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-body">
Hello, world! This is a toast message.
<div class="mt-2 pt-2 border-top">
<button type="button" class="btn btn-primary btn-sm">Take action</button>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="toast">Close</button>
</div>
</div>
</div>
색상 구성 (Color schemes)
위 예시를 바탕으로 색상과 배경 유틸리티로 다양한 toast 색상 구성을 만들 수 있습니다. 여기서 .toast에 .text-bg-primary를, 닫기 버튼에 .btn-close-white를 추가했습니다. 날카로운 모서리를 위해 .border-0으로 기본 테두리를 제거했습니다.
<div class="toast align-items-center text-bg-primary border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
Hello, world! This is a toast message.
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
배치 (Placement)
필요할 때 커스텀 CSS로 toast를 배치하세요. 오른쪽 위는 알림에 자주 쓰이고, 위 중앙도 그렇습니다. 한 번에 하나의 toast만 보여주려면 위치 스타일을 바로 .toast에 넣으세요.
<form>
<div class="mb-3">
<label for="selectToastPlacement">Toast placement</label>
<select class="form-select mt-2" id="selectToastPlacement">
<option value="" selected>Select a position...</option>
<option value="top-0 start-0">Top left</option>
<option value="top-0 start-50 translate-middle-x">Top center</option>
<option value="top-0 end-0">Top right</option>
<option value="top-50 start-0 translate-middle-y">Middle left</option>
<option value="top-50 start-50 translate-middle">Middle center</option>
<option value="top-50 end-0 translate-middle-y">Middle right</option>
<option value="bottom-0 start-0">Bottom left</option>
<option value="bottom-0 start-50 translate-middle-x">Bottom center</option>
<option value="bottom-0 end-0">Bottom right</option>
</select>
</div>
</form>
<div aria-live="polite" aria-atomic="true" class="bg-body-secondary position-relative bd-example-toasts rounded-3">
<div class="toast-container p-3" id="toastPlacement">
<div class="toast">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
</div>
</div>
더 많은 알림을 생성하는 시스템에서는 쉽게 쌓을 수 있도록 래핑 요소를 사용하는 것을 고려하세요.
<div aria-live="polite" aria-atomic="true" class="position-relative">
<!-- Position it: -->
<!-- - `.toast-container` for spacing between toasts -->
<!-- - `top-0` & `end-0` to position the toasts in the upper right corner -->
<!-- - `.p-3` to prevent the toasts from sticking to the edge of the container -->
<div class="toast-container top-0 end-0 p-3">
<!-- Then put toasts within -->
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small class="text-body-secondary">just now</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
See? Just like this.
</div>
</div>
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small class="text-body-secondary">2 seconds ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Heads up, toasts will stack automatically
</div>
</div>
</div>
</div>
flexbox 유틸리티로 toast를 가로 및/또는 세로로 정렬하는 멋진 효과도 낼 수 있습니다.
<!-- Flexbox container for aligning the toasts -->
<div aria-live="polite" aria-atomic="true" class="d-flex justify-content-center align-items-center w-100">
<!-- Then put toasts within -->
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
</div>
접근성 (Accessibility)
Toast는 방문자나 사용자에게 작은 방해가 되도록 의도된 것이므로, 스크린 리더 및 유사한 보조 기술을 사용하는 사람들을 돕기 위해 toast를 aria-live 영역으로 감싸야 합니다. 라이브 영역의 변경(toast 컴포넌트 주입/업데이트 등)은 사용자의 포커스를 움직이거나 방해하지 않고 스크린 리더가 자동으로 알려줍니다. 또한 aria-atomic="true"를 포함해 toast 전체가 항상 단일(원자적) 단위로 알려지도록 하세요. 변경된 것만 알리지 않으면(예: toast 콘텐츠의 일부만 업데이트하거나 나중에 같은 toast 콘텐츠를 표시할 때) 문제가 생길 수 있기 때문입니다. 필요한 정보가 프로세스에 중요한 경우(예: 폼의 오류 목록)에는 toast 대신 alert 컴포넌트를 사용하세요.
라이브 영역은 toast가 생성되거나 업데이트되기 전에 마크업에 존재해야 합니다. 둘 다 동시에 동적으로 생성해 페이지에 주입하면 일반적으로 보조 기술이 알려주지 않습니다.
또한 콘텐츠에 따라 role과 aria-live 수준을 조정해야 합니다. 오류 같은 중요한 메시지라면 role="alert" aria-live="assertive"를, 그렇지 않으면 role="status" aria-live="polite" 속성을 사용하세요.
표시하는 콘텐츠가 변경됨에 따라 사용자가 toast를 읽을 충분한 시간을 갖도록 delay 타임아웃을 업데이트해야 합니다.
<div class="toast" role="alert" aria-live="polite" aria-atomic="true" data-bs-delay="10000">
<div role="alert" aria-live="assertive" aria-atomic="true">...</div>
</div>
autohide: false를 사용할 때는 사용자가 toast를 닫을 수 있도록 닫기 버튼을 추가해야 합니다.
<div role="alert" aria-live="assertive" aria-atomic="true" class="toast" data-bs-autohide="false">
<div class="toast-header">
<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
기술적으로는 toast에 포커스 가능/동작 가능한 컨트롤(추가 버튼이나 링크 같은)을 추가하는 것이 가능하지만, 자동 숨김 toast에서는 피해야 합니다. toast에 긴 delay 타임아웃을 줘도, 키보드 및 보조 기술 사용자는 toast가 표시될 때 포커스를 받지 않기 때문에 시간 안에 닿아서 조치를 취하기 어려울 수 있습니다. 추가 컨트롤이 꼭 필요하다면 autohide: false를 사용한 toast를 권장합니다.
CSS
변수 (Variables)
v5.2.0에서 추가됨
Bootstrap의 진화하는 CSS 변수 접근 방식의 일부로, toast는 이제 향상된 실시간 커스터마이즈를 위해 .toast에 로컬 CSS 변수를 사용합니다. CSS 변수의 값은 Sass로 설정되므로 Sass 커스터마이즈도 여전히 지원됩니다.
--#{$prefix}toast-zindex: #{$zindex-toast};
--#{$prefix}toast-padding-x: #{$toast-padding-x};
--#{$prefix}toast-padding-y: #{$toast-padding-y};
--#{$prefix}toast-spacing: #{$toast-spacing};
--#{$prefix}toast-max-width: #{$toast-max-width};
@include rfs($toast-font-size, --#{$prefix}toast-font-size);
--#{$prefix}toast-color: #{$toast-color};
--#{$prefix}toast-bg: #{$toast-background-color};
--#{$prefix}toast-border-width: #{$toast-border-width};
--#{$prefix}toast-border-color: #{$toast-border-color};
--#{$prefix}toast-border-radius: #{$toast-border-radius};
--#{$prefix}toast-box-shadow: #{$toast-box-shadow};
--#{$prefix}toast-header-color: #{$toast-header-color};
--#{$prefix}toast-header-bg: #{$toast-header-background-color};
--#{$prefix}toast-header-border-color: #{$toast-header-border-color};
Sass 변수
$toast-max-width: 350px;
$toast-padding-x: .75rem;
$toast-padding-y: .5rem;
$toast-font-size: .875rem;
$toast-color: null;
$toast-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85);
$toast-border-width: var(--#{$prefix}border-width);
$toast-border-color: var(--#{$prefix}border-color-translucent);
$toast-border-radius: var(--#{$prefix}border-radius);
$toast-box-shadow: var(--#{$prefix}box-shadow);
$toast-spacing: $container-padding-x;
$toast-header-color: var(--#{$prefix}secondary-color);
$toast-header-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85);
$toast-header-border-color: $toast-border-color;
사용법 (Usage)
JavaScript로 toast를 초기화하세요:
const toastElList = document.querySelectorAll('.toast')
const toastList = [...toastElList].map(toastEl => new bootstrap.Toast(toastEl, option))
트리거 (Triggers)
닫기는 아래처럼 toast 안의 버튼에 data-bs-dismiss 속성을 사용해 할 수 있습니다:
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
또는 아래처럼 추가 data-bs-target을 사용해 toast 밖의 버튼에서도 할 수 있습니다:
<button type="button" class="btn-close" data-bs-dismiss="toast" data-bs-target="#my-toast" aria-label="Close"></button>
옵션 (Options)
옵션은 data 속성이나 JavaScript로 전달할 수 있으므로, data-bs-animation="{value}"처럼 data-bs-에 옵션 이름을 붙일 수 있습니다. data 속성으로 옵션을 전달할 때는 옵션 이름의 대소문자 형태를 "camelCase"에서 "kebab-case"로 바꿔야 합니다. 예를 들어 data-bs-customClass="beautifier" 대신 data-bs-custom-class="beautifier"를 사용하세요.
Bootstrap 5.2.0부터 모든 컴포넌트는 JSON 문자열로 간단한 컴포넌트 설정을 담을 수 있는 실험적인 예약 data 속성 data-bs-config를 지원합니다. 요소에 data-bs-config='{"delay":0, "title":123}'과 data-bs-title="456" 속성이 있으면 최종 title 값은 456이 되고, 별도의 data 속성이 data-bs-config에 주어진 값을 덮어씁니다. 또한 기존 data 속성도 data-bs-delay='{"show":0,"hide":150}'처럼 JSON 값을 담을 수 있습니다.
최종 설정 객체는 data-bs-config, data-bs-, js object를 병합한 결과이며, 가장 마지막에 주어진 키-값이 나머지를 덮어씁니다.
| 이름 | 타입 | 기본값 | 설명 |
|---|---|---|---|
animation |
boolean | true |
toast에 CSS fade 전환을 적용합니다. |
autohide |
boolean | true |
delay 후 toast를 자동으로 숨깁니다. |
delay |
number | 5000 |
toast를 숨기기 전 지연 시간(밀리초)입니다. |
메서드 (Methods)
모든 API 메서드는 비동기이며 전환을 시작합니다. 전환이 시작되는 즉시 호출자에게 반환되지만, 끝나기 전에 반환됩니다. 또한 전환 중인 컴포넌트에 대한 메서드 호출은 무시됩니다. JavaScript 문서에서 자세히 알아보세요.
| 메서드 | 설명 |
|---|---|
dispose |
요소의 toast를 숨깁니다. toast는 DOM에 남아 있지만 더 이상 표시되지 않습니다. |
getInstance |
DOM 요소와 연결된 toast 인스턴스를 가져올 수 있게 해 주는 정적 메서드입니다. 예: const myToastEl = document.getElementById('myToastEl') const myToast = bootstrap.Toast.getInstance(myToastEl) — Bootstrap toast 인스턴스를 반환합니다. |
getOrCreateInstance |
DOM 요소와 연결된 toast 인스턴스를 가져오거나, 초기화되지 않은 경우 새로 만드는 정적 메서드입니다. const myToastEl = document.getElementById('myToastEl') const myToast = bootstrap.Toast.getOrCreateInstance(myToastEl) — Bootstrap toast 인스턴스를 반환합니다. |
hide |
요소의 toast를 숨깁니다. toast가 실제로 숨겨지기 전에(hidden.bs.toast 이벤트 전에) 호출자에게 반환합니다. autohide를 false로 만들었다면 이 메서드를 수동으로 호출해야 합니다. |
isShown |
toast의 표시 상태에 따라 boolean을 반환합니다. |
show |
요소의 toast를 표시합니다. toast가 실제로 표시되기 전에(shown.bs.toast 이벤트 전에) 호출자에게 반환합니다. 이 메서드를 수동으로 호출해야 합니다. 그렇지 않으면 toast가 표시되지 않습니다. |
이벤트 (Events)
| 이벤트 | 설명 |
|---|---|
hide.bs.toast |
hide 인스턴스 메서드가 호출될 때 즉시 발생하는 이벤트입니다. |
hidden.bs.toast |
toast가 사용자에게 숨겨지는 것을 마쳤을 때 발생하는 이벤트입니다. |
show.bs.toast |
show 인스턴스 메서드가 호출될 때 즉시 발생하는 이벤트입니다. |
shown.bs.toast |
toast가 사용자에게 보이게 되었을 때 발생하는 이벤트입니다. |
const myToastEl = document.getElementById('myToast')
myToastEl.addEventListener('hidden.bs.toast', () => {
// do something...
})