HTML 폼 제출

HTML 폼 제출4분 분량

HTML 폼 action 속성 설명

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

Preet SutharIkiform 창업자

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
<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/contacthttps://example.com/api/contact호스트가 그 경로에서 함수를 실행할 때만 동작합니다
send폴더 기준 상대 경로인 https://example.com/company/send405 또는 404
"" (empty)페이지 자체인 contact.html대부분의 정적 호스트에서 405
없음페이지 자체대부분의 정적 호스트에서 405
브라우저가 폼의 action 속성을 POST할 URL로 바꾸는 방식절대 경로상대, 빈 값 또는 없음action="https://ikiform.com/f/..."쓰인 그대로 사용Ikiform이 303으로 응답action="send" 또는없음페이지 기준으로 해석또는 페이지에 있다면 <base href>기준자체 정적 호스트POST 불가: 405브라우저가 폼의 action 속성을 POST할 URL로 바꾸는 방식절대 경로상대, 빈 값 또는 없음action="https://ikiform.com/f/..."쓰인 그대로 사용Ikiform이 303으로 응답action="send" 또는 없음페이지 기준으로 해석또는 페이지에 있다면 <base href> 기준자체 정적 호스트POST 불가: 405
  1. 절대 action은 쓰인 그대로 사용되므로 POST가 Ikiform에 도달하고, Ikiform이 리디렉션으로 응답합니다.
  2. 상대, 빈, 또는 없는 action은 페이지 자체 주소를 기준으로, 페이지에 base 요소가 있으면 그것을 기준으로 해석됩니다.
  3. 그러면 POST가 자체 호스트로 갑니다. 정적 호스트는 파일만 제공하므로 POST를 거부하며, 대개 405입니다.
제출 직후 주소 표시줄에 내 도메인이 보인다면 action이 상대 경로였던 것입니다.
페이지 head의 <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 폼이 자기 수신자에게 이메일을 보냅니다.

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

클릭부터 리디렉션까지 전체 제출 흐름은 개요를 참고하세요. 엔드포인트가 받는 모든 것은 HTML 폼에 있습니다.

질문

막히는 단계가 있거나 내 상황에 맞는 가이드가 필요하다면 이메일을 보내세요: hello@ikiform.com.