다양한 입력 요소(text·checkbox·radio·select)에서 값 읽기, submit 이벤트 처리, 클라이언트 사이드 유효성 검사, 실시간 피드백 패턴까지 폼 처리를 완성합니다.
🎯 학습 목표
이 강을 마치면 다음을 할 수 있습니다.
- 텍스트 입력, 체크박스, 라디오 버튼, 셀렉트 박스에서 값을 읽을 수 있습니다.
- 폼 제출 이벤트를 가로채 자바스크립트로 처리할 수 있습니다.
- 간단한 클라이언트 사이드 유효성 검사를 구현할 수 있습니다.
- 실시간 입력 피드백을 제공하는 UI를 만들 수 있습니다.
📝 텍스트 입력값 읽기
이 섹션에서는 <input>과 <textarea>에서 사용자가 입력한 값을 읽는 방법을 살펴보겠습니다.
폼의 가장 기본은 텍스트 입력입니다. 자바스크립트에서 입력 요소의 현재 값은 .value 속성으로 읽고 씁니다. 18강에서 배운 이벤트와 결합하면 실시간 처리가 가능합니다.
// HTML:
// <input type="text" id="username" placeholder="이름 입력">
// <input type="number" id="age" min="0" max="120">
// <input type="password" id="pwd">
// <textarea id="bio"></textarea>
const usernameInput = document.querySelector("#username");
const ageInput = document.querySelector("#age");
const bioArea = document.querySelector("#bio");
// 값 읽기
console.log(usernameInput.value); // 현재 입력값 (문자열)
console.log(ageInput.value); // 숫자여도 문자열! "25"
console.log(Number(ageInput.value)); // 숫자로 변환 필요
// 값 설정
usernameInput.value = "홍길동";
// 포커스 & 초기화
usernameInput.focus(); // 커서 이동
usernameInput.select(); // 텍스트 전체 선택
usernameInput.value = ""; // 초기화
// 실시간 글자 수 카운터
const bioDisplay = document.querySelector("#bio-count");
bioArea.addEventListener("input", (e) => {
const len = e.target.value.length;
bioDisplay.textContent = `${len} / 200자`;
bioDisplay.style.color = len > 200 ? "red" : "gray";
});
// 실시간 검색 (디바운스 적용)
let timer;
usernameInput.addEventListener("input", (e) => {
clearTimeout(timer); // 이전 타이머 취소
timer = setTimeout(() => {
console.log(`검색: ${e.target.value}`); // 입력 멈추고 300ms 후 실행
}, 300);
});
중요한 점은 input.value는 항상 문자열을 반환한다는 것입니다. type="number"로 설정해도 값은 문자열로 반환됩니다. 숫자 계산에 사용하려면 Number(), parseInt(), parseFloat()으로 변환해야 합니다. 디바운스(Debounce) 패턴은 실시간 검색에서 필수입니다. 사용자가 타이핑할 때마다 API를 호출하면 서버에 부담이 크므로, 입력이 멈춘 후 일정 시간이 지났을 때만 처리하는 패턴입니다. 22강 타이머에서 자세히 다룹니다.
☑️ 체크박스와 라디오 버튼
이 섹션에서는 체크박스와 라디오 버튼처럼 선택/해제 방식의 입력 요소를 다루는 방법을 살펴보겠습니다.
체크박스와 라디오 버튼은 텍스트 입력과 달리 .value가 아닌 .checked 속성으로 선택 여부를 확인합니다. 이 차이를 이해하는 것이 중요합니다.
// HTML:
// <input type="checkbox" id="agree" value="agree">
// <label for="agree">약관에 동의합니다</label>
//
// <input type="radio" name="gender" value="male" id="male">
// <label for="male">남성</label>
// <input type="radio" name="gender" value="female" id="female">
// <label for="female">여성</label>
// 체크박스
const agreeBox = document.querySelector("#agree");
console.log(agreeBox.checked); // true/false (선택 여부)
console.log(agreeBox.value); // "agree" (value 속성값, 체크 여부 무관)
agreeBox.addEventListener("change", (e) => {
console.log(e.target.checked); // true (체크) or false (해제)
submitBtn.disabled = !e.target.checked; // 미동의 시 버튼 비활성화
});
// 여러 체크박스 — 선택된 것만 수집
const hobbies = document.querySelectorAll("input[name='hobby']");
const selected = [...hobbies]
.filter(cb => cb.checked)
.map(cb => cb.value);
console.log(selected); // ["독서", "코딩"] 등
// 라디오 버튼
const genderRadios = document.querySelectorAll("input[name='gender']");
genderRadios.forEach(radio => {
radio.addEventListener("change", (e) => {
if (e.target.checked) {
console.log(`선택: ${e.target.value}`); // "male" or "female"
}
});
});
// 현재 선택된 라디오 값 읽기
const selectedGender = document.querySelector("input[name='gender']:checked");
console.log(selectedGender?.value); // "male" or null (없으면 null)
라디오 버튼 그룹에서 현재 선택된 값을 읽을 때 :checked CSS 가상 클래스를 선택자로 사용하는 패턴이 편리합니다. 선택된 것이 없으면 null을 반환하므로 옵셔널 체이닝 ?.value를 함께 사용합니다. 체크박스에서 여러 개를 선택해 배열로 수집할 때는 querySelectorAll로 모두 선택한 뒤 배열로 변환해 filter(cb => cb.checked)로 걸러내는 패턴을 사용합니다.
📋 셀렉트 박스
이 섹션에서는 드롭다운 선택 요소인 <select>와 <option>을 다루는 방법을 살펴보겠습니다.
// HTML:
// <select id="country">
// <option value="">선택하세요</option>
// <option value="kr">대한민국</option>
// <option value="jp">일본</option>
// <option value="us">미국</option>
// </select>
const select = document.querySelector("#country");
// 현재 선택된 값
console.log(select.value); // "kr" 등 선택된 option의 value
console.log(select.options[select.selectedIndex].text); // 표시 텍스트
// 변경 이벤트
select.addEventListener("change", (e) => {
console.log(e.target.value); // 새로 선택된 값
if (!e.target.value) {
console.log("선택하세요 항목 선택됨");
}
});
// 동적으로 옵션 추가
const newOption = document.createElement("option");
newOption.value = "cn";
newOption.textContent = "중국";
select.appendChild(newOption);
// 다중 선택 — multiple 속성
// <select multiple id="tags">...</select>
const multiSelect = document.querySelector("#tags");
const selectedValues = [...multiSelect.selectedOptions]
.map(opt => opt.value);
console.log(selectedValues); // 선택된 모든 값 배열
📤 폼 제출 처리
이 섹션에서는 폼 전체를 처리하는 submit 이벤트와 여러 입력값을 한 번에 수집하는 방법을 살펴보겠습니다.
18강에서 event.preventDefault()로 폼 기본 동작을 막는 법을 배웠습니다. 이를 폼의 모든 입력값 수집과 결합하면 완전한 폼 처리 로직을 만들 수 있습니다.
// HTML:
// <form id="signup-form">
// <input type="text" name="username" required>
// <input type="email" name="email" required>
// <input type="password" name="password" minlength="8">
// <button type="submit">가입하기</button>
// </form>
const form = document.querySelector("#signup-form");
form.addEventListener("submit", (event) => {
event.preventDefault(); // 페이지 새로고침 막기
// 방법 1: FormData로 수집 (name 속성 필요)
const formData = new FormData(form);
console.log(formData.get("username"));
console.log(formData.get("email"));
// Object로 변환
const data = Object.fromEntries(formData);
console.log(data); // { username: "...", email: "...", password: "..." }
// 방법 2: 개별 요소에서 직접 읽기
const username = form.querySelector("[name='username']").value.trim();
const email = form.querySelector("[name='email']").value.trim();
const password = form.querySelector("[name='password']").value;
// 유효성 검사 후 처리
if (validate({ username, email, password })) {
console.log("서버로 전송!", { username, email });
// fetch로 API 호출 (26강에서 학습)
}
});
function validate({ username, email, password }) {
const errors = [];
if (!username || username.length < 2) {
errors.push("이름은 2자 이상이어야 합니다");
}
if (!email.includes("@")) {
errors.push("올바른 이메일 형식이 아닙니다");
}
if (password.length < 8) {
errors.push("비밀번호는 8자 이상이어야 합니다");
}
if (errors.length > 0) {
alert(errors.join("\n")); // 실제 서비스에서는 UI 표시 권장
return false;
}
return true;
}
FormData는 폼의 모든 입력을 한 번에 수집하는 브라우저 내장 클래스입니다. Object.fromEntries(formData)로 일반 객체로 변환하면 다루기 쉽습니다. 단, type="file"의 파일 객체나 체크박스 그룹은 별도 처리가 필요합니다. 유효성 검사를 서버에만 맡기지 않고 클라이언트에서도 수행하면 불필요한 서버 요청을 줄이고 즉각적인 피드백을 제공할 수 있습니다. 다만 클라이언트 검사는 우회가 가능하므로 서버 검사를 반드시 병행해야 합니다.
📝 핵심 요약
19강에서 배운 내용을 정리해 보겠습니다.
| 입력 요소 | 값 읽기 | 이벤트 |
|---|---|---|
| text/number/password | el.value | input(실시간), change(확정) |
| textarea | el.value | input |
| checkbox | el.checked | change |
| radio | el.checked / :checked 선택자 | change |
| select | el.value | change |
| 폼 전체 | new FormData(form) | submit (+ preventDefault) |
다음 강인 20강 — 요소 생성·삭제에서는 자바스크립트로 HTML 요소를 동적으로 만들고 DOM에 추가하거나 제거하는 방법을 배웁니다. createElement, appendChild, remove를 마스터하면 17강의 innerHTML보다 안전하고 강력한 방식으로 UI를 동적으로 구성할 수 있습니다!
관련 주제
- 폼 입력값 읽기
- submit 이벤트
- 유효성 검사
- checkbox radio select
- 실시간 입력 피드백
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 자바스크립트 기초 30강 — 웹을 움직이는 언어
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
📚 시리즈 전체 공유
자바스크립트 기초 30강 — 웹을 움직이는 언어
이 강의가 속한 시리즈는 총 22강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.
댓글
불러오는 중...
