본문으로 건너뛰기
Dot Design System
v0.3.1

검색과 필터

대상과 범위를 분명히 하고 결과 상태를 조작 가까이에 보여줍니다.

목표

사용자가 데이터 구조를 외우지 않고도 원하는 record 집합을 빠르게 좁히고, 현재 조건과 결과 범위를 항상 이해하게 한다. 검색은 예측 가능한 입력을 받고, 필터는 결과 가까이에서 작동하며, 상세 화면을 다녀와도 조건을 보존한다.

검색과 필터의 구분

  • 검색: 이름, 이메일, ID처럼 사용자가 알고 있는 문자열로 대상을 찾는다.
  • 필터: 상태, 기간, 소유자처럼 정의된 속성으로 결과 집합을 좁힌다.
  • 정렬: 같은 결과의 표시 순서를 바꾼다.
  • 보기 전환: 같은 데이터의 표현만 바꾼다.

이 네 가지를 하나의 모호한 control로 합치지 않는다.

검색 판단 규칙

  • placeholder 또는 label에서 검색 가능한 필드를 말한다.
  • 여러 필드를 통합 검색하면 입력 형식과 결과가 사용자의 예상 범위 안에 있어야 한다.
  • 내부 enum, 코드, 정확한 문법을 알아야만 찾을 수 있는 검색을 만들지 않는다.
  • 결과가 매우 적고 즉시 계산 가능하면 입력 중 갱신할 수 있다.
  • 요청 비용이 크면 debounce 또는 명시적 submit을 사용하고 진행 상태를 보여준다.
  • 검색어를 자동 교정하거나 확장했다면 적용된 기준을 알리고 원문으로 돌아갈 수 있게 한다.

필터 판단 규칙

  • 자주 쓰고 결과를 크게 바꾸는 1~3개 필터는 toolbar에 직접 둔다.
  • 나머지 고급 필터는 popover 또는 sheet에서 점진적으로 제공한다.
  • 필터 label은 데이터 속성을, option은 사용자 언어를 사용한다.
  • 적용 전에 결과 비용이 큰 경우 예상 결과 개수를 보여줄 수 있다.
  • 상호 배타적인 조건은 선택할 수 없게 숨기기보다 왜 함께 쓸 수 없는지 구조를 단순화한다.
  • 기본 filter가 적용되어 있으면 화면에서 명확히 드러낸다.

상태 보존과 URL

  • query, filter, sort, page는 공유·새로고침 가능한 URL search params를 우선한다.
  • 새 검색이나 결과 범위를 크게 바꾸는 filter는 page를 첫 페이지로 돌린다.
  • sort나 보기 전환이 selection을 조용히 해제하지 않게 한다.
  • 필터 초기화는 검색어까지 지우는지 레이블과 scope를 구분한다.
  • 상세로 이동했다 돌아오면 동일 조건과 scroll을 복원한다.

상태와 피드백

Initial

  • 기본 조건, 검색 범위와 전체 결과 개수를 보여준다.

Typing and loading

  • loading indicator가 input value를 가리거나 focus를 빼앗지 않는다.
  • 이전 결과를 유지할 수 있으면 stale 상태로 두고 갱신 중임을 표시한다.
  • 요청 순서가 뒤바뀌어 오래된 결과가 최신 입력을 덮지 않게 한다.

No results

  • 데이터 자체가 없는 empty와 검색 결과 없음을 구분한다.
  • 현재 검색어 또는 활성 filter를 요약한다.
  • 가장 비용이 낮은 복구 행동 하나를 제공한다: 검색어 수정, 개별 filter 해제, 전체 초기화.

Error

  • 검색 요청 실패 시 입력값과 filter를 유지한다.
  • 재시도는 같은 조건으로 실행한다.
  • 일부 facet count만 실패하면 전체 결과까지 숨기지 않는다.

좁은 화면

  • 검색 control은 가능한 전체 폭을 사용한다.
  • filter sheet를 닫은 뒤 toolbar에 활성 filter 개수와 핵심 조건을 표시한다.
  • apply가 필요한 filter는 sheet 안에서 결과 영향과 함께 명확한 button을 제공한다.
  • 긴 filter chip을 가로 scroll로 숨기기보다 요약과 전체 조건 확인 경로를 제공한다.

접근성

  • 검색 영역은 role="search"와 접근 가능한 이름을 가진다.
  • input에는 눈에 보이거나 programmatic label이 있다.
  • clear button은 검색어 지우기처럼 이름을 가진다.
  • 결과 개수는 debounce된 갱신 완료 시 polite live region으로 알린다.
  • filter popover/sheet의 focus trap과 trigger 복귀를 보장한다.
  • 선택된 option은 색뿐 아니라 checked/selected semantics로 전달한다.
  • keyboard만으로 검색 입력, filter 열기, 적용, 초기화, 결과 진입이 가능해야 한다.

예제

사용 코드 · TSX
<PageToolbar
  title="실행 기록"
  description="최근 30일 · 총 248건"
  controls={
    <>
      <Input
        type="search"
        aria-label="작업 이름 또는 실행 ID 검색"
        placeholder="작업 이름 또는 실행 ID 검색"
        value={query}
        onChange={(event) => setQuery(event.target.value)}
      />
      <Button variant="outline" aria-expanded={filtersOpen} onClick={openFilters}>
        필터 {activeFilterCount > 0 ? `${activeFilterCount}` : ''}
      </Button>
    </>
  }
/>

검색 결과 없음:

사용 코드 · TSX
<EmptyState
  title={`${query}” 검색 결과가 없어요`}
  description="검색어를 줄이거나 상태 필터를 초기화해보세요."
  action={<Button onClick={resetFilters}>검색과 필터 초기화</Button>}
/>

피해야 할 사용

  • 무엇을 입력해야 하는지 알 수 없는 만능 검색
  • 기본 filter를 숨겨 사용자가 전체 데이터라고 오해하게 함
  • filter 변경 중 input focus를 잃게 함
  • 결과 없음과 서버 오류를 같은 EmptyState로 표현
  • 상세 화면을 다녀오면 조건을 초기화
  • 선택 상태를 색상 chip만으로 표시
  • 작은 화면에서 활성 filter를 sheet 안에만 숨김

검토 체크

  • 검색 가능한 필드가 label 또는 placeholder에 드러난다.
  • 검색 입력과 결과의 형태를 예측할 수 있다.
  • 자주 쓰는 filter만 toolbar에 직접 노출한다.
  • URL로 현재 검색·filter·sort를 복원할 수 있다.
  • 이전 요청이 최신 결과를 덮지 않는다.
  • loading 중 입력과 이전 결과 맥락을 보존한다.
  • 데이터 없음, 결과 없음, 요청 실패를 구분한다.
  • keyboard와 screen reader로 filter 상태를 확인·변경할 수 있다.
  • 좁은 화면에서도 활성 조건을 확인할 수 있다.

관련 항목