ConfirmActionDialog
대상, 영향, 복구 가능성을 포함해 중요한 행동을 확인합니다.
목적
ConfirmActionDialog는 사용자가 실행 결과를 다시 확인해야 하는 행동을 보호한다. 삭제처럼 되돌릴 수 없는 행동뿐 아니라, 현재 작업을 잃거나 운영 상태가 바뀌는 행동에도 사용할 수 있다. 대상, 결과, 다음 행동을 한 화면에서 설명하며 브라우저 기본 확인창을 대신한다.
선택 기준
- 실행 즉시 데이터, 권한, 공개 상태 또는 진행 중인 작업이 바뀌면 사용한다.
- 사용자가 이미 같은 내용을 명시적으로 확인한 직후에는 반복해서 열지 않는다.
- 단순 성공 알림은 toast나 inline feedback을 사용한다.
- 사용자의 선택 없이 알려주기만 하면 되는 내용은 일반
Dialog또는 페이지 안의 안내를 검토한다. - 파괴 행동이 아니면
tone="default"를 유지한다.
구조
title: 사용자가 결정해야 하는 행동을 짧게 말한다.description: 대상, 영향, 되돌릴 수 있는지를 설명한다.children: 추가 경고, 대상 요약, 입력 확인처럼 꼭 필요한 내용을 넣는다.- 취소 버튼: 현재 상태를 유지한다.
- 확인 버튼: 실행 결과가 드러나는 레이블을 사용한다.
변형과 크기
tone="default": 저장하지 않고 이동, 상태 변경처럼 파괴가 아닌 확인에 사용한다.tone="destructive": 삭제, 연결 해제, 영구 폐기처럼 복구가 어렵거나 불가능한 행동에만 사용한다.- 콘텐츠 영역은 화면 높이의 90% 안에서 스크롤된다. 임의의 크기 prop은 제공하지 않는다.
- 확인·취소 버튼은 최소 40px 높이를 유지한다.
상태와 동작
open과onOpenChange를 함께 사용해 표시 상태를 소비 제품이 소유한다.- 확인 버튼은 클릭만으로 자동 닫히지 않는다.
onConfirm에서 작업을 시작하고, 성공한 뒤onOpenChange(false)를 호출한다. isPending동안 두 버튼이 비활성화되고 외부 동작으로 닫히지 않는다.confirmDisabled는 확인 조건을 충족하지 못했을 때 사용한다. 다이얼로그 안에서 이유를 함께 설명한다.pendingLabel이 없으면 처리 중에도confirmLabel을 유지한다.- 작업 실패 시 다이얼로그를 유지하고
children또는 인접한 상태 영역에 복구 방법을 보여준다.
콘텐츠
- 제목은
이 연결을 해제할까요?처럼 결정 대상을 포함한다. - 설명에는 사용자가 잃는 것, 유지되는 것, 복구 방법을 우선순위대로 쓴다.
- 확인 레이블은
해제,확인보다연결 해제,초안 없이 나가기처럼 결과를 말한다. - 처리 중 레이블은
연결 해제 중…처럼 현재 작업을 유지한다. - 위험을 과장하거나 사용자를 탓하지 않는다.
접근성
- 제목과 설명은 alert dialog의 접근 가능한 이름과 설명으로 연결된다.
aria-busy가 확인 버튼의 처리 중 상태를 전달한다.- 추가 경고가 확인 판단에 필수라면 해당 요소의 ID를
confirmAriaDescribedBy에 전달한다. returnFocusRef를 제공하면 닫힌 뒤 연결된 요소로 초점이 돌아간다. 목록에서 행이 사라지는 경우처럼 trigger가 제거될 수 있으면 안전하게 남아 있는 요소를 지정한다.- 색상만으로 위험을 전달하지 말고 제목과 설명에 결과를 명시한다.
좁은 화면과 긴 콘텐츠
- 버튼은 좁은 화면에서 세로 흐름이 될 수 있으므로 레이블만으로 각 행동을 구분할 수 있어야 한다.
- 긴 설명을 넣을 수 있지만 정책 전문이나 대량 데이터를 다이얼로그에 넣지 않는다. 상세 검토가 필요하면 별도 페이지나 sheet로 이동한다.
- 긴 대상명은 줄바꿈을 허용하고 중요한 식별자를 문장 앞부분에 둔다.
- 스크롤이 생겨도 제목, 설명, 행동의 의미가 유지되어야 한다.
공개 API
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
open | boolean | 필수 | 다이얼로그 표시 여부 |
title | string | 필수 | 결정 제목 |
description | string | 필수 | 결과와 영향 설명 |
confirmLabel | string | 필수 | 확인 행동 레이블 |
cancelLabel | string | '취소' | 현재 상태를 유지하는 행동 레이블 |
pendingLabel | string | confirmLabel | 처리 중 레이블 |
tone | 'default' | 'destructive' | 'default' | 확인 행동의 의미 톤 |
isPending | boolean | false | 비동기 작업 진행 여부 |
confirmDisabled | boolean | false | 확인 행동 비활성 여부 |
confirmAriaDescribedBy | string | 없음 | 확인 버튼에 추가로 연결할 설명 ID |
returnFocusRef | RefObject<HTMLElement | null> | 없음 | 닫힌 뒤 초점을 돌려보낼 요소 |
children | ReactNode | 없음 | 추가 경고 또는 대상 요약 |
onOpenChange | (open: boolean) => void | 필수 | 표시 상태 변경 요청 |
onConfirm | () => void | 필수 | 확인 행동 실행 |
예제
import { useRef, useState } from 'react';
import { Button, ConfirmActionDialog } from '@classum/dot-design-system/ui';
export function DisconnectAction({ onDisconnect }: { onDisconnect: () => Promise<void> }) {
const triggerRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(false);
const [pending, setPending] = useState(false);
async function disconnect() {
setPending(true);
try {
await onDisconnect();
setOpen(false);
} finally {
setPending(false);
}
}
return (
<>
<Button ref={triggerRef} variant="outline" onClick={() => setOpen(true)}>
워크스페이스 연결 해제
</Button>
<ConfirmActionDialog
open={open}
title="워크스페이스 연결을 해제할까요?"
description="자동 동기화가 중단돼요. 저장된 기록은 삭제되지 않아요."
confirmLabel="연결 해제"
pendingLabel="연결 해제 중…"
tone="destructive"
isPending={pending}
returnFocusRef={triggerRef}
onOpenChange={setOpen}
onConfirm={disconnect}
/>
</>
);
}피해야 할 사용
- 성공 사실을 알리기 위해 확인 버튼 하나만 둔 다이얼로그
- 모든 저장과 이동에 습관적으로 확인을 요구하는 흐름
- 파괴 행동이 아닌데 destructive tone을 사용하는 경우
isPending없이 중복 실행을 허용하는 비동기 확인- 실패했는데 다이얼로그를 먼저 닫아 작업 맥락을 잃게 하는 처리
확인,진행처럼 결과를 예측할 수 없는 레이블