PasswordInput

PasswordInput

사용자로부터 비밀번호 데이터를 입력받는 컴포넌트예요. 비밀번호 표시/숨기기 토글 버튼을 제공해요.

출처: 문서

본문

사용법 (Usage)

PasswordInput 컴포넌트는 Input 및 Input.Wrapper 컴포넌트 기능과 모든 input 요소 prop을 지원해요. PasswordInput 문서에는 컴포넌트가 지원하는 모든 기능이 포함되지는 않아요. 사용 가능한 모든 기능은 Input 문서를 참고해요.

import { PasswordInput } from '@mantine/core';

function Demo() {
  return <PasswordInput label="Password" placeholder="Your password" />;
}

variant, size, radius, label, description, error 등의 표준 Input prop을 지원해요.

로딩 상태 (Loading state)

loading prop을 설정하면 로딩 인디케이터가 표시돼요. 기본적으로 로더는 인풋 오른쪽에 표시돼요. loadingPosition prop을 'left' 또는 'right'로 바꿔 위치를 변경할 수 있어요. API 호출, 검색, 검증 같은 비동기 작업에 유용해요.

import { PasswordInput } from '@mantine/core';

function Demo() {
  return <PasswordInput label="Password" loading />;
}

제어 방식 (Controlled)

import { useState } from 'react';
import { PasswordInput } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState('');
  return <PasswordInput value={value} onChange={(event) => setValue(event.currentTarget.value)} />;
}

비제어 방식 (Uncontrolled)

PasswordInput은 네이티브 input[type="password"]와 같은 방식으로 비제어 폼에서 사용할 수 있어요. 폼 제출 시 FormData 객체에 비밀번호 인풋 값을 포함하려면 name 속성을 설정해요. 비제어 폼에서 초기 값을 제어하려면 defaultValue prop을 사용해요.

FormData와 함께 비제어 PasswordInput을 사용하는 예시:

import { PasswordInput } from '@mantine/core';

function Demo() {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log('Password value:', formData.get('password'));
      }}
    >
      <PasswordInput name="password" />
      <button type="submit">Submit</button>
    </form>
  );
}

표시 상태 제어 (Controlled visibility toggle)

visible과 onVisibilityChange prop으로 표시 상태를 제어할 수 있어요. 예를 들어 두 인풋 사이의 표시 상태를 동기화하는 데 사용할 수 있어요.

import { useDisclosure } from '@mantine/hooks';
import { PasswordInput, Stack } from '@mantine/core';

function Demo() {
  const [visible, { toggle }] = useDisclosure(false);
  return (
    <Stack>
      <PasswordInput label="Password" visible={visible} onVisibilityChange={toggle} />
      <PasswordInput label="Confirm password" visible={visible} onVisibilityChange={toggle} />
    </Stack>
  );
}

표시 토글 아이콘 변경 (Change visibility toggle icon)

표시 토글 아이콘을 변경하려면 reveal prop을 받는 React 컴포넌트를 visibilityToggleIcon에 전달해요.

import { PasswordInput } from '@mantine/core';
import { EyeIcon, EyeSlashIcon } from '@phosphor-icons/react';

const VisibilityToggleIcon = ({ reveal }: { reveal: boolean }) =>
  reveal ? <EyeSlashIcon size={16} /> : <EyeIcon size={16} />;

function Demo() {
  return <PasswordInput label="Password" visibilityToggleIcon={VisibilityToggleIcon} />;
}

강도 측정기 예시 (Strength meter example)

Progress와 Popover 컴포넌트로 만든 비밀번호 강도 측정기 예시예요.

import { useState } from 'react';
import { XIcon, CheckIcon } from '@phosphor-icons/react';
import { PasswordInput, Progress, Text, Popover, Box } from '@mantine/core';

function PasswordRequirement({ meets, label }: { meets: boolean; label: string }) {
  return (
    <Text color={meets ? 'teal' : 'red'} size="sm" mt={7}>
      {meets ? <CheckIcon size={12} /> : <XIcon size={12} />} {label}
    </Text>
  );
}

const requirements = [
  { re: /[0-9]/, label: 'Includes number' },
  { re: /[a-z]/, label: 'Includes lowercase letter' },
  { re: /[A-Z]/, label: 'Includes uppercase letter' },
  { re: /[$&+,:;=?@#|'<>.^*()%!-]/, label: 'Includes special symbol' },
];

function getStrength(password: string) {
  let multiplier = password.length > 5 ? 0 : 1;

  requirements.forEach((requirement) => {
    if (!requirement.re.test(password)) {
      multiplier += 1;
    }
  });

  return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 10);
}

function Demo() {
  const [popoverOpened, setPopoverOpened] = useState(false);
  const [value, setValue] = useState('');
  const checks = requirements.map((requirement, index) => (
    <PasswordRequirement key={index} label={requirement.label} meets={requirement.re.test(value)} />
  ));

  const strength = getStrength(value);
  const color = strength === 100 ? 'teal' : strength > 50 ? 'yellow' : 'red';

  return (
    <Popover opened={popoverOpened} position="bottom" width="target" transitionProps={{ transition: 'pop' }}>
      <Popover.Target>
        <div onFocusCapture={() => setPopoverOpened(true)} onBlurCapture={() => setPopoverOpened(false)}>
          <PasswordInput value={value} onChange={(event) => setValue(event.currentTarget.value)} label="Your password *" withAsterisk />
        </div>
      </Popover.Target>
      <Popover.Dropdown>
        <Progress color={color} value={strength} size={5} mb="xs" />
        <PasswordRequirement label="Has at least 6 characters" meets={value.length > 5} />
        {checks}
      </Popover.Dropdown>
    </Popover>
  );
}

표시 토글 없이 사용 (Usage without visibility toggle)

표시 토글 버튼이 필요 없다면 TextInput 컴포넌트를 사용해요.

import { TextInput } from '@mantine/core';

function Demo() {
  return <TextInput type="password" />;
}

왼쪽·오른쪽 섹션 (Left and right sections)

PasswordInput은 leftSection과 rightSection prop을 지원해요. 이 섹션들은 인풋 래퍼 안에서 절대 위치로 렌더링돼요. 아이콘, 인풋 컨트롤 또는 다른 요소를 표시하는 데 사용할 수 있어요.

섹션 스타일과 콘텐츠를 제어하려면 다음 prop을 사용할 수 있어요.

  • rightSection/leftSection – 인풋의 해당 쪽에 렌더링할 React 노드
  • rightSectionWidth/leftSectionWidth – 오른쪽 섹션의 너비와 인풋 해당 쪽의 패딩을 제어해요. 기본적으로 컴포넌트 size prop에 의해 제어돼요.
  • rightSectionPointerEvents/leftSectionPointerEvents – 섹션의 pointer-events 속성을 제어해요. 비대화형 요소를 렌더링하고 싶다면 none으로 설정해 클릭이 인풋으로 통과하게 해요.

rightSection prop을 사용하면 표시 토글 버튼은 렌더링되지 않아요.

import { PasswordInput } from '@mantine/core';
import { LockIcon } from '@phosphor-icons/react';

function Demo() {
  const icon = <LockIcon size={16} />;

  return (
    <>
      <PasswordInput leftSection={icon} label="With left section" defaultValue="secret" />
      <PasswordInput rightSection={icon} label="With right section" defaultValue="secret" mt="md" />
    </>
  );
}

오류 상태 (Error state)

import { PasswordInput } from '@mantine/core';

function Demo() {
  return (
    <>
      <PasswordInput label="Boolean error" error defaultValue="secret" />
      <PasswordInput label="With error message" error="Invalid name" defaultValue="secret" mt="md" />
    </>
  );
}

성공 상태 (Success state)

import { PasswordInput } from '@mantine/core';

function Demo() {
  return <PasswordInput label="Password Input" error="Looks good!" defaultValue="secret" />;
}

비활성 (Disabled)

disabled prop이 설정되면 표시 토글 버튼이 숨겨져요.

import { PasswordInput } from '@mantine/core';

function Demo() {
  return <PasswordInput label="Disabled password input" disabled defaultValue="secret" />;
}

Styles API

PasswordInput은 Styles API를 지원해요. classNames prop으로 컴포넌트의 내부 요소에 스타일을 추가할 수 있어요.

Styles API 셀렉터:

  • wrapper – Input의 루트 요소
  • input – 인풋 요소
  • section – 왼쪽·오른쪽 섹션
  • bottomSection – 인풋 테두리 하단 안쪽에 렌더링되는 아래쪽 섹션 요소
  • root – 루트 요소
  • label – 라벨 요소
  • required – 라벨 안에 렌더링되는 필수 별표 요소
  • description – 설명 요소
  • error – 오류 요소
  • success – 성공 요소
  • innerInput – 실제 인풋 요소
  • visibilityToggle – 표시 토글 버튼

요소 ref 가져오기 (Get element ref)

import { useRef } from 'react';
import { PasswordInput } from '@mantine/core';

function Demo() {
  const ref = useRef<HTMLInputElement>(null);
  return <PasswordInput ref={ref} />;
}

접근성 (Accessibility)

label prop 없이 PasswordInput을 사용하면 스크린 리더가 제대로 알리지 못해요.

// Inaccessible input – screen reader will not announce it properly
import { PasswordInput } from '@mantine/core';
function Demo() { return <PasswordInput placeholder="Password" />; }

aria-label을 설정하면 인풋을 접근 가능하게 만들 수 있어요. 이 경우 라벨은 보이지 않지만 스크린 리더가 알려줘요.

// Accessible input – it has aria-label
import { PasswordInput } from '@mantine/core';
function Demo() { return <PasswordInput aria-label="Password" />; }

label prop이 설정되어 있으면 인풋은 접근 가능하며 aria-label을 설정할 필요가 없어요.

표시 토글 버튼의 aria-label을 설정하려면 visibilityToggleButtonProps prop을 사용해요.

import { PasswordInput } from '@mantine/core';

function Demo() {
  return <PasswordInput visibilityToggleButtonProps={{ 'aria-label': 'Toggle password visibility' }} />;
}

표시 토글 포커스 (Visibility toggle focus)

기본적으로 표시 토글 버튼은 탭 순서에서 제외돼요 (tabindex="-1"). 이는 네이티브 브라우저의 비밀번호 표시 컨트롤을 모방하고 폼 흐름을 중단시키지 않아요. Tab을 누르면 토글 버튼이 아니라 다음 필드로 포커스가 이동해요. 토글은 마우스, 터치, 그리고 탭 순서가 아니라 요소별로 탐색하는 보조 기술로 계속 조작할 수 있어요.

사용자가 Tab으로 토글에 도달하길 기대한다면 visibilityToggleFocusable prop을 설정해요. 그러면 버튼이 tabindex="0"을 받고 Enter 또는 Space로 활성화할 수 있어요.

import { PasswordInput, Stack, TextInput } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      <TextInput label="Email" placeholder="Email" />
      <PasswordInput label="Password" placeholder="Password" visibilityToggleFocusable />
      <TextInput label="Nickname" placeholder="Nickname" />
    </Stack>
  );
}

애플리케이션의 모든 PasswordInput에 이 동작을 적용하려면 theme에서 prop을 설정해요.

import { createTheme, PasswordInput } from '@mantine/core';

const theme = createTheme({
  components: {
    PasswordInput: PasswordInput.extend({
      defaultProps: {
        visibilityToggleFocusable: true,
      },
    }),
  },
});

더 알아보기 (Learn more)