# 문제 해결

> CORS 오류, 출처 불일치, challenge_required, 429, 알 수 없는 필드, 할당량 오류 해결 방법.

응답부터 확인하세요. Ikiform의 모든 응답에는 `X-Request-Id` 헤더가 있고, 오류에는 바뀌지 않는 `error.code`가 있습니다. 다음 curl은 여러분의 사이트가 보낼 것과 같은 출처에서 둘 다 보여 줍니다.

`Terminal`:

```bash
curl -i https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Origin: https://example.com" \
  -d '{"email":"ada@example.com"}'
```

Ikiform 자체에서 `5xx` 오류나 시간 초과가 발생하나요? 먼저 [status.ikiform.com](https://status.ikiform.com)에서 현재 장애를 확인하세요.

## 브라우저에서 CORS가 요청을 막았다고 나올 때

제출 엔드포인트는 모든 출처를 허용하므로, 실제 CORS 오류는 대개 요청이 Ikiform에 닿지 않았거나 잘못된 URL로 갔다는 뜻입니다.

- URL이 `frm_xxxxxxxxxxxxxxxx`처럼 전체 `frm_` ID를 포함한 `https://ikiform.com/f/<formId>`이고 뒤에 경로가 붙지 않았는지 확인하세요.
- 브라우저에서 `/api/v1`을 호출하고 있습니다. REST API는 설계상 브라우저 요청을 거부하므로 서버에서 호출하세요.
- 광고 차단기, 개인정보 보호 확장 프로그램, 회사 프록시가 요청을 막았습니다. 확장 프로그램 없이 시크릿 창에서 시도해 보세요.
- 사이트 자체의 `Content-Security-Policy`가 막고 있습니다. `connect-src`에 `https://ikiform.com`을 추가하고, 일반 HTML POST라면 `form-action`에도 추가하세요.

프리플라이트를 직접 확인하세요. 여러분의 출처를 그대로 돌려주며 `204`로 응답해야 합니다.

`Terminal`:

```bash
curl -i -X OPTIONS https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx \
  -H "Origin: https://example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: content-type"
```

## 제출이 origin_mismatch로 스팸에 들어갈 때

폼에 허용 도메인이 있는데 POST가 다른 곳에서 왔습니다. 대시보드의 스팸 사유에 `origin_mismatch`가 표시됩니다.

- `www`, 스테이징, 미리보기 도메인을 포함해 폼이 제공되는 모든 도메인을 추가하세요. 등록한 도메인의 하위 도메인은 이미 일치합니다.
- 로컬에서 개발하는 동안에는 `localhost`를 추가하세요.
- 여러분의 서버에서 보낸 POST에는 `Origin`이 없습니다. 이 검사를 건너뛰는 시크릿 키를 보내세요.
- **Mark as spam** 대신 **Reject**를 골랐다면 같은 POST가 `403 origin_not_allowed`를 받습니다.

## 403 challenge_required

폼의 스팸 검사가 **Ikiform**으로 설정되어 있는데 POST에 Turnstile 토큰이 없었습니다.

1. 브라우저 코드: 제출 직전마다 SDK의 `challenge(formId)`를 호출하고, 토큰을 `submit`에 `turnstileToken`으로 전달하세요.
2. 서버 코드: 시크릿 키로 `Authorization: Bearer ik_sk_...`를 보내세요. 시크릿 키는 확인을 건너뜁니다.
3. 일반 HTML POST는 이 코드를 받지 않고 대신 확인 페이지를 받습니다. 폼 POST에서 이 코드가 나온다면 무언가가 `Accept: application/json`을 보내고 있는 것입니다.
4. curl로 테스트하고 있나요? 테스트용 폼에서는 확인을 끄거나 시크릿 키를 사용하세요.

`403 captcha_failed`는 토큰을 보냈지만 검증되지 않았다는 뜻입니다. 토큰은 한 번만 쓸 수 있고 300초 후 만료되므로 시도할 때마다 새로 받으세요.

## 429 rate_limited

- `Retry-After`를 읽고 그 초만큼 기다리세요.
- 직접 손으로 테스트하고 있나요? Standard 프리셋은 IP 하나에서 분당 5건, 10초에 3건을 허용합니다. 테스트용 폼은 Relaxed로 바꾸세요.
- 방문자마다 서버에서 POST하고 있나요? 모든 POST가 서버의 IP를 공유합니다. 브라우저에서 POST하거나 Relaxed 또는 Custom을 고르세요.
- 같은 사무실이나 이벤트 네트워크를 쓰는 많은 사람도 IP를 공유합니다. Relaxed는 분당 20건을 허용합니다.

## Unknown field와 함께 422가 나올 때

폼에 필드가 정의되어 있는데 POST에 정의에 없는 이름이 있습니다. `error.fields`에 그 이름이 표시됩니다. 예: `{ "website": ["Unknown field \"website\""] }`.

- 필드를 정의에 추가하거나 입력을 제거하세요.
- 여러분만 쓰는 표시용 입력이라면 이름이 `_`로 시작하도록 바꾸세요. 예약된 이름은 검증하지 않고 제거됩니다.
- 허니팟 이름이 폼의 **Honeypot field** 설정과 일치하는지 확인하세요.
- 필드 ID는 소문자 snake_case입니다. 마크업의 `firstName`은 정의의 `first_name`과 같지 않습니다.

## 429 quota_exceeded

워크스페이스가 이번 달에 월간 한도의 2배를 받아, 다음 달이 시작되거나 워크스페이스가 업그레이드할 때까지 제출을 받지 않습니다. [월간 사용량](https://ikiform.com/ko/docs/limits.md#monthly-usage)을 참고하세요.

## 제출이 보이지 않을 때

- 스팸 필터를 확인하세요. 스팸도 정상적인 성공 응답을 받으므로 방문자는 알 수 없습니다.
- 폼이 일시 중지되지 않았는지, `action`의 ID가 대시보드와 일치하는지 확인하세요.
- 브라우저 POST에서 Ikiform의 오류 페이지가 나왔다면 거부된 것입니다. 페이지에 이유가 표시됩니다.

## 웹훅이 실패할 때

- `no_matching_signature`: JSON을 파싱하기 전의 원본 본문으로 검증하고, 올바른 `whsec_` 시크릿을 복사했는지 확인하세요. 전송을 [검증기](https://ikiform.com/ko/docs/webhooks.md#verifier)에 붙여 넣어 보세요.
- `timestamp_too_old`: 서버 시계가 맞지 않거나, 예전 전송을 재생하고 있습니다.
- 시간 초과: 15초 안에 응답하고 오래 걸리는 작업은 그 뒤에 처리하세요.
- 리디렉션은 따르지 않습니다. 프레임워크가 기대하는 끝의 슬래시까지 포함한 최종 URL을 사용하세요.
- 5일 동안 실패했거나 `410`을 받아 엔드포인트가 비활성화되었습니다. 문제를 고친 뒤 엔드포인트 페이지에서 다시 켜세요.
