Select

정해진 옵션에서 값을 선택하고, 검색하거나 여러 개를 고릅니다.

Live preview
컴포넌트를 조작하면 여기에 결과가 표시됩니다.
코드 보기
HTML
<sy-select
  placeholder="팀 선택"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  name="team"
  id="sample"
  aria-label="팀 선택"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

사용 가이드

#

정해진 후보에서 하나 또는 여러 값을 선택하게 합니다. sy-option의 value와 label로 저장값과 표시 이름을 구분하세요.

  1. 오류 문구는 error 슬롯에 작성합니다.

    컴포넌트 안에 <span slot="error">오류 메시지</span>을 넣으세요. 글자뿐 아니라 링크 등 필요한 안내도 넣을 수 있습니다. noNativeValidity를 지원하는 입력은 이를 켜면 브라우저 팝업 대신 이 안내를 표시합니다. Switch는 항상 컴포넌트 아래에 오류를 표시합니다.

  2. 업무 규칙의 실패는 setCustomError()로 알립니다.

    예를 들어 서버가 중복된 값을 거절하면 error 슬롯의 문구를 바꾸고 setCustomError()를 호출하세요. 이 메서드에는 메시지 인자를 전달하지 않습니다. 사용자 지정 오류가 있으면 폼 제출도 차단됩니다.

  3. 해결 후 해제하고 제출 가능 여부를 확인합니다.

    clearCustomError()로 업무 규칙 오류를 해제하세요. required나 입력 형식 검사까지 해제되지는 않습니다. await checkValidity()는 true/false를, reportValidity()는 검사 결과와 오류 표시를 함께 제공합니다. name을 지정한 유효한 값은 제출 시 FormData로 읽습니다.

기본 선택

짧은 목록에서 하나를 고를 때 사용합니다. defaultValue로 초기 선택을 지정하세요.

검색하며 선택

mode="searchable"은 후보가 많아 이름으로 좁혀야 할 때 알맞습니다.

복수 선택

mode="multiple"로 여러 후보를 선택하고 maxTagCount로 표시할 태그 수를 조절합니다.

새 값도 입력

mode="tag"는 목록에 없는 값도 추가해야 할 때 사용하세요.

예제

#

복수 선택

여러 항목을 선택하고 태그의 닫기 버튼으로 하나씩 제거합니다.

코드 보기
HTML
<sy-select
  mode="multiple"
  clearable
  maxtagcount="2"
  defaultvalue="1,2,3"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

새 태그 추가

목록에 없는 값을 입력하고 Enter로 새 항목을 추가하세요.

코드 보기
HTML
<sy-select mode="tag" placeholder="팀 입력 후 Enter">
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

선택값 제어

setValue로 옵션을 선택하고 빈 배열로 선택을 해제합니다. clearValue는 선택을 유지하며 검색 중인 글자만 지웁니다.

코드 보기
HTML
<sy-select
  mode="multiple"
  placeholder="팀 선택"
  id="sample"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">디자인·개발 선택</sy-button>
  <sy-button id="action-1" size="small">선택 비우기</sy-button>
  <sy-button id="action-2" size="small">검색어 지우기</sy-button>
  <sy-button id="action-3" size="small">목록 닫기</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setValue([
      "1",
      "2"
    ]);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setValue([]);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearValue();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.closeDropdown();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

단일 선택값 변경

일반 선택에서도 value를 지정하면 화면의 레이블과 폼 전송 값이 함께 바뀝니다.

코드 보기
HTML
<sy-select
  name="team"
  clearable
  placeholder="팀 선택"
  id="sample"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">개발 선택</sy-button>
  <sy-button id="action-1" size="small">선택 비우기</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setValue("2");
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setValue([]);
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

이벤트와 전달 값

컴포넌트를 조작하고 최근 이벤트의 이름과 detail 값을 확인하세요. 코드에는 이 컴포넌트와 하위 구성 요소가 보내는 이벤트의 구독 방법이 모두 포함되어 있습니다.

코드 보기
HTML
<sy-select
  placeholder="팀 선택"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  name="team"
  id="sample"
  aria-label="팀 선택"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>
<output id="demo-events" aria-live="polite">컴포넌트를 조작하면 최근 이벤트와 전달 값이 표시됩니다.
</output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  for (const name of ["opened", "removed", "selected", "focused", "blured", "inputChanged",
      "cleared", "activated"
    ]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

선택값을 읽기 전용으로 표시

readonly는 현재 선택을 유지하면서 변경을 막습니다. disabled와 달리 폼 값으로 사용할 수 있습니다.

코드 보기
HTML
<sy-select
  placeholder="팀 선택"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  readonly
  name="team"
  id="sample"
  aria-label="팀 선택"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "readonly": true
  });
  const component2 = document.querySelector("#sample");
  Object.assign(component2, {
    "selectedOptions": [{
      "value": "1",
      "label": "Design"
    }]
  });
</script>

검색 결과가 없을 때

empty 옵션으로 빈 목록 상태를 안내합니다. 사용자가 선택할 수 있는 일반 항목과 구분해 표시하세요.

코드 보기
HTML
<sy-select placeholder="목록 열기">
  <sy-option empty label="검색 결과가 없습니다."></sy-option>
</sy-select>

목록의 로딩과 빈 상태

loading으로 데이터를 기다리는 상태를 보여줍니다. 실제 응답이 도착하면 loading을 false로 바꾸고 옵션을 넣습니다.

코드 보기
HTML
<sy-select
  placeholder="팀 선택"
  mode="default"
  size="medium"
  maxtagcount="2"
  clearable
  loading
  name="team"
  id="sample"
  aria-label="팀 선택"
>
  <sy-option value="1" label="Design"></sy-option>
  <sy-option value="2" label="Development"></sy-option>
  <sy-option value="3" label="Marketing"></sy-option>
</sy-select>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "loading": true
  });
</script>

옵션별 사용 가능 상태

선택할 수 없는 항목은 disabled로 표시합니다. 긴 이름의 설명은 showTooltip으로 확인할 수 있습니다.

코드 보기
HTML
<sy-select placeholder="프로젝트 선택">
  <sy-option
    value="design"
    label="Design"
    showtooltip
  ></sy-option>
  <sy-option
    value="archived"
    label="종료된 프로젝트"
    disabled
  ></sy-option>
</sy-select>

폼과 유효성 검사

값을 비운 채 제출하면 필수 입력 검사가 동작합니다. 값을 입력한 뒤 제출해 폼 데이터를 확인하고, 사용자 지정 오류도 켜고 꺼보세요.

코드 보기
HTML
<form style="display:grid;gap:16px">
  <sy-select
    name="field"
    required
    placeholder="팀 선택"
    id="sample"
  >
    <sy-option value="1" label="Design"></sy-option>
    <sy-option value="2" label="Development"></sy-option>
    <sy-option value="3" label="Marketing"></sy-option><span slot="error">팀을
      선택하세요.</span>
  </sy-select>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">제출</sy-button>
    <sy-button type="reset">초기화</sy-button>
  </div>
</form>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">유효성 확인</sy-button>
  <sy-button id="action-1" size="small">오류 표시</sy-button>
  <sy-button id="action-2" size="small">오류 해제</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.checkValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

검사 결과와 오류 상태 읽기

제출 전에 검사하거나 오류를 화면에 표시할 수 있습니다. 필수 값을 비워 두고 검사한 다음, 값을 채워 반환 값이 어떻게 달라지는지 확인하세요. 상태 조회는 사용자 지정 오류와 필수 입력 오류를 구분하는 데 사용합니다.

코드 보기
HTML
<form style="display:grid;gap:16px">
  <sy-select
    name="field"
    required
    placeholder="팀 선택"
    id="sample"
  >
    <sy-option value="1" label="Design"></sy-option>
    <sy-option value="2" label="Development"></sy-option>
    <sy-option value="3" label="Marketing"></sy-option><span slot="error">팀을
      선택하세요.</span>
  </sy-select>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">제출</sy-button>
    <sy-button type="reset">초기화</sy-button>
  </div>
</form>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">유효성 확인</sy-button>
  <sy-button id="action-1" size="small">오류 표시</sy-button>
  <sy-button id="action-2" size="small">오류 해제</sy-button>
  <sy-button id="action-3" size="small">오류와 함께 검사</sy-button>
  <sy-button id="action-4" size="small">검증 상태 조회</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.checkValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearCustomError();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.reportValidity();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget4 = document.querySelector("#sample");
  document.querySelector('#action-4').addEventListener('click', async () => {
    const result = await actionTarget4.getStatus();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

사용자 지정 오류 메시지

서버 중복 검사나 업무 규칙에 맞지 않을 때 오류 문구를 직접 표시합니다. 아래 문구를 바꿔 보고 제출을 눌러 차단되는지 확인하세요. 오류 해제 후에는 필수 입력 조건까지 만족해야 제출됩니다.

코드 보기
HTML
<label for="error-message">표시할 오류 메시지</label><input
  id="error-message"
  value="서버에서 이 값을 승인하지 않았습니다. 내용을 확인해 주세요."
  type="text"
>
<form style="display:grid;gap:16px">
  <sy-select
    nonativevalidity
    name="field"
    required
    placeholder="팀 선택"
    id="sample"
  >
    <sy-option value="1" label="Design"></sy-option>
    <sy-option value="2" label="Development"></sy-option>
    <sy-option value="3" label="Marketing"></sy-option><span slot="error">팀을
      선택하세요.</span>
  </sy-select>
  <div style="display:flex;flex-wrap:wrap;gap:8px">
    <sy-button type="submit" variant="primary">제출</sy-button>
    <sy-button type="reset">초기화</sy-button>
  </div>
</form>
<div class="demo-actions">
  <sy-button id="show-custom-error" variant="primary">오류 표시</sy-button>
  <sy-button id="clear-custom-error">오류 해제</sy-button>
</div>
<output id="custom-error-result" aria-live="polite"></output>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? '완료';
  await (async function customErrorSetup(root) {
    const field = root.querySelector('#sample');
    const message = root.querySelector('[slot="error"]');
    const input = root.querySelector('#error-message');
    const output = root.querySelector('#custom-error-result');
    const update = async () => {
      message.textContent = input.value.trim() || _msg("m_4eea8cef81c6");
      await field.setCustomError();
      output.textContent = _msg("m_e00bd46236d3", [message.textContent]);
    };
    input.addEventListener('input', update);
    root.querySelector('#show-custom-error').addEventListener('click', update);
    root.querySelector('#clear-custom-error').addEventListener('click', async () => {
      await field.clearCustomError();
      output.textContent = await field.checkValidity() ? _msg("m_c454251167b2") : _msg(
        "m_5b8cb48627e5");
    });
    root.querySelector('form').addEventListener('invalid', () => {
      output.textContent = _msg("m_c702c7778166");
    }, true);
    root.querySelector('form').addEventListener('reset', () => {
      output.textContent = _msg("m_b90c2b4dc30f");
    });
    await update();
  })(document);
  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    document.querySelector('#demo-result').textContent = describeResult([...new FormData(event
      .currentTarget)]);
  });
</script>

API reference

#

이름에 마우스를 올리거나 키보드로 선택하면 적용 코드를 확인할 수 있습니다. 터치 화면에서는 이름을 누르세요.

<sy-select>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalse현재 값을 비우는 버튼을 표시합니다.
booleanfalse사용자 조작과 키보드 포커스를 차단합니다.
booleanfalse현재 값을 보여주면서 사용자 수정을 막습니다.
booleanfalse빈 목록 상태를 표시합니다.
booleanfalse오류 상태를 표시합니다.
booleanfalse화면에서 항목을 숨깁니다.
booleanfalse처리 중임을 표시합니다.
number0한 번에 표시할 선택 태그 수입니다. 0이면 모두 표시합니다.
string''처음 표시할 선택값입니다.
string''값이 비어 있을 때 표시할 안내 문구입니다.
'small' | 'medium' | 'large''medium'컴포넌트의 크기입니다.
'default' | 'searchable' | 'multiple' | 'tag''default'일반·검색·복수 선택·태그 입력 방식입니다.
booleanfalse값이 없으면 폼 검증을 실패시킵니다.
string''FormData에 사용되는 필드 이름입니다.
booleanfalse브라우저의 기본 오류 표시 대신 앱의 오류 UI를 사용할 때 지정합니다. checkValidity 검사 자체는 유지됩니다.
booleanfalseTree Select와의 내부 결합에 사용합니다. 일반 Select에서는 기본값을 유지하세요.
{ value: string; label?: string }[][]선택된 옵션 객체 배열입니다.

객체·배열 값은 JavaScript 프로퍼티로 전달합니다.

Events

EventDetailDescription
CustomEvent<void>목록을 열면 발생합니다.
CustomEvent<any>선택 항목을 제거하면 발생합니다.
CustomEvent<any>사용자가 항목을 선택했을 때 발생합니다.
CustomEvent<void>포커스를 받으면 발생합니다.
CustomEvent<void>포커스를 잃으면 발생합니다.
CustomEvent<string>검색 입력값이 바뀌면 발생합니다.
CustomEvent<void>선택을 모두 지우면 발생합니다.

Methods

MethodReturnsDescription
Promise<void>지정한 value 문자열 또는 배열에 해당하는 옵션을 선택합니다. 빈 배열은 선택을 해제합니다.
Promise<void>선택한 옵션을 유지하며 검색 입력값만 비웁니다.
Promise<void>열린 옵션 목록을 닫습니다.
Promise<void>사용자 지정 오류 상태를 설정합니다.
Promise<void>사용자 지정 오류 상태를 해제합니다.
Promise<string>현재 유효성 검사 상태를 반환합니다.
Promise<boolean>현재 제약 조건에 맞으면 true, 아니면 false를 반환합니다.
Promise<boolean>현재 값을 검사하고 오류를 사용자에게 표시합니다.

Slots

SlotDescription
컴포넌트의 본문 또는 자식 항목입니다.
유효성 검사 오류 메시지입니다.

<sy-option>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalse사용자 조작과 키보드 포커스를 차단합니다.
string''항목을 설명하는 레이블입니다.
booleanfalse현재 값을 보여주면서 사용자 수정을 막습니다.
string''현재 값 또는 항목의 식별 값입니다.
booleanfalse값 또는 보조 설명의 툴팁 표시 방식입니다.
booleanfalse해당 옵션이 선택됐는지 표시합니다.
booleanfalse화면에서 항목을 숨깁니다.
booleanfalse빈 목록 상태를 표시합니다.
booleanfalse처리 중임을 표시합니다.
booleanfalse태그 입력으로 추가된 옵션인지 표시합니다.
booleanfalse키보드 탐색 중 활성 옵션인지 표시합니다.

Events

EventDetailDescription
CustomEvent<{ value: string; label: string }>키보드 탐색으로 활성 항목이 바뀌면 발생합니다.

Slots

SlotDescription
컴포넌트의 본문 또는 자식 항목입니다.