Nuxt
하이드레이션 전에는 일반 폼으로, 이후에는 $fetch로 POST하는 Nuxt 컴포넌트. 폼 ID는 런타임 설정에 둡니다. nuxt generate와 함께 작동합니다.
Nuxt는 서버에서 폼을 렌더링하므로, 브라우저에 도착한 HTML은 이미 작동하는 폼입니다. 앱이 하이드레이션되기 전에는 일반 HTML 폼처럼 POST하고, 그 이후에는 컴포넌트가 이어받아 결과를 그 자리에 보여 줍니다. 서버에서 실행되는 것이 없으므로 nuxt generate로 빌드한 정적 사이트에서도 작동합니다.
대신 시크릿 키를 사용해 Nuxt 서버 라우트에서 POST하려면 Vue를 참고하세요.
설정하기
폼 ID를 런타임 설정에 넣기
nuxt.config.ts export default defineNuxtConfig({ runtimeConfig: { public: { ikiformFormId: "frm_xxxxxxxxxxxxxxxx", }, }, });환경마다
NUXT_PUBLIC_IKIFORM_FORM_ID로 덮어쓸 수 있습니다.nuxt generate를 쓰면 값이 빌드 시점에 고정되므로 빌드 환경에 변수를 설정하세요.컴포넌트 추가하기
app/components/ContactForm.vue <script setup lang="ts"> type Result = | { ok: true; id: string } | { ok: false; error: { message: string; fields?: Record<string, string[]> } }; const config = useRuntimeConfig(); const endpoint = `https://ikiform.com/f/${config.public.ikiformFormId}`; const status = ref<"idle" | "sending" | "sent" | "error">("idle"); const errors = ref<Record<string, string[]>>({}); const message = ref(""); async function onSubmit(event: Event) { status.value = "sending"; const result = await $fetch<Result>(endpoint, { method: "POST", headers: { Accept: "application/json" }, body: new FormData(event.currentTarget as HTMLFormElement), ignoreResponseError: true, }).catch(() => null); if (result?.ok) { status.value = "sent"; return; } errors.value = result?.error.fields ?? {}; message.value = result?.error.message ?? "Couldn't reach the server. Try again."; status.value = "error"; } </script> <template> <p v-if="status === 'sent'">Thanks, we got it.</p> <form v-else :action="endpoint" method="post" @submit.prevent="onSubmit"> <label> Email <input name="email" type="email" autocomplete="email" required /> </label> <p v-if="errors.email">{{ errors.email[0] }}</p> <label> Message <textarea name="message" rows="5" /> </label> <input type="hidden" name="_redirect" value="https://example.com/thanks" /> <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden /> <p v-if="status === 'error' && !errors.email" role="alert">{{ message }}</p> <button :disabled="status === 'sending'">{{ status === "sending" ? "Sending" : "Send" }}</button> </form> </template>- Nuxt 3에서는
app/components/대신components/에 넣으세요. ignoreResponseError: true를 쓰면$fetch가4xx에서 예외를 던지지 않으므로 오류 형식이 데이터로 돌아옵니다. 네트워크 오류만catch에 도달합니다.@submit.prevent는 앱이 하이드레이션된 뒤에만 존재합니다. 그 전에는 브라우저가action으로 POST하고_redirect를 따릅니다.
- Nuxt 3에서는
페이지에서 사용하기
app/pages/contact.vue <template> <ContactForm /> </template>
대체 동작에는 서버 렌더링이나 프리렌더링이 필요합니다. ssr: false이면 JavaScript가 실행될 때까지 페이지가 비어 있어서, JavaScript 없이 POST할 폼이 없습니다.
허용 도메인
프로덕션과 배포처의 미리보기 호스트를 등록하세요.
| 폼이 실행되는 곳 | Allowed domains에 추가할 값 |
|---|---|
nuxt dev | localhost. http://localhost:3000과 일치합니다. Ikiform은 포트가 아니라 호스트 이름만 비교합니다. |
| 여러분의 도메인 | example.com, www.example.com도 포함 |
| Vercel | your-project.vercel.app. your-project-git-branch-team.vercel.app 같은 미리보기 URL은 일치하지 않으므로, 테스트하는 동안 정확한 미리보기 호스트를 추가하세요. |
| Netlify | your-site.netlify.app. 배포 미리보기(deploy-preview-12--your-site.netlify.app)와 브랜치 배포(branch--your-site.netlify.app)는 하위 도메인이 아니므로, 테스트하는 동안 정확한 미리보기 호스트를 추가하세요. |
| Cloudflare Pages | your-project.pages.dev. abc123.your-project.pages.dev 같은 미리보기 URL은 하위 도메인이므로 이미 일치합니다. |
vercel.app,netlify.app,pages.dev만 단독으로 추가하지 마세요. 다른 사람의 사이트를 포함해 그 호스트의 모든 사이트가 일치하게 됩니다.
테스트 체크리스트
- 허용 도메인에
localhost를 넣고nuxt dev를 실행해 제출하세요. 감사 메시지가 폼을 대체해야 합니다. - 브라우저 개발자 도구에서 JavaScript를 끄고 새로 고친 뒤 제출하세요. 로컬에서는 Ikiform의 감사 페이지로,
https:도메인에서는/thanks로 이동해야 합니다. - 필드를 정의한 상태에서 잘못된 이메일을 제출하고 필드 아래에 메시지가 나타나는지 확인하세요.
nuxt generate를 실행하고 결과물을 배포한 뒤 미리보기 호스트에서 제출하세요.
문제 해결
| 증상 | 원인 | 해결 |
|---|---|---|
FetchError: [POST] "https://ikiform.com/f/...": 422 | ignoreResponseError가 없어서 $fetch가 오류에서 예외를 던짐 | ignoreResponseError: true를 추가하세요. |
404 not_found, action이 /f/undefined로 끝남 | 사이트를 빌드할 때 폼 ID가 설정되지 않음 | 빌드 환경에 NUXT_PUBLIC_IKIFORM_FORM_ID를 설정하거나 nuxt.config.ts에 기본값을 넣으세요. |
| JavaScript 없이는 아무 일도 일어나지 않음 | 페이지가 ssr: false로 클라이언트에서 렌더링됨 | 서버 렌더링을 켜거나 페이지를 프리렌더링하세요. |
제출이 origin_mismatch와 함께 스팸 필터에 들어감 | POST한 페이지가 폼의 허용 도메인에 없음 | 위 표의 호스트를 추가하세요. Reject를 골랐다면 같은 POST가 대신 403 origin_not_allowed를 받습니다. |