Select

Select (셀렉트/선택)

Select 컴포넌트는 옵션 목록에서 사용자가 제공한 정보를 수집하는 데 사용해요.

출처: 문서

본문

기본 select (Basic select)

메뉴는 뷰포트(viewpoint) 하단 근처에 있지 않은 한, 이를 발생시킨 요소 아래에 배치돼요.

import * as React from 'react';
import Box from '@mui/material/Box';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

export default function BasicSelect() {
  const [age, setAge] = React.useState('');

  const handleChange = (event: SelectChangeEvent) => {
    setAge(event.target.value as string);
  };

  return (
    <Box sx={{ minWidth: 120 }}>
      <FormControl fullWidth>
        <InputLabel id="demo-simple-select-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-label"
          id="demo-simple-select"
          value={age}
          label="Age"
          onChange={handleChange}
        >
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
    </Box>
  );
}

고급 기능 (Advanced features)

Select 컴포넌트는 네이티브 <select> 요소와 상호 교환할 수 있게 설계되었어요.

combobox, multiselect, autocomplete, async 또는 creatable 지원 같은 더 고급 기능을 찾고 있다면 Autocomplete 컴포넌트로 가 보세요. 이는 "react-select"와 "downshift" 패키지의 개선된 버전을 지향해요.

Props

Select 컴포넌트는 InputBase의 커스텀 <input> 요소로 구현돼요. 선택한 variant에 따라 text field 컴포넌트의 하위 컴포넌트 — OutlinedInput, Input, FilledInput 중 하나를 확장해요. 동일한 스타일과 많은 props를 공유하므로, 각 컴포넌트의 API 페이지를 참고하세요.

:::warning 입력 컴포넌트와 달리 placeholder prop은 Select에서 사용할 수 없어요. placeholder를 추가하려면 아래의 placeholder 섹션을 참고하세요. :::

변형 (Variants)

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

export default function SelectVariants() {
  const [age, setAge] = React.useState('');

  const handleChange = (event: SelectChangeEvent) => {
    setAge(event.target.value);
  };

  return (
    <div>
      <FormControl variant="outlined" sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id="demo-simple-select-outlined-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-outlined-label"
          id="demo-simple-select-outlined"
          value={age}
          onChange={handleChange}
          label="Age"
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
      <FormControl variant="standard" sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id="demo-simple-select-standard-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-standard-label"
          id="demo-simple-select-standard"
          value={age}
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
      <FormControl variant="filled" sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id="demo-simple-select-filled-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-filled-label"
          id="demo-simple-select-filled"
          value={age}
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
    </div>
  );
}

:::warning Select의 outlined variant와 함께 FormControl을 사용할 때는 label을 두 곳에 제공해야 한다는 점에 유의하세요: InputLabel 컴포넌트와 Select 컴포넌트의 label prop (위 데모 참고). 이것은 label이 떠오르는(float) 애니메이션이 제대로 동작하는 데 필요해요. :::

라벨과 도우미 텍스트 (Labels and helper text)

Select에는 항상 접근 가능한 이름(accessible name)이 필요해요. 이 이름은 labelId로 Select에 연결된 보이는 라벨(예: InputLabel)이나, 입력 요소 props(inputProps)에 aria-label prop을 추가하는 방식으로 제공할 수 있어요. 더 많은 정보가 필요하다면 도우미 텍스트 요소를 제공하고 aria-describedby를 사용해 Select에 연결하세요.

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormHelperText from '@mui/material/FormHelperText';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

export default function SelectLabels() {
  const id = React.useId();
  const noLabelId = React.useId();
  const [age, setAge] = React.useState('');

  const handleChange = (event: SelectChangeEvent) => {
    setAge(event.target.value);
  };

  return (
    <div>
      <FormControl sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id={`${id}-label`}>Age</InputLabel>
        <Select
          aria-describedby={`${id}-helper-text`}
          labelId={`${id}-label`}
          id={id}
          value={age}
          label="Age"
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText id={`${id}-helper-text`}>
          Visible label and helper text
        </FormHelperText>
      </FormControl>
      <FormControl sx={{ m: 1, minWidth: 120 }}>
        <Select
          aria-describedby={`${noLabelId}-helper-text`}
          value={age}
          onChange={handleChange}
          displayEmpty
          inputProps={{ 'aria-label': 'Age' }}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText id={`${noLabelId}-helper-text`}>
          aria-label and helper text
        </FormHelperText>
      </FormControl>
    </div>
  );
}

자동 너비 (Auto width)

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

export default function SelectAutoWidth() {
  const [age, setAge] = React.useState('');

  const handleChange = (event: SelectChangeEvent) => {
    setAge(event.target.value);
  };

  return (
    <div>
      <FormControl sx={{ m: 1, minWidth: 80 }}>
        <InputLabel id="demo-simple-select-autowidth-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-autowidth-label"
          id="demo-simple-select-autowidth"
          value={age}
          onChange={handleChange}
          autoWidth
          label="Age"
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={21}>Twenty one</MenuItem>
          <MenuItem value={22}>Twenty one and a half</MenuItem>
        </Select>
      </FormControl>
    </div>
  );
}

작은 크기 (Small Size)

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

export default function SelectSmall() {
  const [age, setAge] = React.useState('');

  const handleChange = (event: SelectChangeEvent) => {
    setAge(event.target.value);
  };

  return (
    <FormControl sx={{ m: 1, minWidth: 120 }} size="small">
      <InputLabel id="demo-select-small-label">Age</InputLabel>
      <Select
        labelId="demo-select-small-label"
        id="demo-select-small"
        value={age}
        label="Age"
        onChange={handleChange}
      >
        <MenuItem value="">
          <em>None</em>
        </MenuItem>
        <MenuItem value={10}>Ten</MenuItem>
        <MenuItem value={20}>Twenty</MenuItem>
        <MenuItem value={30}>Thirty</MenuItem>
      </Select>
    </FormControl>
  );
}

기타 props (Other props)

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormHelperText from '@mui/material/FormHelperText';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

export default function SelectOtherProps() {
  const [age, setAge] = React.useState('');

  const handleChange = (event: SelectChangeEvent) => {
    setAge(event.target.value);
  };

  return (
    <div>
      <FormControl sx={{ m: 1, minWidth: 120 }} disabled>
        <InputLabel id="demo-simple-select-disabled-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-disabled-label"
          id="demo-simple-select-disabled"
          value={age}
          label="Age"
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Disabled</FormHelperText>
      </FormControl>
      <FormControl sx={{ m: 1, minWidth: 120 }} error>
        <InputLabel id="demo-simple-select-error-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-error-label"
          id="demo-simple-select-error"
          value={age}
          label="Age"
          onChange={handleChange}
          renderValue={(value) => `⚠️  - ${value}`}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Error</FormHelperText>
      </FormControl>
      <FormControl sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id="demo-simple-select-readonly-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-readonly-label"
          id="demo-simple-select-readonly"
          value={age}
          label="Age"
          onChange={handleChange}
          inputProps={{ readOnly: true }}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Read only</FormHelperText>
      </FormControl>
      <FormControl required sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id="demo-simple-select-required-label">Age</InputLabel>
        <Select
          labelId="demo-simple-select-required-label"
          id="demo-simple-select-required"
          value={age}
          label="Age *"
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
        <FormHelperText>Required</FormHelperText>
      </FormControl>
    </div>
  );
}

네이티브 select (Native select)

모바일에서는 플랫폼의 네이티브 select를 사용하면 사용자 경험이 더 좋아질 수 있어서, 이 패턴을 허용해요.

import * as React from 'react';
import Box from '@mui/material/Box';
import InputLabel from '@mui/material/InputLabel';
import FormControl from '@mui/material/FormControl';
import NativeSelect from '@mui/material/NativeSelect';

export default function NativeSelectDemo() {
  const id = React.useId();
  return (
    <Box sx={{ minWidth: 120 }}>
      <FormControl fullWidth>
        <InputLabel variant="standard" htmlFor={`${id}-select`}>
          Age
        </InputLabel>
        <NativeSelect
          defaultValue={30}
          inputProps={{
            name: 'age',
            id: `${id}-select`,
          }}
        >
          <option value={10}>Ten</option>
          <option value={20}>Twenty</option>
          <option value={30}>Thirty</option>
        </NativeSelect>
      </FormControl>
    </Box>
  );
}

TextField

TextField 래퍼 컴포넌트는 라벨, 입력, 도움말 텍스트를 포함하는 완전한 폼 컨트롤이에요. select 모드로 사용하는 예시는 이 섹션에서 찾을 수 있어요.

커스터마이즈 (Customization)

컴포넌트를 커스터마이즈하는 몇 가지 예시를 볼게요. 자세한 내용은 overrides 문서 페이지에서 확인할 수 있어요.

첫 단계는 InputBase 컴포넌트를 스타일링하는 거예요. 스타일이 적용되면 텍스트 필드로 직접 사용하거나, select의 input prop에 제공해 select 필드로 만들 수 있어요. "standard" variant는 내용을 fieldset/legend 마크업으로 감싸지 않아서 커스터마이즈하기 더 쉽다는 점에 유의하세요.

import * as React from 'react';
import { styled } from '@mui/material/styles';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import NativeSelect from '@mui/material/NativeSelect';
import InputBase from '@mui/material/InputBase';

const BootstrapInput = styled(InputBase)(({ theme }) => ({
  'label + &': {
    marginTop: theme.spacing(3),
  },
  '& .MuiInputBase-input': {
    borderRadius: 4,
    position: 'relative',
    backgroundColor: (theme.vars ?? theme).palette.background.paper,
    border: '1px solid #ced4da',
    fontSize: 16,
    padding: '10px 26px 10px 12px',
    transition: theme.transitions.create(['border-color', 'box-shadow']),
    // Use the system font instead of the default Roboto font.
    fontFamily: [
      '-apple-system',
      'BlinkMacSystemFont',
      '"Segoe UI"',
      'Roboto',
      '"Helvetica Neue"',
      'Arial',
      'sans-serif',
      '"Apple Color Emoji"',
      '"Segoe UI Emoji"',
      '"Segoe UI Symbol"',
    ].join(','),
    '&:focus': {
      borderRadius: 4,
      borderColor: '#80bdff',
      boxShadow: '0 0 0 0.2rem rgba(0,123,255,.25)',
    },
  },
}));

export default function CustomizedSelects() {
  const textboxId = React.useId();
  const selectId = React.useId();
  const nativeId = React.useId();
  const [age, setAge] = React.useState('');
  const handleChange = (event: { target: { value: string } }) => {
    setAge(event.target.value);
  };
  return (
    <div>
      <FormControl sx={{ m: 1 }} variant="standard">
        <InputLabel htmlFor={`${textboxId}-input`}>Age</InputLabel>
        <BootstrapInput id={`${textboxId}-input`} />
      </FormControl>
      <FormControl sx={{ m: 1 }} variant="standard">
        <InputLabel id={`${selectId}-label`}>Age</InputLabel>
        <Select
          labelId={`${selectId}-label`}
          id={`${selectId}-select`}
          value={age}
          onChange={handleChange}
          input={<BootstrapInput />}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
      <FormControl sx={{ m: 1 }} variant="standard">
        <InputLabel htmlFor={`${nativeId}-select`}>Age</InputLabel>
        <NativeSelect
          id={`${nativeId}-select`}
          value={age}
          onChange={handleChange}
          input={<BootstrapInput />}
        >
          <option aria-label="None" value="" />
          <option value={10}>Ten</option>
          <option value={20}>Twenty</option>
          <option value={30}>Thirty</option>
        </NativeSelect>
      </FormControl>
    </div>
  );
}

🎨 영감이 필요하다면 MUI Treasury의 커스터마이즈 예시를 확인해 보세요.

다중 select (Multiple select)

Select 컴포넌트는 여러 선택(multiple selection)을 처리할 수 있어요. multiple prop으로 활성화돼요.

단일 선택과 마찬가지로, onChange 콜백에서 event.target.value에 접근해 새 값을 꺼낼 수 있어요. 이 값은 항상 배열이에요.

기본 (Default)

import * as React from 'react';
import { Theme, useTheme } from '@mui/material/styles';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  slotProps: {
    paper: {
      style: {
        maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
        width: 250,
      },
    },
  },
};

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

function getStyles(name: string, personName: string[], theme: Theme) {
  return {
    fontWeight: personName.includes(name)
      ? theme.typography.fontWeightMedium
      : theme.typography.fontWeightRegular,
  };
}

export default function MultipleSelect() {
  const theme = useTheme();
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: SelectChangeEvent<typeof personName>) => {
    const {
      target: { value },
    } = event;
    setPersonName(
      // On autofill we get a stringified value.
      typeof value === 'string' ? value.split(',') : value,
    );
  };

  return (
    <div>
      <FormControl sx={{ m: 1, width: 300 }}>
        <InputLabel id="demo-multiple-name-label">Name</InputLabel>
        <Select
          labelId="demo-multiple-name-label"
          id="demo-multiple-name"
          multiple
          value={personName}
          onChange={handleChange}
          input={<OutlinedInput label="Name" />}
          MenuProps={MenuProps}
        >
          {names.map((name) => (
            <MenuItem
              key={name}
              value={name}
              style={getStyles(name, personName, theme)}
            >
              {name}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

선택 표시 (Selection indicators)

이 예시는 listbox의 각 항목 선택 상태를 나타내기 위해 아이콘을 사용하는 방법을 보여줘요.

import * as React from 'react';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import ListItemText from '@mui/material/ListItemText';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CheckBoxIcon from '@mui/icons-material/CheckBox';

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  slotProps: {
    paper: {
      style: {
        maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
        width: 250,
      },
    },
  },
};

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

export default function MultipleSelectCheckmarks() {
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: SelectChangeEvent<typeof personName>) => {
    const {
      target: { value },
    } = event;
    setPersonName(
      // On autofill we get a stringified value.
      typeof value === 'string' ? value.split(',') : value,
    );
  };

  return (
    <div>
      <FormControl sx={{ m: 1, width: 300 }}>
        <InputLabel id="demo-multiple-checkbox-label">Tag</InputLabel>
        <Select
          labelId="demo-multiple-checkbox-label"
          id="demo-multiple-checkbox"
          multiple
          value={personName}
          onChange={handleChange}
          input={<OutlinedInput label="Tag" />}
          renderValue={(selected) => selected.join(', ')}
          MenuProps={MenuProps}
        >
          {names.map((name) => {
            const selected = personName.includes(name);
            const SelectionIcon = selected ? CheckBoxIcon : CheckBoxOutlineBlankIcon;

            return (
              <MenuItem key={name} value={name}>
                <SelectionIcon
                  fontSize="small"
                  style={{ marginRight: 8, padding: 9, boxSizing: 'content-box' }}
                />
                <ListItemText primary={name} />
              </MenuItem>
            );
          })}
        </Select>
      </FormControl>
    </div>
  );
}

칩 (Chip)

import * as React from 'react';
import { Theme, useTheme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import Chip from '@mui/material/Chip';

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  slotProps: {
    paper: {
      style: {
        maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
        width: 250,
      },
    },
  },
};

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

function getStyles(name: string, personName: readonly string[], theme: Theme) {
  return {
    fontWeight: personName.includes(name)
      ? theme.typography.fontWeightMedium
      : theme.typography.fontWeightRegular,
  };
}

export default function MultipleSelectChip() {
  const theme = useTheme();
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: SelectChangeEvent<typeof personName>) => {
    const {
      target: { value },
    } = event;
    setPersonName(
      // On autofill we get a stringified value.
      typeof value === 'string' ? value.split(',') : value,
    );
  };

  return (
    <div>
      <FormControl sx={{ m: 1, width: 300 }}>
        <InputLabel id="demo-multiple-chip-label">Chip</InputLabel>
        <Select
          labelId="demo-multiple-chip-label"
          id="demo-multiple-chip"
          multiple
          value={personName}
          onChange={handleChange}
          input={<OutlinedInput id="select-multiple-chip" label="Chip" />}
          renderValue={(selected) => (
            <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
              {selected.map((value) => (
                <Chip key={value} label={value} />
              ))}
            </Box>
          )}
          MenuProps={MenuProps}
        >
          {names.map((name) => (
            <MenuItem
              key={name}
              value={name}
              style={getStyles(name, personName, theme)}
            >
              {name}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

플레이스홀더 (Placeholder)

import * as React from 'react';
import { Theme, useTheme } from '@mui/material/styles';
import OutlinedInput from '@mui/material/OutlinedInput';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
  slotProps: {
    paper: {
      style: {
        maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
        width: 250,
      },
    },
  },
};

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

function getStyles(name: string, personName: readonly string[], theme: Theme) {
  return {
    fontWeight: personName.includes(name)
      ? theme.typography.fontWeightMedium
      : theme.typography.fontWeightRegular,
  };
}

export default function MultipleSelectPlaceholder() {
  const theme = useTheme();
  const [personName, setPersonName] = React.useState<string[]>([]);

  const handleChange = (event: SelectChangeEvent<typeof personName>) => {
    const {
      target: { value },
    } = event;
    setPersonName(
      // On autofill we get a stringified value.
      typeof value === 'string' ? value.split(',') : value,
    );
  };

  return (
    <div>
      <FormControl sx={{ m: 1, width: 300, mt: 3 }}>
        <Select
          multiple
          displayEmpty
          value={personName}
          onChange={handleChange}
          input={<OutlinedInput />}
          renderValue={(selected) => {
            if (selected.length === 0) {
              return <em>Placeholder</em>;
            }

            return selected.join(', ');
          }}
          MenuProps={MenuProps}
          inputProps={{ 'aria-label': 'Without label' }}
        >
          <MenuItem disabled value="">
            <em>Placeholder</em>
          </MenuItem>
          {names.map((name) => (
            <MenuItem
              key={name}
              value={name}
              style={getStyles(name, personName, theme)}
            >
              {name}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

네이티브 (Native)

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';

const names = [
  'Oliver Hansen',
  'Van Henry',
  'April Tucker',
  'Ralph Hubbard',
  'Omar Alexander',
  'Carlos Abbott',
  'Miriam Wagner',
  'Bradley Wilkerson',
  'Virginia Andrews',
  'Kelly Snyder',
];

export default function MultipleSelectNative() {
  const id = React.useId();
  const [personName, setPersonName] = React.useState<string[]>([]);
  const handleChangeMultiple = (event: React.ChangeEvent<HTMLSelectElement>) => {
    const { options } = event.target;
    const value: string[] = [];
    for (let i = 0, l = options.length; i < l; i += 1) {
      if (options[i].selected) {
        value.push(options[i].value);
      }
    }
    setPersonName(value);
  };

  return (
    <div>
      <FormControl sx={{ m: 1, minWidth: 120, maxWidth: 300 }}>
        <InputLabel shrink htmlFor={`${id}-select`}>
          Native
        </InputLabel>
        <Select<string[]>
          multiple
          native
          value={personName}
          // @ts-ignore Typings are not considering `native`
          onChange={handleChangeMultiple}
          label="Native"
          inputProps={{
            id: `${id}-select`,
          }}
        >
          {names.map((name) => (
            <option key={name} value={name}>
              {name}
            </option>
          ))}
        </Select>
      </FormControl>
    </div>
  );
}

열린 상태 제어하기 (Controlling the open state)

open prop으로 select의 열린 상태를 제어할 수 있어요. 또는 defaultOpen prop으로 컴포넌트의 초기(비제어·uncontrolled) 열린 상태를 설정할 수도 있어요.

:::info

  • 컴포넌트가 부모에 의해 props를 통해 관리되면 **제어(controlled)**됩니다.
  • 컴포넌트가 자체 로컬 상태로 관리되면 **비제어(uncontrolled)**됩니다.

제어(controlled)와 비제어 컴포넌트에 대해 더 알아보려면 React 문서를 참고하세요. :::

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import Button from '@mui/material/Button';

export default function ControlledOpenSelect() {
  const [age, setAge] = React.useState<string | number>('');
  const [open, setOpen] = React.useState(false);

  const handleChange = (event: SelectChangeEvent<typeof age>) => {
    setAge(event.target.value);
  };

  const handleClose = () => {
    setOpen(false);
  };

  const handleOpen = () => {
    setOpen(true);
  };

  return (
    <div>
      <Button sx={{ display: 'block', mt: 2 }} onClick={handleOpen}>
        Open the select
      </Button>
      <FormControl sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id="demo-controlled-open-select-label">Age</InputLabel>
        <Select
          labelId="demo-controlled-open-select-label"
          id="demo-controlled-open-select"
          open={open}
          onClose={handleClose}
          onOpen={handleOpen}
          value={age}
          label="Age"
          onChange={handleChange}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>
      </FormControl>
    </div>
  );
}

다이얼로그와 함께 (With a dialog)

Material Design 가이드라인에서는 권장되지 않지만, 다이얼로그 안에서 select를 사용할 수 있어요.

import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import InputLabel from '@mui/material/InputLabel';
import OutlinedInput from '@mui/material/OutlinedInput';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';

export default function DialogSelect() {
  const nativeId = React.useId();
  const selectId = React.useId();
  const [open, setOpen] = React.useState(false);
  const [age, setAge] = React.useState<number | string>('');

  const handleChange = (event: SelectChangeEvent<typeof age>) => {
    setAge(Number(event.target.value) || '');
  };

  const handleClickOpen = () => {
    setOpen(true);
  };

  const handleDialogClose = (
    _event: React.SyntheticEvent<unknown>,
    reason: string,
  ) => {
    if (!['backdropClick', 'escapeKeyDown'].includes(reason)) {
      setOpen(false);
    }
  };

  const handleActionButtonClick = () => {
    setOpen(false);
  };

  return (
    <div>
      <Button onClick={handleClickOpen}>Open select dialog</Button>
      <Dialog open={open} onClose={handleDialogClose}>
        <DialogTitle>Fill the form</DialogTitle>
        <DialogContent>
          <Box component="form" sx={{ display: 'flex', flexWrap: 'wrap' }}>
            <FormControl sx={{ m: 1, minWidth: 120 }}>
              <InputLabel htmlFor={`${nativeId}-select`}>Age</InputLabel>
              <Select
                native
                value={age}
                onChange={handleChange}
                input={<OutlinedInput label="Age" id={`${nativeId}-select`} />}
              >
                <option aria-label="None" value="" />
                <option value={10}>Ten</option>
                <option value={20}>Twenty</option>
                <option value={30}>Thirty</option>
              </Select>
            </FormControl>
            <FormControl sx={{ m: 1, minWidth: 120 }}>
              <InputLabel id={`${selectId}-label`}>Age</InputLabel>
              <Select
                labelId={`${selectId}-label`}
                id={`${selectId}-select`}
                value={age}
                onChange={handleChange}
                input={<OutlinedInput label="Age" />}
              >
                <MenuItem value="">
                  <em>None</em>
                </MenuItem>
                <MenuItem value={10}>Ten</MenuItem>
                <MenuItem value={20}>Twenty</MenuItem>
                <MenuItem value={30}>Thirty</MenuItem>
              </Select>
            </FormControl>
          </Box>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleActionButtonClick}>Cancel</Button>
          <Button onClick={handleActionButtonClick}>Ok</Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

그룹화 (Grouping)

ListSubheader 컴포넌트나 네이티브 <optgroup> 요소로 카테고리를 표시해요.

import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import ListSubheader from '@mui/material/ListSubheader';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';

export default function GroupedSelect() {
  const nativeId = React.useId();
  const id = React.useId();
  return (
    <div>
      <FormControl sx={{ m: 1, minWidth: 120 }}>
        <InputLabel htmlFor={`${nativeId}-select`}>Grouping</InputLabel>
        <Select native defaultValue="" id={`${nativeId}-select`} label="Grouping">
          <option aria-label="None" value="" />
          <optgroup label="Category 1">
            <option value={1}>Option 1</option>
            <option value={2}>Option 2</option>
          </optgroup>
          <optgroup label="Category 2">
            <option value={3}>Option 3</option>
            <option value={4}>Option 4</option>
          </optgroup>
        </Select>
      </FormControl>
      <FormControl sx={{ m: 1, minWidth: 120 }}>
        <InputLabel id={`${id}-label`}>Grouping</InputLabel>
        <Select
          defaultValue=""
          id={`${id}-select`}
          label="Grouping"
          SelectDisplayProps={{
            'aria-labelledby': `${id}-label`,
          }}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <ListSubheader>Category 1</ListSubheader>
          <MenuItem value={1}>Option 1</MenuItem>
          <MenuItem value={2}>Option 2</MenuItem>
          <ListSubheader>Category 2</ListSubheader>
          <MenuItem value={3}>Option 3</MenuItem>
          <MenuItem value={4}>Option 4</MenuItem>
        </Select>
      </FormControl>
    </div>
  );
}

접근성 (Accessibility)

Select 입력에 올바르게 라벨을 지정하려면 라벨을 담은 id를 가진 추가 요소가 필요해요. 그 id는 Select의 labelId와 일치해야 해요, 예:

<InputLabel id="label">Age</InputLabel>
<Select labelId="label" id="select" value="20">
  <MenuItem value="10">Ten</MenuItem>
  <MenuItem value="20">Twenty</MenuItem>
</Select>

또는 id와 label을 가진 TextField가 적절한 마크업과 id를 만들어 주기도 해요:

<TextField id="select" label="Age" value="20" select>
  <MenuItem value="10">Ten</MenuItem>
  <MenuItem value="20">Twenty</MenuItem>
</TextField>

네이티브 select의 경우, select 요소의 id 속성 값을 InputLabel의 htmlFor 속성에 주어 라벨을 언급해야 해요:

<InputLabel htmlFor="select">Age</InputLabel>
<NativeSelect id="select">
  <option value="10">Ten</option>
  <option value="20">Twenty</option>
</NativeSelect>

NativeSelect API

Demos

이 React 컴포넌트 사용법에 대한 예시와 자세한 내용은 컴포넌트 데모 페이지를 방문해 주세요:

Import

import NativeSelect from '@mui/material/NativeSelect';
// or
import { NativeSelect } from '@mui/material';

Props

Name Type Default Required Description
children node - No 컴포넌트 콘텐츠입니다.
classes object {} No 컴포넌트에 적용되는 스타일을 덮어쓰거나 확장합니다.
IconComponent elementType ArrowDropDownIcon No 아이콘 컴포넌트입니다.
input element <Input /> No <Input /> 요소입니다.
inputProps object - No input 요소에 전달할 속성입니다.
onChange function(event: React.ChangeEvent<HTMLSelectElement>) => void - No 네이티브 select의 값이 바뀌었을 때 호출되는 콜백입니다.
sx Array<func | object | bool> | func | object - No 시스템 오버라이드와 추가 CSS 스타일을 정의할 수 있게 해주는 시스템 prop입니다.
value any - No select의 값입니다.
variant 'filled' | 'outlined' | 'standard' - No 컴포넌트의 변형(variant)입니다.

Note: ref는 루트 요소(HTMLDivElement)로 전달됩니다.

그 외에 제공된 다른 props는 모두 루트 요소(Input)에 전달됩니다.

상속 (Inheritance)

위에서 명시적으로 문서화되지는 않았지만, Input 컴포넌트의 props도 NativeSelect에서 사용할 수 있어요.

테마 기본 props (Theme default props)

MuiNativeSelect를 사용해 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.

CSS

규칙 이름 (Rule name)

Global class Rule name Description
.Mui-disabled - select 컴포넌트의 disabled 클래스에 적용되는 상태 클래스입니다.
.Mui-error - select 컴포넌트의 error 클래스에 적용되는 상태 클래스입니다.
- filled variant="filled"일 때 select 컴포넌트에 적용되는 스타일입니다.
- icon 아이콘 컴포넌트에 적용되는 스타일입니다.
- iconFilled variant="filled"일 때 아이콘 컴포넌트에 적용되는 스타일입니다.
- iconOpen 팝업이 열려 있을 때 아이콘 컴포넌트에 적용되는 스타일입니다.
- iconOutlined variant="outlined"일 때 아이콘 컴포넌트에 적용되는 스타일입니다.
- iconStandard variant="standard"일 때 아이콘 컴포넌트에 적용되는 스타일입니다.
- multiple multiple={true}일 때 select 컴포넌트에 적용되는 스타일입니다.
- nativeInput 기저 네이티브 입력 컴포넌트에 적용되는 스타일입니다.
- outlined variant="outlined"일 때 select 컴포넌트에 적용되는 스타일입니다.
- root 루트 요소에 적용되는 스타일입니다.
- select select 컴포넌트의 select 클래스에 적용되는 스타일입니다.
- standard variant="standard"일 때 select 컴포넌트에 적용되는 스타일입니다.

소스 코드 (Source code)

이 페이지에서 정보를 찾지 못했다면 컴포넌트 구현을 살펴보는 것도 방법이에요.

Select API

Demos

이 React 컴포넌트 사용법에 대한 예시와 자세한 내용은 컴포넌트 데모 페이지를 방문해 주세요:

Import

import Select from '@mui/material/Select';
// or
import { Select } from '@mui/material';

Props

Name Type Default Required Description
autoWidth bool false No 입력에 따라 select의 너비를 자동으로 조정할지 여부입니다.
children node - No 컴포넌트 콘텐츠입니다.
classes object {} No 컴포넌트에 적용되는 스타일을 덮어쓰거나 확장합니다.
defaultOpen bool false No 초기(비제어) 열린 상태입니다.
defaultValue any - No 초기(비제어) 값입니다.
displayEmpty bool false No 값이 빈 문자열일 때 선택된 값을 표시할지 여부입니다.
IconComponent elementType ArrowDropDownIcon No 아이콘 컴포넌트입니다.
id string - No select의 id입니다.
input element - No <Input /> 요소입니다.
inputProps object - No input 요소에 전달할 속성입니다.
label node - No select의 라벨입니다.
labelId string - No 라벨 요소의 id입니다.
MenuProps object - No Menu 컴포넌트에 전달할 속성입니다.
multiple bool false No 여러 값(multiple) 선택을 허용할지 여부입니다.
native bool false No 네이티브 select을 사용할지 여부입니다.
onChange function(event: SelectChangeEvent<Value>, child?: object) => void - No 값이 바뀌었을 때 호출되는 콜백입니다.
onClose function(event: object) => void - No 드롭다운이 닫힐 때 호출되는 콜백입니다.
onOpen function(event: object) => void - No 드롭다운이 열릴 때 호출되는 콜백입니다.
open bool - No 드롭다운의 열림 상태를 제어합니다.
renderValue function(value: any) => ReactNode - No 선택된 값을 렌더링하는 함수입니다.
SelectDisplayProps object - No select의 표시 요소에 전달할 속성입니다.
sx Array<func | object | bool> | func | object - No 시스템 오버라이드와 추가 CSS 스타일을 정의할 수 있게 해주는 시스템 prop입니다.
value '' | any - No select의 값입니다.
variant 'filled' | 'outlined' | 'standard' 'outlined' No 컴포넌트의 변형(variant)입니다.

Note: ref는 루트 요소(HTMLDivElement)로 전달됩니다.

그 외에 제공된 다른 props는 모두 루트 요소(OutlinedInput)에 전달됩니다.

상속 (Inheritance)

위에서 명시적으로 문서화되지는 않았지만, OutlinedInput 컴포넌트의 props도 Select에서 사용할 수 있어요.

테마 기본 props (Theme default props)

MuiSelect를 사용해 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.

CSS

규칙 이름 (Rule name)

Global class Rule name Description
.Mui-disabled - select 컴포넌트의 disabled 클래스에 적용되는 상태 클래스입니다.
.Mui-error - error={true}일 때 루트 요소에 적용되는 상태 클래스입니다.
- filled variant="filled"일 때 select 컴포넌트에 적용되는 스타일입니다.
.Mui-focused - select가 포커스되었을 때 select 컴포넌트에 적용되는 스타일입니다.
- icon 아이콘 컴포넌트에 적용되는 스타일입니다.
- iconOpen 팝업이 열려 있을 때 아이콘 컴포넌트에 적용되는 스타일입니다.
- multiple multiple={true}일 때 select 컴포넌트에 적용되는 스타일입니다.
- nativeInput 기저 네이티브 입력 컴포넌트에 적용되는 스타일입니다.
- outlined variant="outlined"일 때 select 컴포넌트에 적용되는 스타일입니다.
- root 루트 요소에 적용되는 스타일입니다.
- select select 컴포넌트의 select 클래스에 적용되는 스타일입니다.
- standard variant="standard"일 때 select 컴포넌트에 적용되는 스타일입니다.

소스 코드 (Source code)

이 페이지에서 정보를 찾지 못했다면 컴포넌트 구현을 살펴보는 것도 방법이에요.

더 알아보기 (Learn more)