# Segmented Text Input
URL: /lynx/components/segmented-text-input
Source: https://github.com/daangn/seed-design/blob/dev/docs/content/lynx/components/segmented-text-input.mdx
주민등록번호·카드번호처럼 형식이 정해진 값을 여러 구간으로 나눠 입력받는 컴포넌트입니다.
Lynx Engine 최소 버전: 3.6
사용 XElement:
사용 가능 버전: @seed-design/lynx-react@1.1.0, @seed-design/lynx-css@1.1.0
## Preview
```tsx
import "./styles";
import { ResidentNumberField } from "@/components/ui/resident-number-field";
export default function Example() {
return (
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
주민등록번호 뒷자리 첫 번째 자리만 받거나 카드번호 뒷자리를 가려야 하는 경우처럼, 하나의 Text Input으로 처리하기 어려운 국내 입력 형식에 사용합니다. 전화번호·이메일, 해외 카드번호처럼 구간을 나눌 필요가 없는 입력은 [Text Field](/lynx/components/text-field-input)에 포맷을 적용해서 받으세요.
Segment마다 native 입력 하나를 두고, 값은 입력 Segment 순서대로 `string[]`로 다룹니다.
- 끝에서 입력해 Segment를 채우면 다음 Segment로 이동합니다. 자리 수를 넘는 입력은 반영하지 않습니다. 기본 `type`(`"number"`)에서는 숫자 외 문자도 반영하지 않습니다.
- 가린(`masked`) Segment는 입력한 글자 대신 자리 수만큼 점을 그립니다. 가린 값 안에서는 caret을 옮기지 않고 끝에서만 입력·삭제합니다.
- 붙여넣은 값은 구분자를 지우고 다음 Segment까지 차례로 채웁니다. 가린(`masked`)·`readOnly` Segment 앞에서 멈춥니다.
- `autoComplete="bday"` Segment는 붙여넣은 생년월일(`yyyy-MM-dd` 등)을 자리 수에 맞춰 바꿉니다.
- 한글 조합 중에는 다음 Segment로 이동하지 않습니다.
## Installation
주민등록번호와 카드번호는 snippet으로 제공합니다. 자리 구성과 문구(`accessibility-label`, placeholder)는 snippet에 있으니 필요하면 설치한 파일에서 고쳐 쓰세요.
- npm: npx @seed-design/cli@latest add ui:resident-number-field
- pnpm: pnpm dlx @seed-design/cli@latest add ui:resident-number-field
- yarn: yarn dlx @seed-design/cli@latest add ui:resident-number-field
- bun: bun x @seed-design/cli@latest add ui:resident-number-field
* npm: npx @seed-design/cli@latest add ui:card-number-field
* pnpm: pnpm dlx @seed-design/cli@latest add ui:card-number-field
* yarn: yarn dlx @seed-design/cli@latest add ui:card-number-field
* bun: bun x @seed-design/cli@latest add ui:card-number-field
## Props
### `ResidentNumberField`
### `CardNumberField`
### `SegmentedTextInput.Root`
### `SegmentedTextInput.Segment`
`staticText`를 주면 입력 없이 정적 텍스트만 표시하는 Segment가 됩니다. 정적 Segment는 값 배열에 포함하지 않습니다.
### `SegmentedTextInput.Dots`
주민등록번호 뒷자리처럼 가린 자리를 7×7 점으로 표시합니다. `staticText`·`staticSuffix`·`placeholder`에 넣습니다.
```tsx
}
staticSuffix={}
/>
```
## Examples
### Resident Number
`digits`로 입력받을 자리 수를 정합니다. (default: `7`)
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { ResidentNumberField } from "@/components/ui/resident-number-field";
export default function Example() {
return (
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
### Card Number
`maskedDigits`로 가릴 뒷자리 수를 정하고, `suffix`에 카드사 로고를 넣습니다. 로고 크기는 넣는 쪽에서 정합니다(Figma 기준 30×20).
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { CardNumberField } from "@/components/ui/card-number-field";
export default function Example() {
return (
}
/>
);
}
// 예시용 카드사 로고입니다. 실제 서비스에서는 카드사 로고 자산을 크기와 함께 넣습니다.
function CardLogo() {
return (
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
### State
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { ResidentNumberField } from "@/components/ui/resident-number-field";
export default function Example() {
return (
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
### Size
`size`로 크기를 정합니다. (default: `large`) `medium`은 React에서 데스크톱(Breakpoint `lg` 이상) 전용 크기입니다.
```tsx
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { CardNumberField } from "@/components/ui/card-number-field";
export default function Example() {
return (
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
### Controlled
```tsx
import "./styles";
import { useState } from "@lynx-js/react";
import { VStack } from "@seed-design/lynx-react";
import { ResidentNumberField } from "@/components/ui/resident-number-field";
export default function Example() {
const [value, setValue] = useState(["", ""]);
return (
value: {JSON.stringify(value)}
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
### External Keypad
보안 키패드처럼 값을 밖에서 채워야 하면 해당 Segment를 `readOnly`로 두고 값을 controlled로 관리합니다. `readOnly` Segment는 native 입력 대신 텍스트로 그려져 시스템 키보드가 열리지 않습니다. Segment의 `bindtap`에서 키패드를 열고, 키패드가 `value`를 바꿉니다. 키패드 연동과 값 암호화는 서비스에서 구현합니다.
```tsx
import "./styles";
import { Fragment, useState } from "@lynx-js/react";
import { ActionButton, Field, HStack, SegmentedTextInput, VStack } from "@seed-design/lynx-react";
const LABELS = ["1-4번째 자리", "5-8번째 자리", "9-12번째 자리", "13-16번째 자리"];
const KEYPAD_SEGMENTS = [2, 3];
const KEYS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
export default function Example() {
const [value, setValue] = useState(["", "", "", ""]);
const [keypadOpen, setKeypadOpen] = useState(false);
const press = (key: string) => {
"background only";
setValue((prev) => {
const index = KEYPAD_SEGMENTS.find((i) => (prev[i] ?? "").length < 4);
if (index === undefined) return prev;
return prev.map((segment, i) => (i === index ? segment + key : segment));
});
};
const erase = () => {
"background only";
setValue((prev) => {
const index = [...KEYPAD_SEGMENTS].reverse().find((i) => (prev[i] ?? "").length > 0);
if (index === undefined) return prev;
return prev.map((segment, i) => (i === index ? segment.slice(0, -1) : segment));
});
};
return (
카드번호
{LABELS.map((label, i) => {
const usesKeypad = KEYPAD_SEGMENTS.includes(i);
return (
{i > 0 ? - : null}
{
"background only";
setKeypadOpen(usesKeypad);
}}
/>
);
})}
{keypadOpen ? (
{KEYS.map((key) => (
press(key)}>
{key}
))}
지우기
) : null}
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
### Composition
snippet이 다루지 않는 형식은 `SegmentedTextInput`을 직접 조합합니다. 입력 Segment마다 `accessibility-label`로 구간 이름을 주세요.
```tsx
import "./styles";
import { Field, SegmentedTextInput } from "@seed-design/lynx-react";
export default function Example() {
return (
사업자등록번호 - -
);
}
```
웹 미리보기에서는 native 입력을 그리지 못해 Segment 배치와 점 표시가 실제와 다릅니다. QR 코드 패널에서 Lynx Explorer로 실행해 확인해 주세요.
### Keyboard Avoiding
[Keyboard Avoiding Scroll View](/lynx/components/keyboard-avoiding-scroll-view#segmented-text-input) 안에 두면 Segment에 focus할 때 Field 전체나 `SegmentedTextInput.Root`처럼 Segment보다 넓은 영역이 키보드에 가리지 않도록 스크롤합니다. Segment 사이로 focus가 옮겨 가도 스크롤 위치를 유지합니다.
```tsx
import "./styles";
import { KeyboardAvoidingScrollView } from "@seed-design/lynx-react";
import { ResidentNumberField } from "@/components/ui/resident-number-field";
export default function Example() {
return (
구간 입력
아래 Segment를 탭하면 Field 전체가 키보드 위로 보이도록 자동 스크롤됩니다. 다음
Segment로 넘어가도 위치를 유지합니다.
입력 영역이 화면 아래에 오도록 확보한 공간
);
}
```
이 예제는 네이티브 키보드 동작을 사용합니다. QR 코드 패널에서 Lynx Explorer로 실행한 뒤 주민등록번호 입력 영역을 탭해 확인해 주세요.
## 웹 버전과의 차이
- 빈 Segment에서 Backspace를 눌러도 이전 Segment로 이동하지 않습니다. 빈 native 입력에서 지우기를 누르면 값이 바뀌지 않아 입력 이벤트가 오지 않습니다(Android에서 하드웨어·소프트 키보드 모두 확인). 이전 Segment는 탭해서 고칩니다.
- ←·→ 방향키로 Segment 사이를 이동하지 않습니다. `masked` Segment에서도 caret만 끝에 고정합니다.
- native 입력에 paste 이벤트와 자동완성 속성이 없습니다. 한 번의 입력으로 두 글자 이상 늘어나면 붙여넣기로 보고 나눠 채웁니다. `autoComplete`는 native 입력에 전달하지 않고 `"bday"` 정규화에만 씁니다.
- 키보드 종류는 `inputMode` 대신 `type`(default: `"number"`)으로 정합니다. `masked` Segment는 native `type="password"` 대신 글자를 숨긴 입력 위에 점을 그리므로 `masked`여도 `type`의 키보드가 열립니다.
- `readOnly`에서는 [Text Field](/lynx/components/text-field-input)처럼 native 입력 대신 텍스트로 그립니다.
- Field label이 group 이름이 되거나 label을 눌러 첫 Segment로 이동하는 동작이 없습니다. 각 Segment의 `accessibility-label`로 구간 이름을 알립니다.