JavaScript와 fetch
제출 엔드포인트 자세히 보기: 본문 형식, 헤더, CORS, 응답, 멱등성.
HTTP 요청을 보낼 수 있는 코드라면 무엇이든 Ikiform에 제출할 수 있습니다. JSON, FormData, urlencoded 데이터를 https://ikiform.com/f/<formId>로 POST하고 JSON 응답을 읽으세요. SDK는 이 과정을 함수 하나로 감싸지만 꼭 필요하지는 않습니다. 특정 스택의 단계별 설정은 플랫폼별 가이드를 참고하세요.
fetch 사용
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({
name: "Ada Lovelace",
email: "ada@example.com",
message: "Hello",
}),
});
const result = await response.json();
if (!result.ok) console.error(result.error.code, result.error.fields);직접 해 보기
curl https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{"name":"Ada Lovelace","email":"ada@example.com","message":"Hello from the Ikiform docs."}'실제 요청으로 보내려면 내 폼 ID를 입력하세요.
엔드포인트
| 메서드 | 경로 | 하는 일 |
|---|---|---|
POST | /f/<formId> | 제출 생성 |
OPTIONS | /f/<formId> | CORS 프리플라이트, 항상 204 |
GET | /f/<formId>/schema | 폼의 공개 필드 정의, shadcn/ui 참고 |
GET | /f/<formId>/thanks | 호스팅 감사 페이지 |
GET | /f/<formId>/challenge | SDK가 사용하는 Ikiform 사람 확인 프레임 |
브라우저 탭에서 /f/<formId> 자체를 열면 GET 요청이 되어 404 not_found로 응답합니다. 정상입니다. 이 엔드포인트는 POST만 받습니다.
본문 형식
| Content-Type | 형태 | 참고 |
|---|---|---|
application/json 및 +json 타입 | JSON 객체 하나 | 값은 문자열, 숫자, 불리언, 배열, 객체가 될 수 있으며, 최상위 객체를 포함해 최대 3단계까지 중첩할 수 있습니다 |
application/x-www-form-urlencoded | 이름과 값의 쌍 | 반복되는 이름과 name[]은 배열이 됩니다 |
multipart/form-data | 이름과 값의 쌍 | urlencoded와 같음. 실제 파일은 거부됩니다 |
| 그 외 | 415 unsupported_media_type |
본문은 UTF-8이어야 합니다. utf-8이 아닌 charset은 415 unsupported_charset을 받고, 올바른 UTF-8이 아닌 바이트도 같은 코드를 받습니다. JSON 배열이나 형식이 잘못된 본문은 400 invalid_body를 받습니다.
헤더
요청 헤더
| 헤더 | 필수 | 효과 |
|---|---|---|
Content-Type | 예 | 본문 파서를 고릅니다. 위 내용 참고 |
Accept | 아니요 | application/json이면 JSON으로 응답합니다. JSON 또는 리디렉션 참고 |
Idempotency-Key | 아니요 | 재시도를 안전하게 만듭니다. 최대 255자, 멱등성 참고 |
Authorization | 아니요 | Bearer ik_sk_... 또는 Bearer ik_pub_..., 서버에서 보내기 참고 |
Origin, Referer | 브라우저가 보냄 | 폼의 허용 도메인과 비교합니다 |
응답 헤더
| 헤더 | 시점 | 값 |
|---|---|---|
X-Request-Id | 모든 응답 | req_..., 오류 본문에 request_id로도 포함 |
Retry-After | 429 | 다음 POST가 허용될 때까지 남은 초 |
Location | 브라우저 성공 시 303 | 리디렉션 주소 또는 호스팅 감사 페이지 |
Access-Control-Allow-Origin | 요청에 Origin이 있을 때 | 요청 자신의 출처 |
Access-Control-Expose-Headers | CORS 사용 시 | X-Request-Id, Retry-After, 브라우저 코드가 둘 다 읽을 수 있도록 |
Cache-Control | 성공 시 | no-store |
CORS
제출 엔드포인트는 어떤 사이트에서 오는 교차 출처 POST든 받습니다. 프리플라이트는 항상 성공하며 요청의 출처를 그대로 돌려줍니다. 쿠키나 자격 증명을 쓰지 않으므로, 폼을 보호하는 것은 CORS가 아니라 허용 도메인입니다. 허용 도메인을 설정하면 다른 사이트에서 온 POST는 폼 설정에 따라 스팸으로 저장되거나 거부됩니다. 허용 도메인을 참고하세요.
OPTIONS /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Accept, Idempotency-Key, Authorization
Access-Control-Max-Age: 86400
Vary: Origin응답
성공하면 새 제출 ID와 함께 201을 반환합니다. 오류는 어디서나 같은 형식을 사용하며, 검증에 실패하면 필드 ID를 키로 필드 오류를 담습니다. 모든 코드는 오류에 정리되어 있습니다.
{ "ok": true, "id": "sub_01k7v2c9t3e8m5q4x6z0a1b2c3" }201을 받습니다. 스팸은 따로 저장되며 웹훅이나 받은편지함에 닿지 않습니다.멱등성
재시도를 안전하게 하려면 Idempotency-Key 헤더나 _idempotency 필드를 보내세요. 키는 최대 255자이며 24시간 동안 폼 단위로 적용됩니다. 둘 다 보내면 헤더가 우선합니다.
| 재시도 조건 | 결과 |
|---|---|
| 같은 키, 같은 필드 | 첫 응답을 그대로 돌려줍니다. 두 번째 제출은 만들어지지 않습니다. |
| 같은 키, 다른 필드 | 422 idempotency_mismatch |
| 첫 요청이 아직 처리 중일 때 같은 키 | 409 idempotency_in_progress |
검증 오류처럼 제출이 저장되기 전에 실패한 POST는 키를 보관하지 않으므로, 데이터를 고친 뒤 같은 키로 다시 시도할 수 있습니다. 안전한 재시도 루프:
const key = crypto.randomUUID();
async function send(attempt = 1) {
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": key },
body: JSON.stringify({ email: "ada@example.com" }),
}).catch(() => null);
if (response && response.status < 500 && response.status !== 429) return response.json();
if (attempt === 3) throw new Error("Ikiform is unreachable");
const wait = Number(response?.headers.get("Retry-After")) || attempt * 2;
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return send(attempt + 1);
}플랫폼별 가이드
프레임워크
- Next.js클라이언트 컴포넌트 또는 서버 액션.
- React대기, 오류, 성공 상태.
- Vue싱글 파일 컴포넌트.
- Nuxt하이드레이션 전에는 일반 POST, 이후에는
$fetch. - SvelteSvelte 5 컴포넌트와 서버 액션.
- SvelteKit점진적 향상을 적용한 프리렌더링 페이지.
- AstroJavaScript 없는 폼, 원하면 향상 적용.
- Gatsby정적 폼 또는 Gatsby Function.
- 순수 JavaScript인라인 오류를 표시하는
fetch.