# 필드와 검증

> 필드를 한 번 정의하면 Ikiform이 모든 제출을 서버에서 검증하고, 명확한 메시지와 타입이 있는 값을 제공합니다.

필드는 선택 사항입니다. 필드가 없는 폼은 폼이 보내는 어떤 데이터든 받습니다. 필드를 게시하면 Ikiform이 모든 제출을 서버에서 그 기준으로 검증하고, 잘못된 데이터는 명확한 메시지와 함께 거부하며, 깔끔하게 타입이 지정된 값을 저장합니다.

## 필드 정의하기

대시보드에서 폼을 열고 **Fields**로 이동하세요. 비주얼 편집기에서 필드를 추가하거나 JSON 정의를 직접 편집하세요. 각 필드의 `id`는 폼이 보내야 하는 입력의 `name`입니다.

`Form definition`:

```json
{
  "version": 1,
  "title": "Contact",
  "fields": [
    { "id": "name", "type": "text", "label": "Name", "required": true, "validation": { "max_length": 120 } },
    { "id": "email", "type": "email", "label": "Email", "required": true },
    {
      "id": "topic",
      "type": "select",
      "label": "Topic",
      "options": [
        { "value": "sales", "label": "Sales" },
        { "value": "support", "label": "Support" }
      ]
    },
    { "id": "message", "type": "textarea", "label": "Message", "validation": { "min_length": 10 } }
  ],
  "settings": { "submit_label": "Send", "success_message": "Thanks, we'll reply within a day." }
}
```

### 정의

| 키 | 필수 | 규칙 |
| --- | --- | --- |
| `version` | 예 | 항상 `1` |
| `title` | 예 | 1~200자 |
| `description` | 아니요 | 최대 2,000자 |
| `fields` | 예 | 고유한 ID를 가진 필드 1~100개, 옵션은 전체 최대 2,000개 |
| `settings.submit_label` | 아니요 | 1~100자, [shadcn/ui 컴포넌트](https://ikiform.com/ko/docs/shadcn.md) 같은 렌더러가 사용 |
| `settings.success_message` | 아니요 | 1~1,000자 |
| `settings.redirect_url` | 아니요 | 최대 2,048자의 `http:` 또는 `https:` URL |
| `$schema` | 아니요 | 최대 200자의 문자열, 무시됨 |

최상위와 모든 필드에서 알 수 없는 키는 거부되므로, `requried` 같은 오타는 저장할 때 오류로 나타납니다.

### 모든 필드에 쓰는 키

| 키 | 필수 | 규칙 |
| --- | --- | --- |
| `id` | 예 | 소문자 snake_case, 영문자로 시작, 최대 64자. 예: `team_size` |
| `type` | 예 | 아래 12가지 타입 중 하나 |
| `label` | 예 | 1~200자. 오류 페이지와 알림 이메일에 사용 |
| `required` | 아니요 | `true`이면 빈 값이 오류가 됨 |
| `placeholder` | 아니요 | 최대 200자 |
| `help_text` | 아니요 | 최대 1,000자 |

## 플레이그라운드

이 페이지의 인터랙티브 플레이그라운드에서 폼 정의와 샘플 JSON 페이로드를 편집할 수 있습니다. Ikiform의 실제 검증기를 브라우저에서 실행해, 서버가 반환할 정확한 `201` 또는 `422` 응답, 저장되는 변환된 값, 폼용으로 생성된 HTML 마크업을 보여 줍니다.

## 필드 타입

| 타입 | 저장 형식 | 허용 값 |
| --- | --- | --- |
| `text` | string | 한 줄 텍스트 |
| `textarea` | string | 여러 줄 텍스트 |
| `email` | string, 도메인은 소문자로 | 이메일 주소 하나 |
| `number` | number | 숫자, 또는 HTML 폼에서 온 숫자 문자열 |
| `select` | string | 목록에 있는 옵션 값 하나 |
| `radio` | string | 목록에 있는 옵션 값 하나 |
| `multi_select` | string[] | 목록에 있는 옵션 값들, 중복 제거 |
| `checkbox` | boolean | `on`, `true`, `1`, `yes`, 또는 값 없음 |
| `date` | string, `YYYY-MM-DD` | 실제 존재하는 날짜 |
| `url` | string, 정규화됨 | 웹 주소, 없으면 `https://` 추가 |
| `phone` | string | 숫자, 공백, `+ ( ) . -` |
| `rating` | integer | 1부터 `max`까지의 정수 |

### text

| validation | 값 | 기본값 |
| --- | --- | --- |
| `min_length` | 0~10,000 | 없음 |
| `max_length` | 1~10,000, 패턴이 있으면 최대 256 | 1,000, 패턴이 있으면 256 |
| `pattern` | 최대 200자의 정규 표현식, 값 전체와 일치해야 함 | 없음 |
| `pattern_message` | 1~200자 | `Invalid format` |

패턴은 `u` 플래그로 실행되며 값 전체와 일치해야 합니다. 저장할 때 Ikiform은 치명적인 백트래킹을 일으킬 수 있는 패턴을 거부합니다. 역참조, 반복이나 선택을 포함한 반복 그룹(`(a+)+`, `(a|b)*`), 지나치게 많이 중첩된 반복이 해당합니다.

### textarea

| 키 | 값 | 기본값 |
| --- | --- | --- |
| `validation.min_length` | 0~16,000 | 없음 |
| `validation.max_length` | 1~16,000 | 10,000 |
| `rows` | 1~50, 렌더러를 위한 표시 힌트 | 없음 |

### email

| validation | 값 | 기본값 |
| --- | --- | --- |
| `allowed_domains` | `example.com` 같은 도메인 1~100개, 정확히 일치 | 모든 도메인 |

254자보다 긴 주소는 거부됩니다. 도메인 부분은 소문자로 저장하고, `@` 앞부분은 보낸 그대로 유지합니다.

### number

| validation | 값 | 기본값 |
| --- | --- | --- |
| `min` | 아무 숫자 | 없음 |
| `max` | `min` 이상의 아무 숫자 | 없음 |
| `integer` | `true`이면 소수를 거부 | `false` |

### select, radio, multi_select

| 키 | 값 | 기본값 |
| --- | --- | --- |
| `options` | 고유한 값을 가진 `{ "value", "label" }` 객체 1~500개, 각각 1~200자 | 필수 |
| `validation.min_items` | `multi_select` 전용, 0~500 | 0, 필수이면 1 |
| `validation.max_items` | `multi_select` 전용, 1~500 | 옵션 개수 |

목록에 있는 값만 허용됩니다. `select`와 `radio`는 값 하나를 받습니다. `multi_select`는 반복되는 이름, `name[]`, JSON 배열로 보내세요.

### checkbox

추가 키가 없습니다. 빠진 체크박스는 `false`입니다. `required: true`는 반드시 체크해야 한다는 뜻이며 동의 체크박스에 적합합니다. `on`, `true`, `1`, `yes`, `off`, `false`, `0`, `no`, 빈 값 외의 값은 거부됩니다.

### date

| validation | 값 | 기본값 |
| --- | --- | --- |
| `min` | `YYYY-MM-DD` 형식의 날짜 | 없음 |
| `max` | `min`과 같거나 이후인 `YYYY-MM-DD` 형식의 날짜 | 없음 |

### url

| validation | 값 | 기본값 |
| --- | --- | --- |
| `https_only` | `true`이면 `http:`를 거부 | `false` |
| `max_length` | 1~4,096 | 2,048 |

점이 있는 호스트 이름을 가지며 사용자 이름이나 비밀번호가 없는 `http:` 및 `https:` URL만 허용됩니다. 저장되는 값은 정규화된 URL입니다.

### phone

| validation | 값 | 기본값 |
| --- | --- | --- |
| `e164` | `true`이면 국제 형식을 요구하며, 구분 기호 없이 저장 | `false` |

`e164`가 없으면 전화번호는 숫자 6~15자리가 필요하며 보낸 그대로 저장됩니다. 이 옵션을 켜면 `+1 (415) 555-0123`이 `+14155550123`으로 저장됩니다.

### rating

| validation | 값 | 기본값 |
| --- | --- | --- |
| `max` | 2~10 | 5 |

## 타입 변환

HTML 폼은 모든 값을 텍스트로 보냅니다. Ikiform은 저장하기 전에 각 필드가 선언한 타입으로 변환합니다. `name`이 텍스트, `email`, `guests`가 숫자, `newsletter`가 체크박스, `site`가 URL, `phone`이 전화번호 필드일 때:

`Posted, urlencoded`:

```text
name=++Ada+Lovelace++&email=Ada@Example.COM&guests=3&newsletter=on&site=example.com&phone=%2B1%20415%20555%200123
```

`Stored, with phone set to e164`:

```json
{
  "name": "Ada Lovelace",
  "email": "Ada@example.com",
  "guests": 3,
  "newsletter": true,
  "site": "https://example.com/",
  "phone": "+14155550123"
}
```

- 값의 앞뒤 공백은 제거됩니다. 빈 문자열은 값이 없는 것으로 봅니다.
- 값 하나를 기대하는 필드는 항목이 둘 이상인 배열을 거부합니다.
- 숫자는 최대 64자의 일반 소수 표기와 지수 표기를 허용합니다. 예: `3`, `-2.5`, `1e3`.

## 검증 오류

검증에 실패한 제출은 `422 validation_failed`로 거부되며 아무것도 저장되지 않습니다. `error.fields`는 각 필드 ID와 그 메시지를 연결합니다. 브라우저 POST에서는 같은 메시지가 필드 레이블과 함께 호스팅 오류 페이지에 표시됩니다.

`422 Unprocessable Content`:

```json
{
  "ok": false,
  "error": {
    "code": "validation_failed",
    "message": "Some fields are invalid.",
    "fields": {
      "email": ["Enter a valid email address"],
      "website": ["Unknown field \"website\""]
    }
  },
  "request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}
```

### 알 수 없는 필드

필드가 정의되어 있으면 정의에 없는 필드는 `Unknown field "name"`으로 거부됩니다. `_`로 시작하는 예약 필드, 허니팟, `cf-turnstile-response`는 해당하지 않습니다. 정의가 없으면 어떤 필드 이름이든 허용됩니다.

### 메시지

| 문제 | 메시지 |
| --- | --- |
| 필수 값 누락 | `This field is required` |
| 필수 체크박스 미선택 | `This box must be checked` |
| 너무 짧거나 김 | `Must be at least 10 characters`, `Must be at most 120 characters` |
| 패턴 불일치 | 설정한 `pattern_message`, 또는 `Invalid format` |
| 이메일 | `Enter a valid email address`, `Email must be at one of: example.com` |
| 숫자 | `Enter a number`, `Enter a whole number`, `Must be at least 1` |
| 옵션 | `Choose one of the listed options`, `Choose at least 2 options`, `Choose at most 3 options` |
| 날짜 | `Enter a valid date (YYYY-MM-DD)`, `Must be on or after 2026-01-01` |
| URL | `Enter a valid URL`, `Enter a valid https:// URL` |
| 전화번호 | `Enter a valid phone number`, `Use international format, e.g. +14155550123` |
| 평점 | `Choose a rating`, `Rating must be at most 5` |

## 브라우저에서 검증하기

SDK의 `validate`는 12가지 필드 타입 모두에서 서버의 규칙과 메시지를 그대로 따르므로, POST하기 전에 오류를 보여 줄 수 있습니다. 최종 판단은 여전히 서버가 합니다. [validate](https://ikiform.com/ko/docs/sdk.md#validate)를 참고하세요.
