JavaScriptJavaScript 기초강의 10개 중 9번째난이도 중급

실전 패턴 — 자주 쓰는 JavaScript 기법 모음

발행 읽는 시간 약 1 분

  • JavaScript
  • 실전
  • 폼검증
  • 디바운스
  • 패턴

강의 소개

폼 유효성 검사, 디바운스, 무한 스크롤 등 실제 웹 개발에서 자주 사용하는 JavaScript 패턴들을 배웁니다.

이론에서 실전으로#

지금까지 변수, 조건문, 함수, DOM, 이벤트, 배열, fetch를 배웠습니다. 이번 편에서는 이것들을 조합해 실제 서비스에서 자주 쓰는 패턴들을 배웁니다.


패턴 1: 폼 유효성 검사#

HTML
<form id="signup-form">
  <input type="text"  id="username"  placeholder="아이디 (4~12자)" />
  <span id="username-msg" class="msg"></span>

  <input type="password" id="password" placeholder="비밀번호 (8자 이상)" />
  <span id="password-msg" class="msg"></span>

  <button type="submit">가입하기</button>
</form>
JavaScript
const form = document.querySelector("#signup-form");

function showMsg(id, text, isValid) {
  const msg = document.querySelector(`#${id}-msg`);
  msg.textContent = text;
  msg.style.color = isValid ? "green" : "red";
}

function validateUsername(value) {
  if (value.length < 4) return { ok: false, text: "4자 이상 입력하세요." };
  if (value.length > 12) return { ok: false, text: "12자 이하로 입력하세요." };
  if (!/^[a-zA-Z0-9]+$/.test(value)) return { ok: false, text: "영문/숫자만 가능합니다." };
  return { ok: true, text: "사용 가능한 아이디입니다." };
}

function validatePassword(value) {
  if (value.length < 8) return { ok: false, text: "8자 이상 입력하세요." };
  return { ok: true, text: "안전한 비밀번호입니다." };
}

// 실시간 유효성 검사
document.querySelector("#username").addEventListener("input", (e) => {
  const result = validateUsername(e.target.value);
  showMsg("username", result.text, result.ok);
});

document.querySelector("#password").addEventListener("input", (e) => {
  const result = validatePassword(e.target.value);
  showMsg("password", result.text, result.ok);
});

// 폼 제출 시 최종 검사
form.addEventListener("submit", (e) => {
  e.preventDefault();
  const username = document.querySelector("#username").value;
  const password = document.querySelector("#password").value;

  const usernameValid = validateUsername(username);
  const passwordValid = validatePassword(password);

  if (!usernameValid.ok || !passwordValid.ok) {
    alert("입력값을 확인해주세요.");
    return;
  }

  alert("가입 완료!");
});

패턴 2: 디바운스 (Debounce)#

사용자가 타이핑할 때마다 검색 요청을 보내면 서버에 부담이 됩니다. 일정 시간이 지난 후에만 실행하는 패턴입니다.

JavaScript
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

// 검색 함수
async function search(query) {
  if (!query.trim()) return;
  console.log(`"${query}" 검색 중...`);
  // 실제로는 fetch 호출
}

// 0.3초 후에만 실행
const debouncedSearch = debounce(search, 300);

document.querySelector("#search-input").addEventListener("input", (e) => {
  debouncedSearch(e.target.value);
});

패턴 3: 탭 컴포넌트#

HTML
<div class="tabs">
  <button class="tab active" data-tab="1">소개</button>
  <button class="tab" data-tab="2">경력</button>
  <button class="tab" data-tab="3">연락처</button>
</div>

<div class="tab-content active" id="tab-1">소개 내용입니다.</div>
<div class="tab-content" id="tab-2">경력 내용입니다.</div>
<div class="tab-content" id="tab-3">연락처 내용입니다.</div>
CSS
.tab-content { display: none; }
.tab-content.active { display: block; }
.tab.active { background: #0070f3; color: white; }
JavaScript
const tabs = document.querySelectorAll(".tab");
const contents = document.querySelectorAll(".tab-content");

tabs.forEach(tab => {
  tab.addEventListener("click", () => {
    const target = tab.dataset.tab;

    // 모든 탭/콘텐츠 비활성화
    tabs.forEach(t => t.classList.remove("active"));
    contents.forEach(c => c.classList.remove("active"));

    // 선택된 탭/콘텐츠 활성화
    tab.classList.add("active");
    document.querySelector(`#tab-${target}`).classList.add("active");
  });
});

패턴 4: 모달 (팝업)#

HTML
<button id="open-modal">모달 열기</button>

<div id="modal" class="modal-overlay" hidden>
  <div class="modal-box">
    <h2>알림</h2>
    <p>모달 내용입니다.</p>
    <button id="close-modal">닫기</button>
  </div>
</div>
CSS
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  display: flex;
  justify-content: center;
  align-items: center;
}

.modal-box {
  background: white;
  padding: 32px;
  border-radius: 12px;
  max-width: 400px;
  width: 90%;
}
JavaScript
const modal = document.querySelector("#modal");

document.querySelector("#open-modal").addEventListener("click", () => {
  modal.hidden = false;
});

document.querySelector("#close-modal").addEventListener("click", () => {
  modal.hidden = true;
});

// 오버레이 클릭 시 닫기
modal.addEventListener("click", (e) => {
  if (e.target === modal) modal.hidden = true;
});

// ESC 키로 닫기
document.addEventListener("keydown", (e) => {
  if (e.key === "Escape") modal.hidden = true;
});

패턴 5: 숫자 애니메이션 (카운터)#

HTML
<span class="counter" data-target="1000">0</span>
<span class="counter" data-target="500">0</span>
JavaScript
function animateCounter(element) {
  const target = parseInt(element.dataset.target);
  const duration = 1500;
  const start = performance.now();

  function update(now) {
    const elapsed = now - start;
    const progress = Math.min(elapsed / duration, 1);
    const eased = 1 - Math.pow(1 - progress, 3);  // ease-out
    element.textContent = Math.floor(eased * target).toLocaleString();

    if (progress < 1) requestAnimationFrame(update);
  }

  requestAnimationFrame(update);
}

document.querySelectorAll(".counter").forEach(animateCounter);

직접 해보기 ✏️#

탭 컴포넌트를 직접 구현해보세요.

  1. "정보", "취미", "목표" 탭 3개 만들기
  2. 각 탭 클릭 시 해당 내용만 표시
  3. 활성 탭 스타일 변경 (.active 클래스)
  4. 기본으로 첫 번째 탭 활성화

정리#

패턴용도
폼 유효성 검사사용자 입력 실시간 검증
디바운스과도한 API 호출 방지
탭 컴포넌트같은 공간에서 여러 콘텐츠 전환
모달팝업 다이얼로그
카운터 애니메이션숫자 올라가는 효과

다음 편은 JavaScript 시리즈 마지막 — 완성 프로젝트입니다!

Follow · Ask · Work

이 글이 도움이 됐다면

짧은 작업 기록과 데모는 인스타그램에 올립니다. 만들고 싶은 것이 있다면 프로젝트 의뢰나 협업 제안도 편하게 보내 주세요.

글쓴이

파란여우

직접 만든 AI 앱·제품의 개발·배포를 공개하는 실전형 테크 크리에이터

파란여우 소개 Instagram @bluefox._.hi

이어서 보기

만들고 싶은 것이 있다면 프로젝트 의뢰로 알려 주세요.