가이드

가이드11분 분량

HTML 폼 제출이 동작하는 방식

폼이 제출될 때 브라우저가 하는 일: 어떤 필드를 보내는지, 어떻게 인코딩하는지, 서버가 어떻게 응답해야 하는지, 그리고 Ikiform이 어떻게 응답하는지 설명합니다.

Preet SutharIkiform 창업자

누군가 제출을 누르면 브라우저는 폼의 제약 조건을 확인하고, 이름이 있고 활성화된 모든 컨트롤을 모아 폼의 enctype으로 인코딩한 뒤, 폼의 method로 action URL에 보냅니다. 서버는 일반 페이지 로드처럼 응답하며, 잘 동작하는 서버는 성공한 POST에 303 리디렉션으로 응답해 새로고침해도 폼이 두 번 전송되지 않게 합니다. action이 Ikiform 같은 폼 백엔드를 가리키게 하면 자체 서버 없이 그 리디렉션, 필드 검증, 스팸 필터링을 얻습니다.

HTML 폼은 JavaScript보다 오래되었고 지금도 웹 페이지에서 데이터를 보내는 가장 안정적인 방법입니다. 스크립트가 꺼져 있어도, 느린 휴대폰에서도, 마크업을 붙여 넣을 수 있는 모든 빌더에서 동작합니다. 폼 버그의 대부분은 브라우저가 스스로 하는 일을 모르는 데서 생기므로, 이 가이드는 제출을 단계별로 살펴본 뒤 폼 백엔드가 어디에 들어가는지 보여 줍니다.

제출을 누르면 일어나는 일

제출은 본문이 붙은 일반 탐색입니다. 브라우저는 페이지에서 무언가 나가기 전에 네 가지 일을 한 뒤, 요청을 네트워크에 넘기고 돌아오는 것을 불러옵니다.

제출 클릭과 다음 페이지 사이에 브라우저가 하는 일브라우저에서네트워크를 거쳐제출버튼, Enter 키또는requestSubmit()제약 조건 확인required,type, pattern,min, max 확인폼 데이터 세트이름이 있고 활성화된컨트롤만인코딩urlencoded 또는multipart필드에 메시지 표시아무것도 전송 안 됨action으로POSTSec-Fetch-Mode:navigate서버 응답303 SeeOtherLocation을GET감사 페이지유효유효하지 않음따라감제출 클릭과 다음 페이지 사이에 브라우저가 하는 일브라우저에서네트워크를 거쳐제출버튼, Enter 키 또는requestSubmit()제약 조건 확인required, type,pattern, min, max 확인폼 데이터 세트이름이 있고 활성화된 컨트롤만인코딩urlencoded 또는multipart필드에 메시지표시아무것도 전송 안 됨action으로 POSTSec-Fetch-Mode:navigate서버 응답303 See OtherLocation을 GET감사 페이지유효따라감유효하지 않음
  1. 제출 버튼, 텍스트 필드의 Enter 키, 또는 form.requestSubmit()이 제출을 시작하고 submit 이벤트를 발생시킵니다.
  2. 폼에 novalidate가 없으면 브라우저가 모든 컨트롤의 제약 조건을 확인합니다. 첫 번째로 유효하지 않은 컨트롤에 메시지가 표시되고 아무것도 전송되지 않습니다.
  3. 브라우저가 이름이 있고 비활성화되지 않은 모든 컨트롤로 폼 데이터 세트를 만듭니다.
  4. 따로 지정하지 않으면 urlencoded인 폼의 enctype으로 그 세트를 인코딩합니다.
  5. 폼의 method로 action URL에 이동하며 본문을 보냅니다.
  6. 서버가 응답합니다. 303은 브라우저에 다른 URL을 GET으로 불러오라고 알리며, 방문자가 최종적으로 보게 되는 페이지가 됩니다.
선 위의 모든 일은 네트워크 없이 일어납니다. submit 이벤트 리스너는 첫 단계에서 흐름을 멈추고 fetch로 넘겨받을 수 있습니다.

요청을 결정하는 속성

<form>의 다섯 가지 속성이 데이터가 어디로 어떻게 가는지 정합니다. 각각은 대응하는 form* 속성으로 버튼 하나에 대해 덮어쓸 수 있으며, 그래서 한 폼에 서로 다르게 동작하는 Send와 Save draft 버튼을 둘 수 있습니다.

속성기본값하는 일버튼별 덮어쓰기
action페이지 자체의 URL데이터를 받는 URL입니다. 상대 값은 페이지를 기준으로 해석됩니다. action 속성을 참고하세요formaction
methodgetget은 필드를 URL의 쿼리 문자열에 넣습니다. post는 본문으로 보냅니다. dialog는 아무것도 보내지 않고 <dialog>를 닫습니다formmethod
enctypeapplication/x-www-form-urlencodedpost 본문을 인코딩하는 방식입니다. 파일에는 multipart/form-data가 필요합니다. text/plain은 디버깅용입니다formenctype
novalidate끔제출 시 브라우저의 제약 조건 확인을 건너뜁니다formnovalidate
target_self응답이 불러와지는 곳입니다. _blank는 새 탭을 열고, 이름이 있는 iframe은 페이지를 그대로 둡니다formtarget

버튼 두 개, 동작 두 가지. 두 번째 버튼은 검증을 건너뛰며, 클릭한 버튼의 name과 value가 데이터에 추가되므로 서버는 어느 버튼이 눌렸는지 압니다.

제출 버튼 두 개
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <input name="email" type="email" required />
  <textarea name="details" required></textarea>
  <button name="intent" value="report">Send report</button>
  <button name="intent" value="draft" formnovalidate>Save as draft</button>
</form>

전송되는 컨트롤

브라우저는 HTML 명세가 폼 데이터 세트라고 부르는 것만 보냅니다. 제출 로그의 놀라운 점 대부분은 다음 규칙에서 옵니다.

  • 컨트롤에는 name이 필요합니다. id만 있으면 아무것도 보내지 않습니다.
  • disabled 컨트롤은 건너뜁니다. 값을 보여 주고 잠그면서 보내려면 readonly를 쓰세요.
  • 체크하지 않은 체크박스나 라디오는 false가 아니라 아무것도 보내지 않습니다. value가 없는 체크된 체크박스는 on을 보냅니다.
  • 폼을 제출한 버튼만 name과 value와 함께 포함됩니다.
  • <select multiple>과 이름을 공유하는 체크박스 그룹은 선택된 값마다 쌍을 하나씩 보냅니다.
  • <form> 요소 밖의 입력은 폼의 id를 지정하는 form 속성으로 폼에 합류할 수 있습니다.

본문이 인코딩되는 방식

기본 enctype에서 본문은 &로 이어진 name=value 쌍 한 줄이며, 공백은 +가 되고 안전한 문자 집합 밖의 나머지는 퍼센트 인코딩됩니다. 아래쪽 신고 폼에 대해 브라우저가 실제로 보내는 요청은 다음과 같습니다.

application/x-www-form-urlencoded
POST /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Host: ikiform.com
Content-Type: application/x-www-form-urlencoded
Origin: https://example.com
Sec-Fetch-Mode: navigate

email=ada%40example.com&page=https%3A%2F%2Fexample.com%2Fpricing&severity=broken&details=The+annual+toggle+does+nothing&_redirect=https%3A%2F%2Fexample.com%2Freport-sent&_gotcha=

multipart/form-data는 브라우저가 고른 경계 문자열로 각 필드를 별도 파트로 감쌉니다. 더 크지만 파일을 담을 수 있습니다. Ikiform은 둘 다 읽습니다. text/plain은 값이 어디서 끝나는지 확실히 알 방법이 없으므로 Ikiform이 415 unsupported_media_type으로 거부합니다.

multipart/form-data
POST /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Host: ikiform.com
Content-Type: multipart/form-data; boundary=----formdata7f3a

------formdata7f3a
Content-Disposition: form-data; name="email"

ada@example.com
------formdata7f3a
Content-Disposition: form-data; name="severity"

broken
------formdata7f3a--
인코딩이 무엇이든 페이지는 UTF-8로 유지하세요. Ikiform은 모든 본문을 UTF-8로 디코딩하고 다른 문자셋은 415 unsupported_charset으로 거부합니다. 파일 업로드는 아직 지원하지 않습니다. 파일이 있는 POST는 422 files_not_supported를 받습니다.

폼을 제출하는 네 가지 방법

일반 POST는 선택지 중 하나일 뿐입니다. 아래의 각 방식은 어딘가에서는 타당한 선택입니다. 달라지는 것은 누가 응답을 처리하는지, 그리고 스크립트가 꺼졌을 때 무슨 일이 일어나는지입니다.

방식잘하는 것단점
리디렉션을 쓰는 일반 method="post"어떤 빌더에서든 JavaScript 없이 동작합니다. 브라우저가 로딩 상태, 뒤로 가기, 새로고침을 처리합니다페이지 전체를 다시 불러옵니다. 필드 오류에는 서버 렌더링 페이지나 백엔드의 오류 페이지가 필요합니다
FormData를 쓰는 fetch새로고침이 없습니다. 성공과 각 필드 오류의 모습을 직접 정합니다로딩 상태, 네트워크 오류, 포커스를 직접 맡아야 합니다. 신경 쓰지 않으면 스크립트가 꺼졌을 때 깨집니다. 페이지 새로고침 없이를 참고하세요
Next.js 서버 액션이나 SvelteKit 폼 액션 같은 프레임워크 폼 액션서버가 POST를 먼저 보며, 같은 폼이 하이드레이션 전후 모두 동작합니다서버 런타임이 필요하고, 백엔드로 가는 요청이 서버에서 출발하므로 레이트 리밋과 origin 확인이 달라집니다
method="get"검색 및 필터 폼. 결과에 공유 가능한 URL이 생깁니다필드가 URL, 방문 기록, 서버 로그에 남습니다. 개인 데이터나 상태를 바꾸는 것에는 절대 쓰지 마세요

일반 POST로 시작하고 그 위에 fetch를 더하세요. 그러면 스크립트 로드에 실패해도 폼이 동작하며, 이런 일은 대부분의 분석 도구가 보여 주는 것보다 자주 일어납니다.

서버가 응답해야 하는 방식

브라우저는 POST에 대한 응답을 새 페이지로 취급합니다. 그 페이지가 상태 200의 응답 자체라면 브라우저는 그것이 POST에서 왔다고 기억합니다. 새로고침을 누르면 폼을 다시 보내겠냐고 묻고, 뒤로 갔다 앞으로 가면 일부 브라우저도 같은 일을 합니다. 그렇게 두 번 결제되고, 고객 지원에 같은 메시지가 세 번 옵니다.

해결책은 Post/Redirect/Get 패턴입니다. 서버가 POST를 처리한 뒤 Location 헤더와 함께 303 See Other로 응답합니다. 브라우저는 그 URL을 GET으로 불러오고, 방문자가 보는 페이지는 GET에서 왔으므로 새로고침해도 문제가 없습니다.

POST에 페이지로 응답하는 것과 303 리디렉션으로 응답하는 것의 비교페이지로 응답 (200)리디렉션으로 응답 (303)POST/report200 감사HTML페이지가 POST에서 옴새로고침"양식 다시 제출 확인"다시 POST중복 발생POST/report303 SeeOtherLocation:/report-sent이동GET/report-sent새로고침GET만 다시 불러옴POST에 페이지로 응답하는 것과 303 리디렉션으로 응답하는 것의 비교페이지로 응답 (200)리디렉션으로 응답 (303)POST /report200 감사 HTML페이지가 POST에서 옴새로고침"양식 다시 제출 확인"다시 POST중복 발생POST /report303 See OtherLocation: /report-sent 이동GET /report-sent새로고침GET만 다시 불러옴
  1. 200 응답이면 브라우저가 폼을 POST하고 응답 본문을 페이지로 보여 줍니다.
  2. 그 페이지는 POST에 묶여 있으므로 새로고침하면 다시 제출할지 묻고, 확인하면 데이터를 다시 보냅니다.
  3. 303 응답이면 브라우저가 폼을 POST하고 다른 URL을 불러오라는 안내를 받습니다.
  4. 그 URL을 GET으로 불러옵니다. 새로고침은 GET만 반복하므로 아무것도 두 번 전송되지 않습니다.
Ikiform은 브라우저 POST에 항상 303으로 응답하므로 폼은 이 동작을 공짜로 얻습니다.
상태브라우저가 다음에 하는 일사용처
303 See OtherLocation을 GET, 본문은 버림성공한 모든 폼 POST 뒤
302 Found실제로 브라우저는 GET으로 바꾸지만, 명세는 메서드 유지를 허용합니다오래된 서버. 303을 권장
307과 308같은 본문으로 같은 POST를 새 URL에 반복엔드포인트 이전용이며, 감사 페이지에는 절대 쓰지 않음
HTML과 함께 200페이지를 보여 줌. 새로고침하면 다시 POST할지 물음오류와 함께 폼을 다시 보여 줄 때
HTML과 함께 4xx오류 페이지를 보여 줌방문자가 뒤로 가서 고칠 수 있는 오류
Post/Redirect/Get
HTTP/1.1 303 See Other
Location: https://example.com/report-sent
X-Request-Id: req_01k7v2c9t3e8m5q4x6z0a1b2c3

GET /report-sent HTTP/1.1
Host: example.com

Ikiform이 응답하는 방식

엔드포인트 하나 https://ikiform.com/f/<formId>가 브라우저와 코드 모두에 응답합니다. 브라우저가 스스로 설정하는 헤더를 보고 요청마다 페이지로 응답할지 JSON으로 응답할지 정합니다.

요청응답
Accept에 application/json 포함JSON
본문이 JSONJSON
Sec-Fetch-Mode: navigate, 모든 일반 폼 POST리디렉션 또는 호스팅 페이지
fetch의 cors 같은 그 밖의 Sec-Fetch-ModeJSON
Sec-Fetch-Mode가 없고 Accept에 text/html 포함리디렉션 또는 호스팅 페이지
curl 같은 그 밖의 모든 것JSON

Sec-Fetch-Mode는 브라우저가 설정하며 페이지 스크립트가 바꿀 수 없는 헤더입니다. 탭을 이동시키는 폼 POST는 navigate를 보내고, fetch 호출은 크로스 사이트 요청에 cors를 보냅니다. 그래서 같은 폼이 일반 방식으로 POST하면 리디렉션을, 스크립트가 POST하면 JSON을 받으며, 직접 전환할 것이 없습니다. 전체 표는 JSON 또는 리디렉션에 있습니다.

일반 폼 POST와 fetch 호출에 응답하는 같은 Ikiform 엔드포인트일반 폼 POSTfetch 또는 Accept: application/json브라우저가 이동Sec-Fetch-Mode:navigateIkiform페이지 모드내 페이지로 303또는 호스팅 감사 페이지호스팅 오류 페이지필드 레이블별 문제, 실제 상태스크립트가 POSTSec-Fetch-Mode: cors전송IkiformJSON 모드id와 함께 201{ ok: true, id }JSON 오류필드 id별 error.fields거부됨거부됨일반 폼 POST와 fetch 호출에 응답하는 같은 Ikiform 엔드포인트일반 폼 POSTfetch 또는 Accept: application/json브라우저가 이동Sec-Fetch-Mode:navigateIkiform페이지 모드내 페이지로 303또는 호스팅 감사 페이지호스팅 오류페이지필드 레이블별 문제,실제 상태스크립트가 POSTSec-Fetch-Mode:cors 전송IkiformJSON 모드id와 함께 201{ ok: true, id }JSON 오류필드 id별error.fields거부됨거부됨
  1. 일반 폼 POST는 탭을 이동시키므로 브라우저가 Sec-Fetch-Mode: navigate를 보냅니다.
  2. Ikiform은 페이지 모드로 응답합니다. 성공하면 303 리디렉션, 아니면 오류의 실제 상태가 담긴 호스팅 오류 페이지입니다.
  3. fetch 호출은 Sec-Fetch-Mode: cors를 보내거나, 코드가 Accept 헤더로 JSON을 요청합니다.
  4. Ikiform은 JSON 모드로 응답합니다. 제출 id와 함께 201, 또는 필드 id별 필드 오류가 담긴 오류 봉투입니다.
모드는 설정하지 않습니다. 요청이 만들어진 방식에 따라 정해집니다.

성공한 POST가 도착하는 곳

성공한 브라우저 POST는 303을 받습니다. Ikiform은 다음 순서로 목적지를 고릅니다.

  1. 대시보드에 있는 폼의 Redirect after submit 설정. 내 워크스페이스만 설정할 수 있으므로 모든 http: 또는 https: URL이 동작합니다.
  2. POST의 _redirect 필드, _redirect가 없으면 _next. 사용자 이름, 비밀번호, 커스텀 포트가 없는 https: URL이고, 폼의 Allowed domains 중 하나이거나 그 서브도메인일 때입니다.
  3. 그 밖에는 /f/<formId>/thanks의 호스팅 감사 페이지. 허용 도메인의 https: 페이지에서 온 POST라면 그곳으로 돌아가는 링크가 있습니다.

규칙을 어긴 _redirect는 오류가 아니라 무시되며, 허용 도메인이 없는 폼은 _redirect를 아예 무시합니다. 그래서 누구도 내 폼을 이용해 방문자를 자기가 고른 사이트로 보낼 수 없습니다. 제출 후 리디렉션이 감사 페이지를 깊이 다루고, 전체 규칙은 HTML 폼에 있습니다.

POST가 거부될 때

페이지 모드에서 거부된 POST는 오류의 실제 HTTP 상태가 담긴 호스팅 오류 페이지를 보여 줍니다. 검증 오류라면 각 문제를 필드 레이블 아래에 나열하고 POST가 출발한 페이지로 링크합니다. 브라우저의 Back 버튼은 대개 방문자가 입력한 내용이 남은 폼으로 돌아가고, 링크는 페이지를 새로 불러옵니다. JSON 모드에서는 같은 오류가 하나의 봉투로 돌아옵니다. 모든 코드는 오류에 있습니다.

JSON 모드의 422
{
  "ok": false,
  "error": {
    "code": "validation_failed",
    "message": "Some fields are invalid.",
    "fields": {
      "page": ["Enter a valid URL"],
      "details": ["Must be at least 20 characters"]
    }
  },
  "request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}

스팸만은 절대 오류처럼 보이지 않습니다. 스팸으로 판정된 POST도 실제 POST와 같은 303이나 201을 받으므로 봇은 아무것도 알아내지 못하며, 이메일이나 웹훅도 보내지 않습니다.

내 스택의 폼

다음은 모든 스택으로 만든 실제 폼, 깨진 페이지 신고 폼입니다. 스택을 고르면 그 선택이 이 가이드들의 모든 코드 샘플에 이어집니다. 아래 필드 정의는 Ikiform이 서버에서 강제하는 것과 같으니 시험해 보세요. 상세 내용을 짧게 쓰거나 주소에 example을 입력하면 폼이 받을 422를 받게 됩니다.

미리보기

Ikiform의 검증기를 브라우저에서 실행합니다. 아무것도 전송되지 않습니다.

대시보드에서 폼의 Fields 탭에 이 정의를 붙여 넣으면 Ikiform이 모든 POST를 이에 맞춰 검증합니다. 필드와 검증을 참고하세요.

스크립트가 전혀 없습니다. 브라우저가 required, type, minlength를 확인하고, 필드를 urlencoded 텍스트로 POST하고, Ikiform의 303을 따라 _redirect 페이지로 가며, 서버가 POST를 거부하면 Ikiform의 오류 페이지를 보여 줍니다.

report.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="email">Your email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="page">Page address</label>
  <input id="page" name="page" type="url" required placeholder="https://example.com/pricing" />

  <label for="severity">How bad is it</label>
  <select id="severity" name="severity">
    <option value="cosmetic">Looks wrong</option>
    <option value="broken">Something doesn't work</option>
    <option value="blocking">I can't continue</option>
  </select>

  <label for="details">What happened</label>
  <textarea id="details" name="details" rows="5" minlength="20" maxlength="4000" required></textarea>

  <input type="hidden" name="_redirect" value="https://example.com/report-sent" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send report</button>
</form>
서버 측 버전은 시크릿 키로 POST하므로 Ikiform이 Allowed domains와 사람 확인을 건너뜁니다. 그러면 모든 POST가 서버의 IP에서 오고, 방문자당 분당 5회인 Standard 한도가 모든 방문자를 합쳐서 계산합니다. 서버를 거쳐 POST하는 폼에는 Relaxed 또는 Custom 레이트 리밋을 고르고, IKIFORM_SECRET_KEY는 서버 전용 환경 변수에 두세요.

각 서버 버전은 브라우저의 Origin 헤더가 있으면 전달합니다. 서버 측 fetch는 이 헤더를 보내지 않는데, Ikiform은 플랜이 없는 워크스페이스에서 무료 localhost 테스트를 판별하는 데 이 헤더를 쓰기 때문입니다.

  • 모든 버전이 실제 method를 유지하고, Ikiform으로 바로 POST하는 경우에는 실제 action도 유지합니다. 스크립트가 꺼져 있어도 폼은 제출되고 303을 받습니다.
  • 각 입력의 name이 정의의 필드 id입니다. page 대신 url처럼 일치하지 않으면 Unknown field "url"와 함께 422를 받습니다.
  • 숨겨진 _gotcha 입력이 허니팟입니다. 이를 채우는 봇은 조용히 스팸으로 저장됩니다.
  • JavaScript 버전은 각 필드의 첫 번째 메시지를 써서 각 입력 아래에 error.fields를 보여 줍니다. 일반 HTML 버전은 이를 Ikiform의 오류 페이지에 맡깁니다.

문서는 프레임워크별로 더 깊이 다룹니다. Next.js, Astro, SvelteKit, Nuxt, React, Vue, 서버에서를 참고하세요.

두 겹의 검증

브라우저 검증과 서버 검증은 하는 일이 다릅니다. 브라우저의 제약 조건 확인(required, type="email", minlength, pattern)은 즉각적인 피드백을 주지만 누구나 건너뛸 수 있습니다. 봇은 직접 POST하고, 스크립트는 확인을 전혀 실행하지 않는 form.submit()을 호출할 수 있습니다. 규칙이 실제로 지켜지는 곳은 서버뿐입니다.

규칙브라우저에서Ikiform 필드 정의에서
필수required"required": true
이메일type="email""type": "email", 선택적으로 allowed_domains
URLtype="url""type": "url", 선택적으로 https_only
길이minlength, maxlengthvalidation.min_length, max_length
나열된 선택지만봇이 무시하는 <select>options. 그 밖의 값은 거부됨
추가 필드 없음불가능정의에 없는 필드는 모두 거부됨

정의가 없으면 Ikiform은 어떤 필드든 받아 도착한 그대로 저장합니다. 정의를 게시하면 검증하고, 숫자와 체크박스를 실제 숫자와 boolean으로 저장하며, 무엇이든 저장하기 전에 422 validation_failed로 POST를 거부합니다. 검증에서 두 겹이 같은 말을 하도록 유지하는 방법을 보여 줍니다.

JavaScript로 제출하기

방문자를 페이지에 머물게 하려면 submit을 듣고, preventDefault()를 호출하고, fetch로 new FormData(form)을 보내세요. 처음부터 제대로 동작할지는 몇 가지 세부 사항이 결정합니다.

  • 본문이 FormData일 때는 Content-Type을 직접 설정하지 마세요. 브라우저가 경계와 함께 multipart/form-data를 설정합니다. 직접 쓴 헤더에는 경계가 없어 본문을 파싱할 수 없습니다.
  • Accept: application/json을 보내세요. Ikiform은 어차피 Sec-Fetch-Mode로 JSON을 고르지만, 이 헤더가 이를 명시하며 브라우저가 프리플라이트 없이 크로스 사이트로 보낼 수 있는 요청 헤더 중 하나입니다.
  • FormData나 URLSearchParams 본문은 단순 크로스 사이트 요청이므로 프리플라이트가 없습니다. Content-Type: application/json인 JSON 본문은 먼저 프리플라이트를 일으킵니다. Ikiform이 응답하지만 왕복 한 번의 비용이 듭니다.
  • form.submit()은 검증과 submit 이벤트를 건너뜁니다. form.requestSubmit()은 클릭할 때처럼 둘 다 실행합니다.
  • 클릭한 버튼의 name과 value가 중요하다면 두 번째 인수로 버튼을 넘기세요. new FormData(form, event.submitter)입니다.

로딩 상태, 포커스, 네트워크 오류를 포함한 전체 패턴은 페이지 새로고침 없이에 있습니다. JavaScript와 fetch 문서에 엔드포인트가 받는 모든 헤더와 본문 형식이 나열되어 있습니다.

상황별 가이드

각 페이지는 제출의 한 부분을 맡아 위 개요보다 더 깊이 다루며, 그 경우를 위한 코드가 있습니다.

작동 확인하기

폼마다 비공개 창에서 한 번씩 확인하세요. Ikiform에는 무료 플랜이 없습니다. 테스트는 무료입니다. 플랜이 없는 워크스페이스는 localhost에서 월 50건의 테스트 제출을 받습니다. 실제 운영하려면 월 $6부터 500건을 제공하는 Pro를 고르거나, 한 번 결제로 10,000건 $49 또는 50,000건 $149를 구매하세요. 크레딧은 만료되지 않습니다. 요금을 참고하세요.

  1. 폼의 Allowed domains에 사이트 도메인을 추가하고, 개발하는 동안에는 localhost도 추가하세요. 비워 두면 어디서든 POST를 받습니다.
  2. 브라우저 개발자 도구에서 JavaScript를 끄고 폼을 제출하세요. 여전히 303과 함께 감사 페이지로 이동해야 합니다.
  3. JavaScript를 켜고 Network 패널을 연 뒤 다시 제출하세요. /f/<formId>로 가는 요청이 JSON과 함께 201로 응답하고 페이지는 새로고침되지 않아야 합니다.
  4. 너무 짧은 메시지로 제출하세요. 일반 POST는 필드 레이블이 있는 Ikiform 오류 페이지를 보여 주고, 스크립트 버전은 필드 아래에 메시지를 보여 줍니다.
  5. 감사 페이지에서 새로고침을 누르세요. 아무것도 다시 제출되지 않고 두 번째 제출도 나타나지 않아야 합니다.
  6. 대시보드에서 Submissions를 여세요. 보낸 모든 필드가 담긴 새 제출이 있습니다.
  7. 허니팟을 채워서 _gotcha=bot으로 POST하세요. 여전히 성공 응답을 받지만, 제출은 스팸으로 저장되고 이메일이나 웹훅을 보내지 않습니다.

문제 해결

문제해결 방법
제출 뒤 405 Method Not Allowed폼이 POST를 받을 수 없는 자체 정적 호스트로 POST했습니다. action을 확인하세요. 상대 경로나 빈 값이 아니라 전체 https://ikiform.com/f/<formId> URL이어야 합니다.
탭에서 엔드포인트를 열면 404 not_found가 표시됨정상입니다. 탭은 GET을 보내고 엔드포인트는 POST만 받습니다. 폼의 method가 post인지 확인하세요. method가 없는 폼은 필드를 URL에 담아 GET을 보냅니다.
415 unsupported_media_type폼에 enctype="text/plain"이 있거나, 스크립트가 Ikiform이 읽지 않는 타입을 보냈습니다. enctype을 제거하거나 multipart/form-data를 쓰세요.
422 validation_failedPOST가 폼의 필드 정의와 맞지 않습니다. error.fields에 각 필드와 이유가 나옵니다. 입력의 name을 필드 ID와 맞추세요.
브라우저가 내 페이지가 아니라 Ikiform의 감사 페이지로 이동합니다_redirect는 도메인이 Allowed domains에 있고 https: URL일 때만 따릅니다. 도메인을 추가하거나, 대신 폼 설정에서 Redirect after submit을 설정하세요.
제출에서 필드가 빠짐name이 없거나, disabled이거나, 체크하지 않은 체크박스입니다. name을 주고, disabled 대신 readonly를 쓰거나, 체크하지 않은 상자가 false로 저장되도록 checkbox 필드를 정의하세요.
새로고침하면 폼을 다시 제출할지 물음페이지가 POST에 200으로 응답했습니다. Ikiform으로 바로 가는 POST는 항상 303을 받습니다. 중간에 자체 서버가 있다면 POST를 처리한 뒤 리디렉션하세요.
403 origin_not_allowed, 또는 모든 POST가 스팸으로 분류됨페이지의 도메인이 폼의 Allowed domains에 없습니다. 도메인을 추가하고, 로컬 테스트용으로 localhost도 추가하세요. 등록된 도메인의 하위 도메인은 허용됩니다.

질문

막히는 단계가 있거나 내 상황에 맞는 가이드가 필요하다면 이메일을 보내세요: hello@ikiform.com.