Command
키보드 중심의 빠른 검색과 명령 선택 경험을 만듭니다.
목적
Command는 사용자가 많은 명령이나 대상 중에서 검색하고 하나를 빠르게 선택하도록 돕는다. 복잡한 기능을 한꺼번에 펼치지 않고 현재 입력과 일치하는 다음 행동을 점진적으로 보여주며, 선택 전후의 작업 맥락을 잇는다.
선택 기준
- 이름을 알고 있는 명령, 페이지, 대상을 검색해 바로 실행할 때 사용한다.
- 항목 수가 적고 구조가 고정돼 있으면 DropdownMenu나 일반 목록을 사용한다.
- form의 값을 고르는 단순 단일 선택에는 Select를 사용한다.
- 사용자가 전체 후보를 비교해야 한다면 검색 결과를 좁은 palette에 숨기지 말고 별도 화면을 제공한다.
구조
기본 구조는 Command → CommandInput + CommandList다. List 안에서 CommandGroup으로 관련 후보를 묶고 각 후보는 CommandItem으로 표시한다. 결과가 없을 때 CommandEmpty, 그룹 사이에 CommandSeparator, 단축키 안내에 CommandShortcut을 사용한다.
화면 위에 띄우는 구성은 CommandDialog를 root로 사용한다. 이 구성은 Dialog 표면 안에 Command를 넣고 padding과 항목 크기를 조정한다.
변형과 크기
별도 variant나 size prop은 없다. CommandList의 기본 최대 높이는 300px이고 overflow는 세로로 처리된다. CommandDialog 안의 input은 48px, 항목은 더 넉넉한 세로 padding을 사용한다. 일반 Command는 부모의 높이와 너비를 채운다.
상태와 동작
입력값에 따라 후보가 걸러지고 keyboard 이동 대상은 data-selected 상태로 강조된다. CommandItem은 선택과 비활성 상태를 지원하며 선택 동작은 onSelect에서 처리한다. CommandDialog는 open, onOpenChange 같은 Dialog props를 전달한다. 이 wrapper는 접근 가능한 제목을 자동으로 만들지 않으므로 소비 화면이 DialogTitle을 자식으로 제공한다.
비동기 결과를 가져오는 상태는 내장하지 않는다. 제품이 loading, error, retry를 명시적으로 구성해야 하며 이전 검색어나 선택 맥락을 불필요하게 초기화하지 않는다.
콘텐츠
Input placeholder는 커리큘럼 또는 명령 검색처럼 검색 범위를 알려준다. Group heading은 사용자가 결과 종류를 구분하는 짧은 명사를 사용한다. Item은 실행 결과가 분명해야 하며, 같은 이름이 반복되면 보조 텍스트를 추가해 구분한다.
접근성
CommandInput에는 검색 범위를 설명하는 placeholder만 의존하지 말고 필요한 경우 외부 label이나 aria-label을 제공한다. CommandDialog에는 시각적으로 숨기더라도 DialogTitle을 제공한다. keyboard 선택과 focus 스타일을 제거하지 않는다. 아이콘만으로 명령을 표시하지 않는다. Shortcut은 안내일 뿐 유일한 실행 방법이 되어서는 안 된다.
좁은 화면과 긴 콘텐츠
긴 후보 이름은 핵심 식별자가 남도록 말줄임과 보조 텍스트를 설계한다. CommandDialog가 좁은 화면에서 너무 많은 세부 정보를 담지 않도록 결과 한 행의 구조를 단순하게 유지한다. 탐색 결과가 많거나 복잡한 filter가 필요하면 전체 화면 검색으로 전환한다.
공개 API
Command: command rootCommandDialog: Dialog props를 받는 overlay 구성CommandInput,CommandList,CommandItem: 입력, 결과 목록, 선택 항목CommandGroup,CommandSeparator: 결과 구획CommandEmpty: 결과 없음 상태CommandShortcut: 오른쪽 단축키 안내
각 wrapper는 해당 기반 요소의 props를 전달하며 별도 Dot 전용 검색 상태 prop은 없다.
예제
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
DialogTitle,
} from '@classum/dot-design-system/ui';
interface ScreenCommandProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}
export function ScreenCommand({ open, onOpenChange }: ScreenCommandProps) {
return (
<CommandDialog open={open} onOpenChange={onOpenChange}>
<DialogTitle className="sr-only">화면과 명령 검색</DialogTitle>
<CommandInput aria-label="화면과 명령 검색" placeholder="화면 또는 명령 검색" />
<CommandList>
<CommandEmpty>일치하는 결과가 없어요.</CommandEmpty>
<CommandGroup heading="커리큘럼">
<CommandItem value="신규 입사자 온보딩">신규 입사자 온보딩 열기</CommandItem>
<CommandItem value="초안 저장">현재 커리큘럼 초안 저장</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
);
}피해야 할 사용
- 후보가 몇 개뿐인 단순 선택을 Command로 복잡하게 만들지 않는다.
- 검색 결과가 없는데 빈 목록만 보여주지 않는다.
- 후보 이름을 내부 route나 식별자 그대로 노출하지 않는다.
- 선택 즉시 파괴 행동을 실행하지 않는다.
- 단축키만 제공하고 눈에 보이는 진입점을 생략하지 않는다.