HTML 폼 제출4분 분량
HTML 폼 action 속성 설명
폼의 action 속성이 하는 일, 상대 값과 빈 값이 해석되는 방식, formaction으로 한 폼을 두 곳에 보내는 방법, 그리고 정적 호스트가 405로 응답하는 이유를 설명합니다.
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 페이지와 그 위의 폼 다섯 개를 예로 들어 봅시다.
<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가 Ikiform에 도달하고, Ikiform이 리디렉션으로 응답합니다.
- 상대, 빈, 또는 없는 action은 페이지 자체 주소를 기준으로, 페이지에 base 요소가 있으면 그것을 기준으로 해석됩니다.
- 그러면 POST가 자체 호스트로 갑니다. 정적 호스트는 파일만 제공하므로 POST를 거부하며, 대개 405입니다.
<base href> 요소는 action을 포함한 모든 상대 URL의 해석 방식을 바꿉니다. 일부 사이트 빌더와 싱글 페이지 앱 구성이 이를 추가합니다. 전체 URL을 쓰면 상관없습니다.브라우저가 쓸 값 확인하기
브라우저 콘솔에서 속성과 프로퍼티는 다릅니다. getAttribute는 작성한 값을 반환하고, action 프로퍼티는 브라우저가 실제로 POST할 해석된 URL을 반환합니다.
const form = document.querySelector("form");
form.getAttribute("action");
form.action;form.action에 내 도메인이 보인다면 데이터가 그곳으로 갑니다. Webflow나 Framer 같은 빌더는 자체 설정 패널에서 action을 설정하는 경우가 많으니, 소스가 아니라 게시된 페이지를 확인하세요. 사이트 빌더 가이드에 각 빌더가 이를 어디에 두는지 나와 있습니다.
폼 하나, 받은편지함 둘
제출 버튼의 formaction은 그 버튼에 대해서만 폼의 action을 대체합니다. 여기서는 필드 한 세트가 두 Ikiform 폼으로 갑니다. Ask support(지원 문의)는 지원 폼으로, Talk to sales(영업 상담)는 영업 폼으로 POST하고, 각 Ikiform 폼이 자기 수신자에게 이메일을 보냅니다.
<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는 조용히 사라지고, 두 번째 폼은 이를 숨겨진 입력으로 유지합니다.
<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합니다.
<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:..."는 제출 대신 코드를 실행하며 대부분의 콘텐츠 보안 정책에 차단됩니다. 둘 다 실제 엔드포인트로 바꿀 가치가 있습니다.
작동 확인하기
- 폼의 Allowed domains에 사이트 도메인을 추가하고, 개발하는 동안에는
localhost도 추가하세요. 비워 두면 어디서든 POST를 받습니다. - 소스가 아니라 게시된 페이지를 열고 콘솔에서
document.querySelector("form").action을 실행하세요. 전체 Ikiform URL이 출력됩니다. - 폼을 제출하세요. Network 패널에 내 도메인으로의 POST가 아니라
303으로 응답한ikiform.com으로의 POST가 표시됩니다. - 받은편지함 두 개짜리 폼에서 버튼마다 한 번씩 제출하세요. 각 제출이 대시보드에서 각자의 폼 아래에 나타납니다.
- 알림 수신자 각각의 받은편지함을 확인하세요. 각자 필드가 담긴 이메일을 받고, 폼에
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 속성을 쓰세요 |
클릭부터 리디렉션까지 전체 제출 흐름은 개요를 참고하세요. 엔드포인트가 받는 모든 것은 HTML 폼에 있습니다.
질문
폼이 있는 페이지로 제출됩니다. 서버가 그 URL에서 POST를 처리한다면 동작하지만, 정적 호스트에서는 대개 405 Method Not Allowed로 실패합니다.
action은 폼에 설정하며 모든 제출에 적용됩니다. formaction은 제출 버튼 하나에 설정하며 그 버튼을 쓸 때 action을 덮어쓰므로, 한 폼이 두 곳으로 보낼 수 있습니다.
네. 브라우저는 특별한 설정 없이 크로스 사이트로 POST하며, 정적 페이지가 폼 백엔드로 POST하는 방식이 바로 이것입니다. 받는 서버가 수락 여부를 정하며, Ikiform은 페이지의 origin을 폼의 허용 도메인과 대조합니다.
method가 get이면 브라우저가 action의 쿼리 문자열을 폼 자체의 필드로 대체합니다. 각 매개변수를 숨겨진 입력으로 옮기세요. method가 post면 쿼리 문자열이 유지됩니다.
막히는 단계가 있거나 내 상황에 맞는 가이드가 필요하다면 이메일을 보내세요: hello@ikiform.com.