프로그램 언어/JavaScript Dom Part

[J.S_DOM] 8-1. 폼 요소 를 활용한 미니 프로젝트(회원 가입창)

알케이88 2025. 8. 10. 01:13

 

지금까지 폼 요소를 활용하는 방법을 알아봤고 더 나아가 준비한 J.S_DOM 의 과정이 끝이났다. 

다음 파트부터는 프로젝트를 진행 할 예정이지만 해당 폼요소를 활용한 예제를 고민 고민 하다가 해당 요소들을 활용하는 회원가입창을 만들어보려고 한다. 

 

8-1.1 회원 가입폼에는 어떠한 입력 항목들이 필요할까?

입력 항목 설명 및 비고 사용 예정 폼 요소 
이름* 사용자의 이름 ( 필수 입력 ) <input type="text">
아이디* 로그인할 때 쓸 고유 아이디 ( 중복 확인 가능 ) <input type="text">
닉네임 사이트 내 표시용 (미지정시 자동생성 형용사+명사) <input type="text">
이메일* 연락 가능한 이메일 주소 ( 이메일 형식 검사 필요 ) <input type="email">
비밀번호* 로그인용 비밀번호 ( 보안 위해 최소 길이 등 조건 필요 ) <input type="password">
비밀번호 확인* 비밀번호 재입력 ( 정확히 일치하는지 확인 ) <input type="password">
성별* 남성/여성/기타 선택 ( 단일 선택 ) <input type="radio">
생년월일 사용자의 출생일 ( 선택 사항 ) <input type="date">
직업 직업선택(사전 정의된 목록제공) <select>
연락처 휴대폰 또는 전화번호 ( 선택 사항 ) <input type="tel">
프로필 사진 업로드 사진 파일 업로드 ( 이미지 파일만 & 선택사항 )  <input type="file" accept="image/*">
주소 시/도, 구/군, 상세 주소 (카카오 API사용) <input type="text"> (여러 개)
약관 동의 이용 약관에 동의 여부 (필수 체크) <input type="checkbox">
비밀 번호 찾기 질문 비밀번호 분실 시 본인 확인용 질문과 답변 <select> + <input type="text">

 

자 그럼 이제 생성해보자

CSS는 무시하고 일단 HTML 을 사용하여 프론트엔드만 개발해보도록 하자

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8" />
  <title>회원 가입창</title>
</head>
<body>
  <!-- 폼 전체: novalidate는 브라우저 기본 검증을 끄는 옵션입니다.
       지금은 기본 검증을 사용하려 하므로 제거해도 됩니다. 필요 시 JS로 reportValidity를 호출하세요. -->
  <form id="signup_form">
    <!-- 필수 입력 그룹 -->
    <fieldset>
      <legend>필수 입력</legend>

      <!-- 이름: 한글/영문만 허용, 자동완성 힌트는 name -->
      <label for="name">이름</label>
      <input
        type="text"
        id="name"
        name="name"
        required
        pattern="^[a-zA-Z가-힣]+$"
        placeholder="이름을 입력하세요"
        autocomplete="name"
      >
      <br><br>

      <!-- 아이디: 영문/숫자/언더스코어 3자 이상, 자동완성 힌트는 username -->
      <label for="uid">아이디</label>
      <input
        type="text"
        id="uid"
        name="username"
        required
        pattern="^[a-zA-Z0-9_]{3,}$"
        placeholder="아이디를 입력하세요 (최소 3자)"
        autocomplete="username"
      >
      <br><br>

      <!-- 닉네임: 비워두면 JS로 자동 생성 가능(추후 로직 연결) -->
      <label for="nickname">닉네임</label>
      <input
        type="text"
        id="nickname"
        name="nickname"
        placeholder="입력이 없으면 자동생성"
        autocomplete="nickname"
      >
      <br><br>

      <!-- 이메일: 형식 검증 내장, 자동완성 힌트는 email -->
      <label for="email">이메일</label>
      <input
        type="email"
        id="email"
        name="email"
        required
        placeholder="이메일을 입력해 주세요"
        autocomplete="email"
      >
      <br><br>

      <!-- 비밀번호: 강력한 규칙 + 새 비밀번호 힌트 -->
      <label for="password1">비밀번호</label>
      <input
        type="password"
        id="password1"
        name="password"
        required
        autocomplete="new-password"
        placeholder="최소 17자, 대/소문자·숫자·한글·특수기호 포함"
        pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*[가-힣])(?=.*[ㄱ-ㅎ])(?=.*[ㅏ-ㅣ])(?=.*[0-9])[a-zA-Z가-힣ㄱ-ㅎㅏ-ㅣ0-9\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]{17,}$"
      >
      <br><br>

      <!-- 비밀번호 확인: 동일 규칙, new-password로 힌트 통일 -->
      <label for="password2">비밀번호 확인</label>
      <input
        type="password"
        id="password2"
        name="password_confirm"
        required
        autocomplete="new-password"
        placeholder="한 번 더 입력해 주세요"
        pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*[가-힣])(?=.*[ㄱ-ㅎ])(?=.*[ㅏ-ㅣ])(?=.*[0-9])[a-zA-Z가-힣ㄱ-ㅎㅏ-ㅣ0-9\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]{17,}$"
      >
      <!-- 클릭 시 JS로 두 값 일치 여부 안내/검증(추후 연결) -->
      <button type="button" id="pwcheck">비밀번호 확인</button>
    </fieldset>

    <br>

    <!-- 비밀번호 찾기 질문 그룹 -->
    <fieldset>
      <legend>비밀번호 찾기 질문</legend>

      <!-- 드롭다운: 표준화된 질문 선택, 'custom'은 기타 직접 입력 -->
      <label for="secq">질문 선택</label>
      <select id="secq" name="secq" required>
        <option value="" selected disabled>선택하세요</option>
        <option value="first_school">처음 다닌 학교 이름은?</option>
        <option value="first_pet">첫 반려동물 이름은?</option>
        <option value="birth_city">태어난 도시는?</option>
        <option value="favorite_teacher">가장 좋아하던 선생님 성함은?</option>
        <option value="custom">기타(직접 입력)</option>
      </select>

      <!-- '기타'를 선택했을 때만 활성화(추후 JS로 enable/disable) -->
      <input
        type="text"
        id="secq_custom"
        name="secq_custom"
        placeholder="질문을 직접 입력해 주세요"
        disabled
        aria-disabled="true"
        maxlength="80"
        pattern="^[a-zA-Z가-힣0-9\s\-.,()/?]{2,80}$"
        title="2~80자, 한글/영문/숫자와 공백, - . , ( ) / ? 허용"
      >
      <br><br>

      <!-- 답변: 비밀번호처럼 마스킹, 보기/숨기기 토글은 체크박스로(추후 JS 연결) -->
      <label for="seca">답변</label>
      <input
        type="password"
        id="seca"
        name="seca"
        placeholder="답변을 입력해 주세요"
        autocomplete="off"
        required
        minlength="4"
        maxlength="64"
        title="4~64자. 답변은 화면에 표시되지 않습니다."
      >
      <label style="display:inline-flex; align-items:center; gap:6px; margin-top:6px;">
        <input type="checkbox" id="toggleSecA"> 답변 보기
      </label>

      <!-- 실시간 안내 메시지 영역(추후 JS로 갱신) -->
      <div id="secqMsg" aria-live="polite" style="font-size:12px; margin-top:4px;"></div>
    </fieldset>

    <br>

    <!-- 성별 그룹: 라디오 name 동일 → 배타적 선택 -->
    <fieldset>
      <legend>성별</legend>

      <label>
        <input type="radio" name="gender" id="gender_male" value="male" required>
        male
      </label>
      <label>
        <input type="radio" name="gender" id="gender_female" value="female" required>
        female
      </label>
      <label>
        <input type="radio" name="gender" id="gender_other" value="other" required>
        other
      </label>
      <br>

      <!-- 'other' 선택 시에만 활성화(추후 JS로 enable/disable) -->
      <input
        type="text"
        id="gender_other_text"
        name="gender_other_text"
        placeholder="'other'를 선택 시 작성해 주세요"
        disabled
        aria-disabled="true"
      >
    </fieldset>

    <br>

    <!-- 선택 질문(옵션 항목) -->
    <fieldset>
      <legend>선택 질문</legend>

      <!-- 직업: 드롭다운 + 기타 -->
      <label for="job">직업 선택</label>
      <select id="job" name="job">
        <option value="" selected disabled>선택하세요</option>
        <option value="student">학생</option>
        <option value="employee">직장인</option>
        <option value="freelancer">프리랜서</option>
        <option value="self-employed">자영업</option>
        <option value="job-seeker">구직 중</option>
        <option value="unemployed">무직</option>
        <option value="other">기타(직접 입력)</option>
      </select>
      <br><br>

      <!-- 기타 직업: textarea로 변경(여러 단어/줄 입력 용이). 'other'일 때만 활성화(추후 JS 연동) -->
      <label for="job_other" class="visually-hidden">기타 직업 입력</label>
      <textarea
        id="job_other"
        name="job_other"
        placeholder="기타를 선택하셨다면 직업을 입력하세요"
        disabled
        aria-disabled="true"
        rows="2"
        pattern="^[a-zA-Z가-힣0-9\s/_()-]{2,30}$"
        title="2~30자, 한글/영문/숫자와 공백, / _ ( ) - 만 허용"
      ></textarea>
      <br><br>

      <!-- 전화번호: 대시(-) 선택 허용, 자동완성 힌트 tel -->
      <label for="telephone">전화 번호</label>
      <input
        type="tel"
        id="telephone"
        name="telephone"
        placeholder="전화 번호를 입력해 주세요"
        autocomplete="tel"
        inputmode="numeric"
        pattern="^\d{2,3}-?\d{3,4}-?\d{4}$"
        title="예: 010-1234-5678 또는 01012345678"
      >
      <br><br>

      <!-- 생년월일: 자동완성 힌트 bday -->
      <label for="birthday">생년월일</label>
      <input
        type="date"
        id="birthday"
        name="birthday"
        autocomplete="bday"
      >
      <br><br>

      <!-- 프로필 이미지: 이미지 파일만 선택, 미리보기/삭제 UI는 별도 JS 필요 -->
      <label for="profile_image">프로필 이미지 사진</label><br>
      <input
        type="file"
        id="profile_image"
        name="profile_image"
        accept="image/*"
      >
      <div id="imgPreviewWrap">
        <!-- 선택한 이미지를 JS로 넣어 미리보기 표시 -->
        <img id="imgPreview" alt="업로드한 이미지 미리보기" hidden
             style="width:220px;height:220px;object-fit:cover;border:1px solid #ddd;border-radius:8px;">
        <p id="imgHint">이미지를 선택하면 미리보기가 표시됩니다.</p>
        <button type="button" id="clearImage" style="display:none;">이미지 삭제</button>
      </div>
      <br><br>

      <!-- 주소: 우편번호/기본주소/상세주소. 주소 찾기 버튼은 추후 API 연결 -->
      <fieldset>
        <legend>주소</legend>

        <div>
          <label for="postcode">우편번호</label>
          <input
            type="text"
            id="postcode"
            name="postcode"
            inputmode="numeric"
            pattern="^\d{5}$"
            placeholder="우편번호 5자리"
            autocomplete="postal-code"
            required
          >
          <button type="button" id="findPostcode">주소 찾기(나중에 연결)</button>
        </div>
        <br>

        <div>
          <label for="address1">기본주소</label>
          <input
            type="text"
            id="address1"
            name="address1"
            placeholder="도로명/지번 주소"
            autocomplete="address-line1"
            pattern="^[a-zA-Z가-힣0-9\s\-.,()/#]{2,100}$"
            title="2~100자, 한글/영문/숫자와 공백, - . , ( ) / # 허용"
            required
          >
        </div>
        <br>

        <div>
          <label for="address2">상세주소</label>
          <input
            type="text"
            id="address2"
            name="address2"
            placeholder="동/호수, 건물명 등 (선택)"
            autocomplete="address-line2"
            pattern="^[a-zA-Z가-힣0-9\s\-.,()/#]{0,100}$"
            title="한글/영문/숫자와 공백, - . , ( ) / # 허용"
          >
        </div>
      </fieldset>
    </fieldset>
      <fieldset>
    <div>
      <h4>[필수] 서비스 이용 약관</h4>
      <p>
        1. 본 서비스는 회원 가입 후 이용 가능합니다.<br>
        2. 회원은 타인의 정보를 도용하거나 허위 정보를 기재해서는 안 됩니다.<br>
        3. 서비스 이용 중 불법 행위, 명예 훼손, 음란물 배포, 스팸 전송 등의 행위를 해서는 안 됩니다.<br>
        4. 회사는 서비스 품질 향상과 운영을 위해 회원의 일부 정보를 수집·이용할 수 있습니다.<br>
        5. 회원 탈퇴 시 관련 법령에 따라 일정 기간 동안 개인정보를 보관할 수 있습니다.<br>
      </p>

      <h4>[필수] 개인정보 수집 및 이용 동의</h4>
      <p>
        - 수집 항목: 이름, 아이디, 비밀번호, 이메일, 연락처, 주소, 생년월일, 직업<br>
        - 수집 목적: 서비스 제공, 회원 관리, 민원 처리, 마케팅·광고 활용<br>
        - 보유 기간: 회원 탈퇴 후 5년(관계 법령에 따름)<br>
      </p>

      <h4>[선택] 마케팅 정보 수신 동의</h4>
      <p>
        이벤트, 할인 혜택, 신규 서비스 안내 등의 정보를 이메일·SMS로 수신합니다.<br>
        동의하지 않더라도 서비스 이용에는 제한이 없습니다.<br>
      </p>
    </div>
  </fieldset>
  <label><input type="checkbox" required> [필수] 약관에 동의합니다</label><br>
  <label><input type="checkbox"> [선택] 마케팅 정보 수신에 동의합니다</label>


    <br>
    <!-- 재작성 버튼 누를시 입력한 모든 데이터 초기화 -->
    <button type="button" id="btn_del_all">재작성</button>
    <button type="button" id="btn">제출</button>
   
  </form>
</body>
</html>