# HTML 폼 action 속성 설명

> 폼의 action 속성이 하는 일, 상대 값과 빈 값이 해석되는 방식, formaction으로 한 폼을 두 곳에 보내는 방법, 그리고 정적 호스트가 405로 응답하는 이유를 설명합니다.

2026-10-25 게시 · https://ikiform.com/ko/guides/html-form-submission/action-attribute

`action` 속성은 폼이 데이터를 보내는 URL입니다. 상대 값은 페이지 주소를 기준으로 해석되고, 비어 있거나 없으면 페이지 자체를 뜻하며, 제출 버튼의 `formaction`은 그 버튼에 대해 이를 덮어씁니다. 정적 사이트에서는 자체 호스트가 POST를 받을 수 없으므로 `https://ikiform.com/f/<formId>` 같은 폼 백엔드의 전체 URL로 설정하세요.

정적 사이트에서 망가진 폼은 대부분 이 속성 하나 때문입니다. 마크업은 괜찮아 보이고 버튼도 동작하는데, 데이터가 받을 수 없는 곳으로 갔기 때문에 브라우저가 `405`나 빈 페이지를 보여 줍니다. 이 페이지는 주어진 `action`이 데이터를 정확히 어디로 보내는지 보여 준 뒤, `formaction`으로 한 폼을 두 받은편지함으로 보냅니다.

## 이 경우가 다른 점

`action`은 여느 `href`와 같은 URL이지만 한 가지 다릅니다. `action`이 없는 폼은 아무것도 하지 않는 게 아니라 자기가 있는 페이지로 POST합니다. 서버 렌더링 사이트라면 그 페이지가 POST를 처리할 수도 있습니다. 정적 호스트에서는 그럴 수 없으므로, 빠지거나 상대 경로인 `action`은 배포한 뒤에야 실패합니다.

## 각 값이 데이터를 보내는 곳

`https://example.com/company/contact.html` 페이지와 그 위의 폼 다섯 개를 예로 들어 봅시다.

`contact.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<form action="/api/contact" method="post">
<form action="send" method="post">
<form action="" method="post">
<form method="post">
```

| action | 해석 결과 | 정적 호스트에서 |
| --- | --- | --- |
| `https://ikiform.com/f/frm_...` | 그 URL 그대로 | 동작합니다. Ikiform이 POST를 받습니다 |
| `/api/contact` | `https://example.com/api/contact` | 호스트가 그 경로에서 함수를 실행할 때만 동작합니다 |
| `send` | 폴더 기준 상대 경로인 `https://example.com/company/send` | `405` 또는 `404` |
| `""` (empty) | 페이지 자체인 `contact.html` | 대부분의 정적 호스트에서 `405` |
| 없음 | 페이지 자체 | 대부분의 정적 호스트에서 `405` |

다이어그램: 브라우저가 폼의 action 속성을 POST할 URL로 바꾸는 방식

1. 절대 action은 쓰인 그대로 사용되므로 POST가 Ikiform에 도달하고, Ikiform이 리디렉션으로 응답합니다.
2. 상대, 빈, 또는 없는 action은 페이지 자체 주소를 기준으로, 페이지에 base 요소가 있으면 그것을 기준으로 해석됩니다.
3. 그러면 POST가 자체 호스트로 갑니다. 정적 호스트는 파일만 제공하므로 POST를 거부하며, 대개 405입니다.

제출 직후 주소 표시줄에 내 도메인이 보인다면 action이 상대 경로였던 것입니다.

> 페이지 head의 `<base href>` 요소는 `action`을 포함한 모든 상대 URL의 해석 방식을 바꿉니다. 일부 사이트 빌더와 싱글 페이지 앱 구성이 이를 추가합니다. 전체 URL을 쓰면 상관없습니다.

### 브라우저가 쓸 값 확인하기

브라우저 콘솔에서 속성과 프로퍼티는 다릅니다. `getAttribute`는 작성한 값을 반환하고, `action` 프로퍼티는 브라우저가 실제로 POST할 해석된 URL을 반환합니다.

`콘솔`:

```js
const form = document.querySelector("form");
form.getAttribute("action");
form.action;
```

`form.action`에 내 도메인이 보인다면 데이터가 그곳으로 갑니다. Webflow나 Framer 같은 빌더는 자체 설정 패널에서 `action`을 설정하는 경우가 많으니, 소스가 아니라 게시된 페이지를 확인하세요. [사이트 빌더 가이드](https://ikiform.com/ko/docs/guides/site-builders.md)에 각 빌더가 이를 어디에 두는지 나와 있습니다.

## 폼 하나, 받은편지함 둘

제출 버튼의 `formaction`은 그 버튼에 대해서만 폼의 `action`을 대체합니다. 여기서는 필드 한 세트가 두 Ikiform 폼으로 갑니다. **Ask support**(지원 문의)는 지원 폼으로, **Talk to sales**(영업 상담)는 영업 폼으로 POST하고, 각 Ikiform 폼이 자기 수신자에게 이메일을 보냅니다.

`contact.html`:

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

  <label for="message">How can we help?</label>
  <textarea id="message" name="message" rows="5" required></textarea>

  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Ask support</button>
  <button formaction="https://ikiform.com/f/frm_salesxxxxxxxxxxx">Talk to sales</button>
</form>
```

- 각 Ikiform 폼에는 고유한 알림 수신자, 스팸 설정, 웹훅이 있으므로 지원팀과 영업팀은 서로의 메일을 보지 않습니다.
- 두 폼 모두 같은 필드 정의를 쓰거나, 둘 다 정의가 없어야 합니다. 필드를 정의하면 한 폼이 모르는 필드는 `422`로 거부됩니다.
- 다른 `form*` 속성도 같은 방식으로 동작합니다. `formmethod`, `formenctype`, `formnovalidate`, `formtarget`입니다. `formnovalidate`가 있는 **Save draft** 버튼은 브라우저의 확인을 건너뜁니다.
- `formaction`은 제출 버튼에서만 동작합니다. `<button>`, `<input type="submit">`, `<input type="image">`입니다.

대안은 `topic` 필드가 있는 Ikiform 폼 하나를 두고 웹훅이나 받은편지함 필터에서 주제별로 나누는 것입니다. 그러면 목록 하나와 내보내기 하나를 유지합니다. 두 팀이 수신자를 전혀 공유하면 안 될 때는 폼 두 개가 더 간단합니다.

## 쿼리 문자열이 있는 action

`method="get"`이면 브라우저가 action의 쿼리 문자열을 폼의 필드로 대체합니다. 아래 첫 번째 폼의 `lang=de`는 조용히 사라지고, 두 번째 폼은 이를 숨겨진 입력으로 유지합니다.

`search.html`:

```html
<form action="/search?lang=de" method="get">
  <input name="q" />
</form>

<form action="/search" method="get">
  <input name="q" />
  <input type="hidden" name="lang" value="de" />
</form>
```

`method="post"`면 필드가 본문에 들어가므로 쿼리 문자열이 유지됩니다. 그래도 Ikiform에는 달라지는 것이 없습니다. 엔드포인트 URL의 쿼리 매개변수는 저장되지 않으므로, 유지하고 싶은 값에는 숨겨진 입력을 쓰세요.

## 폼 밖의 입력

레이아웃상 다른 콘텐츠 옆의 푸터 가입처럼 `<form>` 요소 안에 있지 않은 입력이 필요할 때가 있습니다. 입력이나 버튼의 `form` 속성은 `id`로 폼에 합류시키며, 그 폼의 `action`으로 POST합니다.

`footer.html`:

```html
<form id="newsletter" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post"></form>

<footer>
  <input form="newsletter" name="email" type="email" required />
  <button form="newsletter">Subscribe</button>
</footer>
```

## mailto:와 javascript: action

`action="mailto:you@example.com"`은 방문자의 메일 앱에 필드가 담긴 메시지를 열어 달라고 요청합니다. 메일 앱이 설정되어 있고 보내기를 누르지 않으면 아무것도 전송되지 않으며, 시도했는지조차 알 수 없습니다. `action="javascript:..."`는 제출 대신 코드를 실행하며 대부분의 콘텐츠 보안 정책에 차단됩니다. 둘 다 실제 엔드포인트로 바꿀 가치가 있습니다.

## 작동 확인하기

1. 폼의 **Allowed domains**에 사이트 도메인을 추가하고, 개발하는 동안에는 `localhost`도 추가하세요. 비워 두면 어디서든 POST를 받습니다.
2. 소스가 아니라 게시된 페이지를 열고 콘솔에서 `document.querySelector("form").action`을 실행하세요. 전체 Ikiform URL이 출력됩니다.
3. 폼을 제출하세요. Network 패널에 내 도메인으로의 POST가 아니라 `303`으로 응답한 `ikiform.com`으로의 POST가 표시됩니다.
4. 받은편지함 두 개짜리 폼에서 버튼마다 한 번씩 제출하세요. 각 제출이 대시보드에서 각자의 폼 아래에 나타납니다.
5. 알림 수신자 각각의 받은편지함을 확인하세요. 각자 필드가 담긴 이메일을 받고, 폼에 `email` 필드가 있으면 **Reply**는 제출자에게 갑니다.

## 주의할 점

| 문제 | 원인과 해결 |
| --- | --- |
| 내 도메인에서 `405 Method Not Allowed` | action이 상대 경로이거나 비어 있거나 없어서 POST가 정적 호스트로 갔습니다. 전체 Ikiform URL을 쓰세요 |
| ikiform.com에서 `404 not_found` | action의 폼 id가 틀렸거나 폼이 삭제되었습니다. 대시보드의 폼 페이지에서 엔드포인트를 복사하세요 |
| 빌더가 계속 action을 초기화함 | 일부 빌더는 폼 블록의 `action`을 직접 관리합니다. 빌더의 커스텀 액션 설정이나, 직접 작성한 마크업이 담긴 임베드를 쓰세요 |
| 브라우저가 폼이 안전하지 않다고 경고함 | `https:` 페이지에서 action이 `http:`를 씁니다. Ikiform 엔드포인트는 `https:`만 지원하니 스킴을 고치세요 |
| 두 번째 폼의 필드까지 POST됨 | 폼은 중첩할 수 없습니다. 브라우저는 안쪽 `<form>` 태그에서 바깥 폼을 닫습니다. 대신 `form` 속성을 쓰세요 |

클릭부터 리디렉션까지 전체 제출 흐름은 [개요](https://ikiform.com/ko/guides/html-form-submission.md#how-it-works)를 참고하세요. 엔드포인트가 받는 모든 것은 [HTML 폼](https://ikiform.com/ko/docs/html.md)에 있습니다.

## 질문

### 폼에 action 속성이 없으면 어떻게 되나요?

폼이 있는 페이지로 제출됩니다. 서버가 그 URL에서 POST를 처리한다면 동작하지만, 정적 호스트에서는 대개 405 Method Not Allowed로 실패합니다.

### action과 formaction의 차이는 무엇인가요?

action은 폼에 설정하며 모든 제출에 적용됩니다. formaction은 제출 버튼 하나에 설정하며 그 버튼을 쓸 때 action을 덮어쓰므로, 한 폼이 두 곳으로 보낼 수 있습니다.

### 폼 action이 다른 도메인의 URL이어도 되나요?

네. 브라우저는 특별한 설정 없이 크로스 사이트로 POST하며, 정적 페이지가 폼 백엔드로 POST하는 방식이 바로 이것입니다. 받는 서버가 수락 여부를 정하며, Ikiform은 페이지의 origin을 폼의 허용 도메인과 대조합니다.

### action URL의 쿼리 문자열 매개변수가 사라지는 이유는 무엇인가요?

method가 get이면 브라우저가 action의 쿼리 문자열을 폼 자체의 필드로 대체합니다. 각 매개변수를 숨겨진 입력으로 옮기세요. method가 post면 쿼리 문자열이 유지됩니다.

## 가이드 더 보기

- [HTML 폼 제출](https://ikiform.com/ko/guides/html-form-submission.md): 폼이 제출될 때 브라우저가 하는 일: 어떤 필드를 보내는지, 어떻게 인코딩하는지, 서버가 어떻게 응답해야 하는지, 그리고 Ikiform이 어떻게 응답하는지 설명합니다.
- [제출 후 리디렉션](https://ikiform.com/ko/guides/html-form-submission/redirect-after-submit.md): 폼이 POST된 뒤 방문자를 직접 만든 감사 페이지로 보내세요. 303 리디렉션, _redirect 필드, 허용되는 도메인, 새로고침 시 동작을 다룹니다.
- [페이지 새로고침 없이](https://ikiform.com/ko/guides/html-form-submission/without-page-reload.md): fetch와 FormData로 폼을 보내 방문자를 페이지에 머물게 하세요. 필드 오류, 로딩 상태, 재시도, 스크립트 실패 시 대체 경로까지 다룹니다.
- [폼 검증](https://ikiform.com/ko/guides/html-form-submission/validation.md): required, type, pattern, min으로 폼을 검증하고, :user-invalid로 오류에 스타일을 입히고, 필드 정의로 서버에서 같은 규칙을 강제하세요.
