연락처 폼을 만들고 사용자가 "제출" 버튼을 클릭한 후 전체 페이지가 새로고침되는 것을 본 적이 있다면, 그 경험이 얼마나 어색한지 이미 알고 계실 것입니다. HTML form submit fetch javascript 기법을 사용하면 폼 데이터를 백그라운드에서 백엔드 엔드포인트로 전송하고, 사용자를 같은 페이지에 유지하며, 전체 페이지 새로고침 없이 깔끔한 성공 또는 오류 메시지를 표시할 수 있어요. 이 튜토리얼에서는 기본 HTML 작성부터 fetch() 호출을 Sendform 엔드포인트에 연결하여 제출 내용이 받은편지함이나 연결된 워크플로우로 직접 전달되도록 하는 모든 단계를 안내해 드려요.
핵심 요점:
- Fetch API를 사용하면 페이지 새로고침 없이 폼 데이터를 제출할 수 있어 사용자에게 더 부드러운 경험을 제공해요.
submit이벤트를 캡처하고preventDefault()를 호출하는 것이 모든 ajax 폼 제출 패턴의 기초예요.- Sendform은 즉시 사용 가능한 엔드포인트 URL을 제공하므로 제출 내용을 받고 저장하기 위한 백엔드 코드가 전혀 필요하지 않아요.
- 적절한 응답 처리(성공 또는 오류 피드백 표시)는 데이터를 올바르게 전송하는 것만큼 중요해요.
목차
기본 폼 제출 대신 fetch()를 사용하는 이유
브라우저의 기본 폼 제출 동작은 정확히 한 가지 일을 해요: 폼 필드를 직렬화하고, action URL로 POST(또는 GET) 요청을 보낸 다음, 서버가 반환하는 응답을 로드해요. 이는 사용자가 흰색 깜빡임을 보고, 스크롤 위치를 잃고, 새 페이지가 그려질 때까지 기다려야 한다는 의미예요. 느린 연결에서는 이것이 고장난 것처럼 느껴질 수 있어요.
Fetch API는 JavaScript에서 프로그래밍 방식으로 HTTP 요청을 만들어 이 문제를 해결하며, 페이지를 벗어나지 않고도 작업할 수 있어요. 이를 통해 페이지 새로고침 없는 폼 제출 패턴을 구현할 수 있어 사용자의 참여를 유지하고 로딩 상태, 인라인 유효성 검사 피드백, 애니메이션 성공 배너를 포함한 사용자 경험의 모든 측면을 제어할 수 있어요.
fetch()를 선호하는 추가적인 실용적 이유들:
- 일반 HTML 폼이 보낼 수 없는 사용자 정의 헤더(예: CSRF 토큰이나 인증 키)를 첨부할 수 있어요.
- 엔드포인트가 기대하는 형식에 따라 데이터를 JSON,
FormData, 또는 URL 인코딩 문자열로 직렬화할 수 있어요. - 오류 처리가 명시적이에요. "실패"가 무엇을 의미하는지, 어떻게 전달할지를 직접 결정할 수 있어요.
- 로직이 전적으로 브라우저에 있기 때문에 GitHub Pages, Netlify, 또는 CDN에 호스팅된 정적 사이트를 포함한 모든 정적 사이트에서 작동해요.
참고: Webflow, WordPress, Hugo 같은 웹사이트 빌더를 사용하고 있다면 동일한 fetch() 접근법이 적용돼요. 플랫폼별 팁은 웹사이트 빌더와 Sendform 통합 가이드를 참고하세요.
기본 HTML 폼 설정
JavaScript 코드를 한 줄도 작성하기 전에, 잘 구조화된 HTML 폼이 필요해요. 여기서 중요한 점은 JavaScript가 제출을 처리할 것이므로 어디든 가리키는 action 속성이 필요하지 않다는 것이에요. 하지만 모든 입력에는 의미 있는 name 속성이 필요해요 - 이것들이 제출된 페이로드에서 필드 키가 되거든요.
<form id="contact-form" novalidate>
<div>
<label for="name">이름</label>
<input type="text" id="name" name="name" required placeholder="홍길동">
</div>
<div>
<label for="email">이메일 주소</label>
<input type="email" id="email" name="email" required placeholder="[email protected]">
</div>
<div>
<label for="message">메시지</label>
<textarea id="message" name="message" rows="5" required></textarea>
</div>
<button type="submit">메시지 보내기</button>
<!-- 피드백 영역 -->
<div id="form-feedback" aria-live="polite"></div>
</form>이 마크업에서 주목할 몇 가지 사항:
- 폼 요소의
novalidate는 네이티브 브라우저 유효성 검사 버블을 비활성화하여 JavaScript에서 오류 메시징을 완전히 제어할 수 있게 해줘요. aria-live="polite"가 있는id="form-feedback"div는 성공 및 오류 메시지가 나타날 곳이에요. ARIA 속성은 스크린 리더가 피드백을 자동으로 알려주도록 해줘요.- 모든 입력에는
id(레이블 연결용)와name(폼 페이로드용)이 모두 있어요.
제출 이벤트 캡처하기
모든 javascript 폼 제출의 첫 번째 단계는 브라우저의 기본 동작을 가로채는 것이에요. 폼 요소에서 submit 이벤트를 수신하고 즉시 event.preventDefault()를 호출하면 돼요.
const form = document.getElementById('contact-form');
form.addEventListener('submit', async function (event) {
event.preventDefault(); // 기본 페이지 이동을 중지
// 기본 클라이언트 측 유효성 검사
const name = form.elements['name'].value.trim();
const email = form.elements['email'].value.trim();
const message = form.elements['message'].value.trim();
if (!name || !email || !message) {
showFeedback('모든 필드를 입력해 주세요.', 'error');
return;
}
// 데이터 전송 진행 (다음 섹션)
await submitForm({ name, email, message });
});유효성 검사 로직을 네트워크 호출과 분리하면 코드를 읽기 쉽게 유지하고 확장하기 쉬워져요. 이벤트 핸들러의 async 키워드는 내부에서 await를 사용할 수 있게 해주어 Fetch API 폼 데이터 호출이 동기적으로 보이게 하고 깊게 중첩된 프로미스 체인을 피할 수 있어요.
fetch()를 Sendform 엔드포인트에 연결하기
여기서 실제 작업이 이루어져요. 폼 제출을 받고, 저장하고, 전달하는 자체 서버를 구축하는 대신 Sendform을 백엔드로 사용할 수 있어요. Sendform 대시보드에서 폼을 생성하면 고유한 엔드포인트 URL을 받게 돼요. 그 URL이 필요한 전부예요.
아래 제출 함수는 FormData API를 사용하여 페이로드를 구성하며, Sendform이 네이티브로 받아들여요:
async function submitForm(data) {
// 이 URL을 실제 Sendform 엔드포인트로 교체하세요
const SENDFORM_ENDPOINT = 'https://sendform.net/ko/YOUR_FORM_ID';
const formData = new FormData();
formData.append('name', data.name);
formData.append('email', data.email);
formData.append('message', data.message);
try {
const response = await fetch(SENDFORM_ENDPOINT, {
method: 'POST',
body: formData,
});
if (response.ok) {
showFeedback('감사합니다! 메시지가 전송되었어요.', 'success');
form.reset();
} else {
const errorData = await response.json().catch(() => ({}));
const errorMsg = errorData.message || '문제가 발생했어요. 다시 시도해 주세요.';
showFeedback(errorMsg, 'error');
}
} catch (networkError) {
showFeedback('네트워크 오류예요. 연결을 확인하고 다시 시도해 주세요.', 'error');
}
}이 코드에서 내린 중요한 결정들:
Content-Type헤더를 수동으로 설정하지 않았어요.FormData객체를 body로 전달하면 브라우저가 올바른multipart/form-data경계를 자동으로 설정해요.response.ok확인은 200뿐만 아니라 모든 2xx HTTP 상태 코드를 커버해요. 이는response.status === 200과 비교하는 것보다 더 견고해요.- 외부
try/catch는response.ok가 절대 볼 수 없는 네트워크 수준 실패(DNS 오류, 오프라인 상태)를 포착해요.
응답 처리하기 - 성공 및 오류 메시지
데이터를 보내는 것은 일의 절반에 불과해요. 사용자에게는 즉각적이고 명확한 피드백이 필요해요. 위에서 참조한 showFeedback() 도우미 함수는 HTML에 추가한 피드백 div에 메시지를 작성해요:
function showFeedback(message, type) {
const feedbackEl = document.getElementById('form-feedback');
feedbackEl.textContent = message;
feedbackEl.className = type === 'success' ? 'feedback-success' : 'feedback-error';
}이는 의도적으로 최소한으로 만들었어요. 실제 프로젝트에서는 textContent를 애니메이션 컴포넌트나 토스트 알림 라이브러리로 교체할 수 있지만, 패턴은 동일해요: fetch() 호출의 결과에 따라 DOM을 업데이트하는 것이죠.
제출 후 사용자 정의 감사 페이지로 리디렉션하거나 다운스트림 자동화를 트리거하는 것과 같은 고급 시나리오의 경우, 웹훅, Zapier, API로 폼 워크플로우를 자동화하는 방법에 대한 글을 확인해 보세요.
모범 사례 및 팁
코드가 작동하게 하는 것은 한 가지예요. 프로덕션에서 견딜 수 있는 방식으로 배포하는 것은 또 다른 문제죠. 염두에 두어야 할 가장 중요한 팁들을 소개해 드려요:
- 요청 중에 제출 버튼을 비활성화하세요.
fetch()를 호출하기 전에button.disabled = true로 설정하고finally블록에서 다시 활성화하세요. 이렇게 하면 사용자가 여러 번 클릭할 경우 중복 제출을 방지할 수 있어요. - 로딩 상태를 표시하세요. 요청이 진행 중일 때 버튼 텍스트를 "전송 중..."으로 변경하거나 스피너 클래스를 추가하세요. 피드백을 보지 못한 사용자는 종종 아무 일도 일어나지 않았다고 생각하고 다시 클릭해요.
- 서버 측에서도 유효성 검사를 하세요. 클라이언트 측 유효성 검사는 사용자 경험을 위한 것이에요. Sendform과 모든 백엔드 서비스는 들어오는 모든 데이터를 신뢰할 수 없는 것으로 취급해야 해요.
- 모든 곳에서 HTTPS를 사용하세요. 일반 HTTP를 통해 폼 데이터를 전송하면 사용자 입력이 전송 중에 노출돼요. Sendform 엔드포인트는 기본적으로 HTTPS이지만, 자신의 페이지도 HTTPS를 통해 제공되는지 확인하세요.
- 스팸 보호를 추가하세요. 허니팟 필드나 CAPTCHA 통합은 정크 제출을 크게 줄여줘요. 이 주제에 대한 자세한 내용은 폼 스팸 보호 모범 사례 가이드를 참고하세요.
- 의도적으로 오류 경로를 테스트하세요. 엔드포인트 URL을 일시적으로 유효하지 않은 것으로 변경하고 오류 메시지가 나타나는지 확인하세요. 대부분의 개발자는 성공 경로만 테스트해요.
- 엔드포인트 URL을 버전 관리에서 제외하세요. 프로젝트가 오픈 소스라면 Sendform 엔드포인트를 환경 변수나
.gitignore에 나열된 설정 파일에 저장하세요.
정적 사이트 사용자: 프로젝트가 Hugo, Eleventy, 또는 서버가 없는 일반 HTML 사이트라면, 여기서 설명한 fetch() 접근법이 권장 방법이에요. 정적 사이트를 위한 서버리스 폼 처리 가이드에서 더 자세히 읽어보세요.
결론
기본 폼 제출을 fetch() 호출로 교체하는 것은 모든 연락처나 리드 캡처 폼에 할 수 있는 가장 큰 영향을 주는 개선 중 하나예요. 그 결과는 사용자를 페이지에 유지하고 피드백 메시징을 완전히 제어할 수 있는 더 빠르고 전문적인 경험이에요. Sendform 엔드포인트와 결합하면 서버 측 코드가 전혀 필요하지 않아요. 폼이 라이브되고, 제출 내용이 받은편지함에 도달하며, 사용자는 어색한 페이지 새로고침을 보지 않아요. 오늘 무료 Sendform 엔드포인트를 만들어 몇 분 안에 첫 번째 제출을 받아보세요.
자주 묻는 질문
네. fetch()는 완전히 브라우저에서 실행되기 때문에 정적 HTML 사이트, JAMstack 프로젝트, HTML을 제공하는 모든 플랫폼에서 작동해요. 자체 서버가 필요하지 않아요. 유일한 요구사항은 POST 요청을 받을 수 있는 엔드포인트(Sendform URL 같은)예요.
fetch()는 XMLHttpRequest의 현대적인 대체재예요. Promise를 사용하고, async/await을 지원하며, 더 깔끔한 API를 가지고 있어요. 새 프로젝트에서는 fetch()가 항상 선호돼요. 둘 다 같은 ajax 폼 제출 결과를 달성하지만, fetch()는 훨씬 적은 보일러플레이트 코드를 필요로 해요.
아니요. FormData 객체를 body로 전달하면 브라우저가 자동으로 Content-Type을 multipart/form-data로 설정하고 올바른 경계 문자열을 포함해요. 수동으로 설정하면 실제로 그 경계 값을 누락시켜 요청을 망가뜨릴 수 있어요.
Sendform에 가입하고, 대시보드에서 새 폼을 만든 다음, 생성된 엔드포인트 URL을 복사하세요. 그 URL을 fetch() 호출의 대상으로 붙여넣으세요. 제출 내용이 설정된 이메일 주소로 즉시 전달돼요.
네트워크 실패는 fetch() Promise를 거부하게 하며, 이는 예제 코드의 외부 try/catch 블록에서 포착돼요. 사용자는 정의한 "네트워크 오류" 메시지를 보게 돼요. 제출은 자동으로 대기열에 들어가지 않아요; 연결이 복원된 후 사용자가 다시 시도해야 해요.