Next.js
Next.js App Router 클라이언트 컴포넌트에서 제출하거나, 시크릿 키를 사용하는 서버 액션으로 제출하세요.
Next.js에서 제출하는 좋은 방법은 두 가지입니다. 클라이언트 컴포넌트에서 브라우저가 POST하면 방문자마다 자신의 IP로 레이트 리밋이 적용되고 모든 스팸 검사와 함께 작동합니다. 서버 액션에서 POST하면 JavaScript 없이도 폼이 작동합니다.
| Item | 클라이언트 컴포넌트 | 서버 액션 |
|---|---|---|
| JavaScript 없이 작동 | 아니요 | 예 |
| 레이트 리밋 집계 기준 | 방문자별 | 서버의 IP |
| Ikiform 사람 확인 | SDK의 challenge 사용 | 시크릿 키로 건너뜀 |
| 허용 도메인 | 브라우저의 출처와 비교 | 시크릿 키로 건너뜀 |
| 시크릿 키 필요 | 아니요 | 예 |
SDK 설치
npm install @ikiform/sdkSDK는 선택 사항입니다. 아래 내용은 모두 fetch로도 작동합니다. JavaScript와 fetch를 참고하세요.
클라이언트 컴포넌트에서 보내기
"use client";
import { submit } from "@ikiform/sdk";
import { useState } from "react";
type Status = "idle" | "sending" | "sent" | "error";
export function ContactForm() {
const [status, setStatus] = useState<Status>("idle");
const [errors, setErrors] = useState<Record<string, string[]>>({});
const [message, setMessage] = useState("");
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
setStatus("sending");
const result = await submit("frm_xxxxxxxxxxxxxxxx", new FormData(event.currentTarget));
if (result.ok) return setStatus("sent");
setErrors(result.error.fields ?? {});
setMessage(result.error.message);
setStatus("error");
}
if (status === "sent") return <p>Thanks, we got it.</p>;
return (
<form onSubmit={onSubmit} noValidate>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" required aria-invalid={Boolean(errors.email)} />
{errors.email ? <p>{errors.email[0]}</p> : null}
<label htmlFor="message">Message</label>
<textarea id="message" name="message" />
<input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
{status === "error" && !Object.keys(errors).length ? <p role="alert">{message}</p> : null}
<button disabled={status === "sending"}>{status === "sending" ? "Sending" : "Send"}</button>
</form>
);
}submit은 예외를 던지지 않습니다.{ ok: true, id }또는{ ok: false, error, status, requestId }로 resolve됩니다.- 폼에 필드가 정의되어 있으면
error.fields가 필드 ID와 메시지를 연결합니다. app/contact/page.tsx처럼 아무 서버 컴포넌트 페이지에서 컴포넌트를 렌더링하세요.
Ikiform 사람 확인과 함께 사용
폼의 스팸 검사가 Ikiform이라면 제출할 때마다 직전에 토큰을 받으세요. challenge는 Turnstile에 클릭이 필요할 때만 보이는 작은 ikiform.com 프레임을 마운트합니다.
const data = new FormData(event.currentTarget);
const turnstileToken = await challenge("frm_xxxxxxxxxxxxxxxx");
const result = await submit("frm_xxxxxxxxxxxxxxxx", data, { turnstileToken });import { challenge, submit } from "@ikiform/sdk";로 가져오세요. 토큰은 한 번만 쓸 수 있으므로 제출마다 한 번씩 호출하세요.
서버 액션에서 보내기
대시보드의 API keys에서 submissions:write 스코프로 시크릿 키를 만들고 환경 변수로 저장하세요. 브라우저에 절대 노출하지 마세요. NEXT_PUBLIC_ 접두사를 붙이면 안 됩니다.
IKIFORM_SECRET_KEY=ik_sk_your_secret_key"use server";
export interface ContactState {
ok: boolean;
message: string;
fields?: Record<string, string[]>;
}
export async function sendContact(_previous: ContactState, formData: FormData): Promise<ContactState> {
const body = new URLSearchParams();
for (const [name, value] of formData) {
if (typeof value === "string" && !name.startsWith("$ACTION")) body.append(name, value);
}
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: {
Accept: "application/json",
Authorization: `Bearer ${process.env.IKIFORM_SECRET_KEY}`,
},
body,
});
const result = await response.json();
if (result.ok) return { ok: true, message: "Thanks, we got it." };
return { ok: false, message: result.error.message, fields: result.error.fields };
}"use client";
import { useActionState } from "react";
import { type ContactState, sendContact } from "./actions";
const initial: ContactState = { ok: false, message: "" };
export function ContactForm() {
const [state, action, pending] = useActionState(sendContact, initial);
if (state.ok) return <p>{state.message}</p>;
return (
<form action={action}>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" required />
{state.fields?.email ? <p>{state.fields.email[0]}</p> : null}
<label htmlFor="message">Message</label>
<textarea id="message" name="message" />
{state.message && !state.fields ? <p role="alert">{state.message}</p> : null}
<button disabled={pending}>{pending ? "Sending" : "Send"}</button>
</form>
);
}SDK의 submit은 서버 액션에서도 쓸 수 있지만 Authorization 헤더를 보내지 않으므로, 허용 도메인이나 Ikiform 사람 확인을 쓰는 폼은 POST를 거부하거나 스팸으로 표시합니다. 예제처럼 시크릿 키와 함께 fetch를 사용하세요.
라우트 핸들러에서 웹훅 받기
Next.js 앱에서 제출마다 작업을 처리하려면 라우트 핸들러를 추가하세요. 웹훅 받기를 참고하세요.