Banner | CoUI

Banner

페이지 상단에 표시되는 알림 배너 컴포넌트

Banner#

페이지 상단에 중요한 정보, 경고, 오류 등을 표시하는 배너 컴포넌트입니다. Banner는 Flutter와 Web에서 동일한 named properties API를 제공하며, default/info/success/warning/destructive 5개 variant를 지원합니다.

Live Preview#

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 페이지 전체에 영향을 미치는 시스템 상태 메시지를 표시할 때 (점검 공지, 서비스 알림)
  • 사용자가 취해야 할 중요한 액션을 안내할 때 (세션 만료 경고, 구독 만료 알림)
  • 사용자가 방금 완료한 작업의 결과를 페이지 상단에 지속적으로 표시할 때

대신 다른 컴포넌트를 사용하세요:

  • Toast: 일시적으로 나타났다가 자동으로 사라지는 알림이 필요할 때
  • Alert: 특정 콘텐츠 영역 내에 인라인으로 표시하는 경고 메시지가 필요할 때
  • Dialog: 사용자의 즉각적인 응답이 필요한 중요한 알림에는 모달 다이얼로그 사용

기본 사용법 (Basic Usage)#

CoUI는 Flutter와 Web에서 동일한 API를 제공합니다. 아래 예제 코드는 양쪽 플랫폼에서 그대로 사용할 수 있습니다.

// 정보 배너
Banner.info(
  message: Text('시스템 점검이 예정되어 있습니다.'),
)

// 성공 배너 (해제 버튼 포함)
Banner.success(
  message: Text('저장이 완료되었습니다.'),
  onDismiss: handleDismiss,
)

// 경고 배너 (액션 포함)
Banner.warning(
  message: Text('세션이 곧 만료됩니다.'),
  description: Text('지금 연장하면 작업을 이어서 할 수 있습니다.'),
  action: Button(
    variant: CoreButtonVariant.outline,
    onPressed: handleExtendSession,
    child: Text('연장하기'),
  ),
  onDismiss: handleDismiss,
)

// 오류 배너
Banner.destructive(
  message: Text('요청을 처리하는 중 오류가 발생했습니다.'),
  onDismiss: handleDismiss,
)

Props / Parameters#

속성타입기본값설명
message Widget / Component 필수 배너에 표시할 메시지
description Widget? / Component? null 메시지 아래에 표시되는 선택적 보조 설명
variant CoreBannerVariant info 배너 변형 ( default_ , info , success , warning , destructive )
onDismiss VoidCallback? / CoreVoidCallback? null 닫기 버튼 클릭 핸들러. null이면 닫기 버튼 미표시
action Widget? / Component? null 배너 우측에 표시할 액션 위젯
showIcon bool true variant 별 기본 아이콘 표시 여부
bannerStyle CoreBannerStyle? null 인스턴스 스타일 (Style 시스템 참조)

스타일 시스템 (Style System)#

Banner 의 모든 chrome / dimensional / nested-slot 오버라이드는 CoreBannerStyle 단일 슬롯으로 흐릅니다 (Epic #1302 원칙 6/7/8, Phase 6f 팬텀 generic 제거). 시맨틱 enum (variant) 과 behaviour (message / description / action / onDismiss / showIcon) 는 위젯 파라미터로 직접 전달합니다.

시맨틱 vs 스타일#

  • 시맨틱 enum / behaviour: 위젯/컴포넌트 파라미터로 직접 (variant, message, description, action, onDismiss, showIcon)
  • chrome / dimensional / 슬롯 스타일: CoreBannerStyle 한 곳으로 (backgroundColor / foregroundColor / borderColor / borderWidth / borderRadius / padding / iconContentGapStyle / contentDismissGapStyle / titleDescriptionGapStyle / titleStyle / descriptionStyle / iconStyle / actionButtonStyle / closeButtonStyle — 닫기 버튼의 패딩·모서리·아이콘·hover 전환은 전부 nested closeButtonStyle 슬롯)

Resolve chain#

design system default for banner
  → CoreBannerTheme.style                  // 프로젝트 공통
  → parent component slot override
  → widget.bannerStyle                     // 인스턴스별

각 nested 슬롯 스타일 (titleStyle / descriptionStyle / iconStyle / actionButtonStyle / closeButtonStyle) 은 자기 컴포넌트의 자체 resolve chain 으로 다시 한 번 머지됩니다.

슬롯 매핑 (CoreBannerStyle 필드)#

Epic #1696 Phase 3 에서 옛 CoreBannerTokens (raw double 보관 holder) 가 폐기되고, 모든 chrome 이 CoreBannerStyle 슬롯 + static const default* 로 흡수됨.

필드타입Default적용 영역
backgroundColor CoreColor? defaultsByVariant[v].background 배너 배경 색 (variant 기본값 위에 오버라이드)
foregroundColor CoreColor? defaultsByVariant[v].textColor 메시지 / 설명 / 아이콘 전경 색
borderColor CoreColor? defaultsByVariant[v].borderColor 좌측 액센트 보더 색
borderWidth double? defaultBorderWidth (4px) 좌측 보더 두께 (px)
borderRadius CoreBorderRadius? defaultBorderRadius (16px all) 모서리 반지름
padding CoreEdgeInsets? defaultPadding (h=16, v=12) 패딩
iconContentGapStyle CoreGapStyle? defaultIconContentGapStyle (12px) 아이콘 / 콘텐츠 / 액션 간격
contentDismissGapStyle CoreGapStyle? defaultContentDismissGapStyle (8px) 닫기 버튼 앞 gap
titleDescriptionGapStyle CoreGapStyle? defaultTitleDescriptionGapStyle (4px) 메시지 ↔ 설명 수직 gap
titleStyle CoreTextStyle? labelLarge 메시지 텍스트 스타일
descriptionStyle CoreTextStyle? bodySmall 설명 텍스트 스타일
iconStyle CoreIconStyle? (variant icon color, 24px) variant 아이콘 스타일
actionButtonStyle CoreButtonStyle? 액션 버튼 chrome 슬롯
closeButtonStyle CoreButtonStyle? defaultCloseButtonStyle (padding 4px / full round / 150ms / 20px glyph) 닫기 버튼 chrome 슬롯 — Button(variant: .plain) 으로 raw-forward. 옛 평면 dismissIconStyle / dismissButtonPadding / dismissBorderRadius / dismissButtonOpacity / dismissButtonHoverOpacity / transitionDuration 는 이 슬롯으로 흡수 ( padding / borderRadius / leadingIconStyle / foregroundColor · hoverForegroundColor / animationDuration )

Migration — 옛 평면 chrome → 새 위치 매핑#

기존 CoreBannerTheme 의 평면 chrome (backgroundColor / borderColor / borderRadius) 은 호환을 위해 유지되지만, 새 코드는 bannerStyle 슬롯을 사용하세요:

기존 (legacy theme 필드)새 위치
CoreBannerTheme.backgroundColor bannerStyle.backgroundColor 또는 CoreBannerTheme.style.backgroundColor
CoreBannerTheme.borderColor bannerStyle.borderColor 또는 CoreBannerTheme.style.borderColor
CoreBannerTheme.borderRadius bannerStyle.borderRadius 또는 CoreBannerTheme.style.borderRadius
인스턴스 padding 오버라이드 (불가능)bannerStyle.padding
인스턴스 메시지 텍스트 스타일 (불가능)bannerStyle.titleStyle
인스턴스 설명 텍스트 스타일 (불가능)bannerStyle.descriptionStyle
인스턴스 아이콘 사이즈/색 (불가능)bannerStyle.iconStyle
옛 평면 닫기 버튼 chrome ( dismissIconStyle / dismissButtonPadding / dismissBorderRadius / dismissButtonOpacity / dismissButtonHoverOpacity / transitionDuration ) bannerStyle.closeButtonStyle ( leadingIconStyle / padding / borderRadius / foregroundColor · hoverForegroundColor / animationDuration )

사용 예 (Flutter)#

Banner.warning(
  message: Text('세션이 곧 만료됩니다.'),
  description: Text('지금 연장하면 작업을 이어서 할 수 있습니다.'),
  bannerStyle: CoreBannerStyle(
    padding: CoreEdgeInsets.symmetric(horizontal: 24, vertical: 16),
    borderRadius: CoreBorderRadius.all(12),
    titleStyle: CoreTextStyle(fontSize: 14, fontWeight: 600),
    descriptionStyle: CoreTextStyle(fontSize: 12),
    iconStyle: CoreIconStyle(size: 20),
  ),
  onDismiss: handleDismiss,
)

사용 예 (Web)#

Banner.warning(
  message: text('세션이 곧 만료됩니다.'),
  description: text('지금 연장하면 작업을 이어서 할 수 있습니다.'),
  bannerStyle: CoreBannerStyle(
    padding: CoreEdgeInsets.symmetric(horizontal: 24, vertical: 16),
    borderRadius: CoreBorderRadius.all(12),
  ),
  onDismiss: handleDismiss,
)

변형 (Variants)#

Info#

일반적인 정보 전달에 사용합니다.

Banner.info(
  message: Text('새로운 업데이트가 있습니다.'),
)

Success#

성공적인 작업 완료를 알릴 때 사용합니다.

Banner.success(
  message: Text('변경 사항이 저장되었습니다.'),
)

Warning#

사용자의 주의가 필요한 상황에 사용합니다.

Banner.warning(
  message: Text('구독이 7일 후 만료됩니다.'),
)

Destructive (Error)#

오류 또는 실패 상태를 알릴 때 사용합니다.

Banner.destructive(
  message: Text('네트워크 연결에 실패했습니다.'),
)

동작 스펙 (Behavior)#

인터랙션#

  • 닫기 버튼 클릭: onDismiss 콜백 실행. 배너 숨김 처리는 부모 위젯에서 상태로 관리
  • 액션 버튼 클릭: action 위젯에 정의된 콜백 실행
  • 호버: 닫기 버튼 opacity 상승

상태 전환#

  • 배너 표시: 슬라이드 다운 또는 페이드인 (부모에서 AnimatedSwitcher 사용 권장)
  • 배너 닫기: onDismiss 호출 후 부모에서 상태를 변경하여 제거

테마 오버라이드#

프로젝트 레벨에서 CoreComponentTheme.banner로 기본값을 재정의할 수 있습니다.

CoreComponentTheme(
  banner: CoreBannerTheme(
    style: CoreBannerStyle(
      backgroundColor: CoreColor(0xFFF8F9FA),
      borderColor: CoreColor(0xFF6366F1),
      borderRadius: CoreBorderRadius.all(12),
    ),
  ),
)

사용 가이드라인 (Usage Guidelines)#

✅ Do#

중요도에 맞는 variant 사용

// 시스템 점검 공지: info
Banner.info(
  message: Text('2026-03-15 02:00~04:00 시스템 점검이 예정되어 있습니다.'),
)

// 보안 경고: warning
Banner.warning(
  message: Text('비밀번호를 90일 이상 변경하지 않았습니다.'),
  action: TextButton(
    onPressed: handleChangePassword,
    child: Text('변경하기'),
  ),
)

올바른 variant를 사용하면 사용자가 메시지의 중요도를 즉시 파악할 수 있습니다.


❌ Don't#

여러 배너를 동시에 표시 금지

// ❌ 여러 배너 동시 표시
Column(
  children: [
    Banner.info(message: Text('점검 예정')),
    Banner.success(message: Text('저장 완료')),
    Banner.warning(message: Text('세션 만료')),
  ],
)

여러 배너가 동시에 표시되면 사용자에게 압도적인 느낌을 주고 콘텐츠 영역을 지나치게 차지합니다. 가장 중요한 하나만 표시하세요.

✅ Do#

해제 가능한 배너에는 항상 onDismiss 제공

Banner.info(
  message: Text('새로운 기능이 추가되었습니다. 지금 확인해보세요!'),
  onDismiss: handleDismissBanner,
  action: TextButton(
    onPressed: handleLearnMore,
    child: Text('자세히 보기'),
  ),
)

사용자가 불필요한 배너를 닫을 수 있어야 콘텐츠에 집중할 수 있습니다.


❌ Don't#

destructive variant를 일반 정보 표시에 남용 금지

// ❌ 단순 안내에 destructive variant 사용
Banner.destructive(
  message: Text('이 기능은 프리미엄 회원 전용입니다.'),
)

destructive variant는 실제 오류 상황에만 사용해야 합니다. 남용하면 사용자가 진짜 오류를 놓칠 수 있습니다.

✅ Do#

액션이 필요한 배너에는 action 버튼을 추가하세요.

Banner.warning(
  message: Text('구독이 3일 후 만료됩니다.'),
  action: TextButton(
    onPressed: handleRenewSubscription,
    child: Text('갱신하기'),
  ),
  onDismiss: handleDismiss,
)

사용자가 즉각적인 조치를 취할 수 있도록 명확한 액션 버튼을 제공하면 전환율이 높아집니다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
Tab배너 내 인터랙티브 요소(액션 버튼, 닫기 버튼)로 이동
Enter / Space포커스된 버튼 활성화
Escape닫기 버튼이 있는 경우 배너 닫기

스크린 리더#

  • Flutter: Semantics(container: true, label: 'Banner') 적용
  • Web: role="banner" 속성 자동 적용

크로스 플랫폼 차이점 (Platform Differences)#

Banner는 Flutter와 Web에서 동일한 named properties API를 사용합니다. 플랫폼별로 타입이 다른 부분만 차이가 있습니다.

항목FlutterWeb
message / description / action 타입 Widget Component
onDismiss 콜백 타입 VoidCallback? CoreVoidCallback?
배치 방식 Column 최상단 또는 Scaffold 내 삽입 페이지 상단 고정 (position: sticky)
애니메이션AnimatedSwitcher 등 Flutter 애니메이션CSS transition
  • Toast: 자동으로 사라지는 일시적 알림에 사용
  • Alert: 특정 섹션 내 인라인 메시지에 사용
  • Status: 시스템이나 사용자 상태 표시에 사용