# HTML 폼

> JavaScript 없이 어떤 HTML 폼이든 Ikiform으로 POST하세요. 리디렉션, 예약 필드, 배열, 허니팟을 설명합니다.

일반 HTML 폼 하나로 연동이 끝납니다. 스크립트도, iframe도, 임베드도 필요 없습니다. `action`을 폼의 엔드포인트로 설정하고 마크업과 CSS는 그대로 두세요. 커스텀 HTML을 붙여 넣을 수 있는 사이트 빌더에서도 작동합니다. [사이트 빌더](https://ikiform.com/ko/docs/guides/site-builders.md)를 참고하세요.

## 기본 폼

`contact.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label>
    Name
    <input name="name" autocomplete="name" required />
  </label>
  <label>
    Email
    <input name="email" type="email" autocomplete="email" required />
  </label>
  <label>
    Message
    <textarea name="message" rows="5"></textarea>
  </label>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send</button>
</form>
```

- `method`는 `post`여야 합니다. 브라우저는 `application/x-www-form-urlencoded`로 보내고, `enctype`을 설정하면 `multipart/form-data`로 보냅니다. 둘 다 작동합니다.
- `name`이 있는 모든 컨트롤은 필드가 됩니다. 이름이 없는 컨트롤과 비활성화된 컨트롤은 브라우저가 보내지 않습니다.
- 값은 UTF-8 텍스트로 저장되며, NFC로 정규화되고, CRLF 줄바꿈은 LF로 바뀌고, 제어 문자는 제거됩니다.
- `required`나 `type="email"` 같은 브라우저 검증은 사용자에게 도움이 되지만 보안 검사는 아닙니다. Ikiform이 서버에서 규칙을 강제하도록 하려면 [필드](https://ikiform.com/ko/docs/fields.md)를 정의하세요.

## 제출 후

브라우저 POST가 성공하면 `303` 리디렉션으로 응답합니다. Ikiform은 다음 순서로 이동할 곳을 고릅니다.

1. 대시보드에 있는 폼의 **Redirect after submit** 설정. 워크스페이스 멤버만 설정할 수 있으므로 어떤 `http:` 또는 `https:` URL이든 사용할 수 있습니다.
2. POST에 포함된 `_redirect`(또는 `_next`) 필드. 아래 규칙을 통과한 경우에만 사용합니다.
3. 그 외에는 `/f/<formId>/thanks`에 있는 Ikiform의 호스팅 감사 페이지. 폼에 허용 도메인이 있고 그중 한 곳의 `https:` 페이지에서 POST가 왔다면, 감사 페이지에 그 페이지로 돌아가는 링크가 표시됩니다.

### _redirect 규칙

`_redirect` 값은 방문자의 브라우저에서 오므로, Ikiform은 사용자 이름, 비밀번호, 커스텀 포트가 없는 `https:` URL이면서 폼의 허용 도메인이나 그 하위 도메인에 있을 때만 따릅니다. 허용 도메인이 없는 폼은 `_redirect`를 완전히 무시합니다. 허용 도메인이 `example.com`일 때:

| _redirect | 따름 | 이유 |
| --- | --- | --- |
| `https://example.com/thanks` | 예 | 허용 도메인 |
| `https://app.example.com/done` | 예 | 허용 도메인의 하위 도메인 |
| `http://example.com/thanks` | 아니요 | `https:`가 아님 |
| `https://example.com:8443/thanks` | 아니요 | 커스텀 포트 |
| `https://user@example.com/` | 아니요 | 사용자 이름 포함 |
| `https://example.com.evil.dev/` | 아니요 | 해당 도메인이나 그 하위 도메인이 아님 |

따르지 않은 `_redirect`는 호스팅 감사 페이지로 넘어가며, 오류를 일으키지 않습니다. JSON 요청은 리디렉션을 무시하고 제출 ID와 함께 `201`을 받습니다.

### POST가 거부되면

필수 필드가 빠진 경우처럼 POST가 거부되면, 브라우저에는 오류의 HTTP 상태와 함께 호스팅 오류 페이지가 표시됩니다. 이 페이지는 문제를 필드 레이블별로 보여 주고 폼으로 돌아가는 링크를 제공합니다. 모든 코드는 [오류](https://ikiform.com/ko/docs/errors.md)를 참고하세요.

## 필드 이름과 배열

반복되는 이름과 `[]`로 끝나는 이름은 배열이 됩니다. 체크박스 그룹과 `<select multiple>`에는 `[]`를 사용하세요. 그러면 하나만 선택해도 배열로 저장됩니다.

`fields.html`:

```html
<fieldset>
  <legend>Topics</legend>
  <label><input type="checkbox" name="topics[]" value="billing" /> Billing</label>
  <label><input type="checkbox" name="topics[]" value="api" /> API</label>
</fieldset>

<select name="plan">
  <option value="starter">Starter</option>
  <option value="team">Team</option>
</select>

<label><input type="checkbox" name="newsletter" /> Send me product news</label>
```

두 주제와 뉴스레터 체크박스를 모두 선택하면 다음과 같이 저장됩니다.

`Stored fields`:

```json
{
  "topics": ["billing", "api"],
  "plan": "team",
  "newsletter": "on"
}
```

- `address[city]`처럼 대괄호가 있는 이름은 그대로 유지되며, 중첩 객체로 바뀌지 않습니다. 중첩된 값이 필요하면 JSON으로 POST하세요.
- 선택하지 않은 체크박스는 아예 전송되지 않습니다. `checkbox` [필드](https://ikiform.com/ko/docs/fields.md#types)를 정의하면 빠진 체크박스는 `false`로, 선택한 체크박스는 `true`로 저장됩니다.
- 제출 하나에는 필드를 최대 100개까지 넣을 수 있고, 이름은 최대 128자, 값은 최대 16 KB입니다. [한도](https://ikiform.com/ko/docs/limits.md)를 참고하세요.

> 파일 업로드는 아직 지원하지 않습니다. 빈 파일 입력은 무시되며, 실제 파일이 포함된 POST는 `422 files_not_supported`로 거부됩니다.

## 예약 필드

`_`로 시작하는 이름은 Ikiform에 대한 지시어입니다. 응답으로 저장되지 않습니다.

| 필드 | 효과 |
| --- | --- |
| `_gotcha` | 허니팟. 비워 두고 숨기세요. 값이 채워지면 스팸 점수에 5점이 더해지며, 이는 POST를 스팸으로 저장하기에 충분합니다. 이름은 폼마다 바꿀 수 있습니다. |
| `_redirect`, `_next` | POST가 성공한 뒤 브라우저를 보낼 곳. [_redirect 규칙](#redirect-rules)을 참고하세요. |
| `_subject` | 알림 이메일의 제목. 공백은 하나로 합쳐지고 200자로 잘립니다. |
| `_replyto` | 알림 이메일의 회신 주소. 올바른 이메일 주소 하나가 아니면 무시됩니다. |
| `_idempotency` | 멱등성 키로, `Idempotency-Key` 헤더와 같습니다. [멱등성](https://ikiform.com/ko/docs/frameworks.md#idempotency)을 참고하세요. |
| `_ikiform_ts` | SDK와 shadcn/ui 컴포넌트가 설정하는 서명된 렌더링 시각. 렌더링 후 2초 안에 보낸 POST는 스팸 점수를 받습니다. |
| `cf-turnstile-response` | Turnstile 위젯이나 SDK가 추가하는 Turnstile 토큰. |
| `_ikiform_via` | Ikiform의 사람 확인 페이지가 설정합니다. 직접 보내지 마세요. |

`_`로 시작하는 다른 필드도 모두 제거되므로, `_source` 같은 이름을 저장하지 않을 숨은 표시용으로 쓸 수 있습니다.

`demo.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <input name="email" type="email" required />
  <input type="hidden" name="_subject" value="New demo request" />
  <input type="hidden" name="_redirect" value="https://example.com/thanks" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Request a demo</button>
</form>
```

## 허니팟

기본 허니팟 이름은 `_gotcha`입니다. 폼 설정의 **Honeypot field**에서 폼마다 바꿀 수 있으며, 어떤 이름을 고르든 예약된 이름으로 취급되어 저장되지 않습니다. 대부분의 봇에는 일반 `hidden` 속성이면 충분합니다. 스팸이 통과한다면, 일부 봇은 `hidden`으로 표시된 입력을 건너뛰므로 대신 입력을 화면 밖으로 숨기세요.

`honeypot.html`:

```html
<div aria-hidden="true" style="position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden">
  <label>
    Leave this empty
    <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" />
  </label>
</div>
```

키보드 사용자와 비밀번호 관리자가 채우지 않도록 `tabindex="-1"`와 `autocomplete="off"`를 유지하세요.

## 사람 확인 추가하기

스팸 검사가 **Ikiform**으로 설정된 폼은 HTML을 바꿀 필요가 없습니다. 브라우저가 토큰 없이 POST하면 Ikiform은 ikiform.com에서 짧은 "Checking you're human" 페이지로 응답합니다. 이 페이지가 Cloudflare Turnstile을 실행한 뒤 원래 필드를 토큰과 함께 다시 POST합니다. 방문자는 이전과 같은 감사 페이지에 도착합니다. 전체 흐름은 [스팸 방지](https://ikiform.com/ko/docs/spam.md#human-check)를 참고하세요.

### 직접 발급한 Turnstile 키 사용

폼이 **Your own Turnstile keys**를 사용한다면 위젯을 직접 렌더링하세요. 위젯이 POST에 `cf-turnstile-response` 필드를 추가합니다. 올바른 토큰이 없는 POST는 `403 captcha_failed`로 거부됩니다.

`turnstile.html`:

```html
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <input name="email" type="email" required />
  <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
  <button>Send</button>
</form>
```
