Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript로 다단계(Multi-Step) 폼 만드는 방법 완벽 가이드

사용자가 긴 양식을 한 번에 작성하도록 하면 이탈률이 높아지기 쉽습니다. 이럴 때 다단계(Multi-Step) 폼을 활용하면 입력 항목을 여러 단계로 나누어 사용자 경험을 크게 개선할 수 있습니다. 이번 글에서는 순수 JavaScript만으로 단계별 등록 폼을 만드는 방법을 예제 코드와 함께 자세히 살펴보겠습니다.

다단계 폼의 동작 원리

이 예제는 크게 세 부분으로 구성됩니다.

  • 단계별 페이지 분리 — 각 단계는 page 클래스를 가진 <div>로 구성되며, 기본적으로 display: none
  • 단계 전환 로직 — [이전]/[다음] 버튼 클릭 시 현재 단계를 숨기고 다음(또는 이전) 단계를 표시합니다.
  • 유효성 검사 및 진행 표시기 — 각 단계의 입력값이 비어 있는지 검사하고, 하단의 원형 인디케이터로 진행 상황을 시각적으로 보여줍니다.

전체 예제 코드

<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
    * {
        box-sizing: border-box;
    }
    body {
        background-color: #f1f1f1;
    }
    #regForm {
        margin: 100px auto;
        font-family: Raleway;
        padding: 40px;
        width: 70%;
        min-width: 300px;
    }
    h1 {
        text-align: center;
    }
    input {
        padding: 10px;
        width: 100%;
        font-size: 17px;
        font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        border: 1px solid #aaaaaa;
    }
    /* 유효성 검사에서 오류가 발생한 입력창 표시 */
    input.invalid {
        background-color: #ffdddd;
    }
    /* 기본적으로 모든 단계 숨기기 */
    .page {
        display: none;
    }
    button {
        background-color: #4CAF50;
        color: #ffffff;
        border: none;
        padding: 10px 20px;
        font-size: 17px;
        font-family: Raleway;
        cursor: pointer;
    }
    button:hover {
        opacity: 0.8;
    }
    #prevBtn {
        background-color: #bbbbbb;
    }
    .step {
        height: 15px;
        width: 15px;
        margin: 0 2px;
        background-color: #bbbbbb;
        border: none;
        border-radius: 50%;
        display: inline-block;
        opacity: 0.5;
    }
    .step.active {
        opacity: 1;
    }
    .step.finish {
        background-color: #4CAF50;
    }
</style>
<body>
<form id="regForm">
<h1>회원가입:</h1>
<div class="page">이름:
<p><input placeholder="이름:" oninput="this.className = ''" name="fname"></p>
<p><input placeholder="성:" oninput="this.className = ''" name="lname"></p>
</div>
<div class="page">연락처 정보:
<p><input placeholder="이메일:" oninput="this.className = ''" name="email"></p>
<p><input placeholder="전화번호:" oninput="this.className = ''" name="phone"></p>
</div>
<div class="page">생년월일:
<p><input placeholder="일:" oninput="this.className = ''" name="dd"></p>
<p><input placeholder="월:" oninput="this.className = ''" name="nn"></p>
<p><input placeholder="연도:" oninput="this.className = ''" name="yyyy"></p>
</div>
<div style="overflow:auto;">
<div style="float:right;">
<button type="button" id="prevBtn" onclick="nextPrev(-1)">이전</button>
<button type="button" id="nextBtn" onclick="nextPrev(1)">다음</button>
</div>
</div>
<div style="text-align:center;margin-top:40px;">
<span class="step"></span>
<span class="step"></span>
<span class="step"></span>
</div>
</form>
<script>
    var currentTab = 0;
    showTab(currentTab);
    function showTab(stepNo) {
        var pageEle = document.getElementsByClassName("page");
        pageEle[stepNo].style.display = "block";
        if (stepNo == 0) {
            document.getElementById("prevBtn").style.display = "none";
        }
        else {
            document.getElementById("prevBtn").style.display = "inline";
        }
        if (stepNo == (pageEle.length - 1)) {
            document.getElementById("nextBtn").innerHTML = "제출";
        }
        else {
            document.getElementById("nextBtn").innerHTML = "다음";
        }
        changeStepIndicator(stepNo)
    }
    function nextPrev(stepNo) {
        var pageEle = document.getElementsByClassName("page");
        if (stepNo == 1 && !checkForm()) return false;
        pageEle[currentTab].style.display = "none";
        currentTab = currentTab + stepNo;
        if (currentTab >= pageEle.length) {
            document.getElementById("regForm").submit();
            return false;
        }
        showTab(currentTab);
    }
    function checkForm() {
        var pageEle, inputEle, i, valid = true;
        pageEle = document.getElementsByClassName("page");
        inputEle = pageEle[currentTab].getElementsByTagName("input");
        for (i = 0; i < inputEle.length; i++) {
            if (inputEle[i].value == "") {
                inputEle[i].className += " invalid";
                valid = false;
            }
        }
        if (valid) {
            document.getElementsByClassName("step")[currentTab].className += " finish";
        }
        return valid;
    }
    function changeStepIndicator(stepNo) {
        var i, pageEle = document.getElementsByClassName("step");
        for (i = 0; i < pageEle.length; i++) {
            pageEle[i].className = pageEle[i].className.replace(" active", "");
        }
        pageEle[stepNo].className += " active";
    }
</script>
</body>
</html>

핵심 함수 설명

1. showTab() — 단계 표시 함수

현재 단계 번호(currentTab)에 해당하는 페이지만 화면에 보여줍니다. 첫 번째 단계에서는 [이전] 버튼을 숨기고, 마지막 단계에서는 [다음] 버튼의 텍스트를 [제출]로 변경합니다.

2. nextPrev() — 단계 전환 함수

[다음] 버튼 클릭 시 먼저 checkForm()으로 유효성 검사를 수행하고, 통과하지 못하면 단계가 넘어가지 않습니다. 마지막 단계에서 [제출]을 누르면 form.submit()으로 폼 데이터를 서버에 전송합니다.

3. checkForm() — 유효성 검사 함수

현재 단계의 모든 input 요소를 순회하며 빈 값이 있으면 해당 입력창에 invalid 클래스를 추가해 붉은 배경으로 표시합니다. 모든 값이 유효하면 해당 단계의 인디케이터에 finish 클래스를 추가해 초록색으로 완료 표시를 합니다.

4. changeStepIndicator() — 진행 표시기 업데이트

하단의 원형 인디케이터 중 현재 활성화된 단계에 active 클래스를 적용하여 사용자가 몇 번째 단계에 있는지 직관적으로 알 수 있게 합니다.

실행 결과

위 코드를 실행하면 아래와 같이 이름 → 연락처 정보 → 생년월일 순으로 진행되는 3단계 등록 폼이 나타납니다.

JavaScript로 다단계(Multi-Step) 폼 만드는 방법 완벽 가이드

마무리

이 예제는 외부 라이브러리 없이 HTML, CSS, JavaScript만으로 다단계 폼을 구현하는 기본적인 패턴입니다. 실무에서는 정규식 기반의 이메일·전화번호 유효성 검사, 진행률 바 애니메이션, 키보드 Enter 키 지원 등을 추가하면 더욱 완성도 높은 폼을 만들 수 있습니다.