React도 모르고, Vue도 모르고, 그냥 JavaScript만 손에 쥐어진 상태에서 뭔가 만들어보려고 했을 때 제일 막막했던 게 있다.
"jQuery 없으면 못 하는 거 아니야?"
아니다. Vanilla JS — 즉 순수 JavaScript만으로 DOM 조작, 이벤트 처리, 동적 렌더링까지 다 된다. 오히려 기초를 제대로 잡으면 React 같은 라이브러리를 나중에 배울 때 왜 그렇게 만들었는지 이해가 훨씬 빠르다.
변수와 데이터 타입
JavaScript에서 변수를 선언하는 방법은 세 가지다.
var name = '옛날 방식'; // 쓰지 마라
let count = 0; // 재할당 가능
const MAX = 100; // 재할당 불가
JavaScript
복사
var는 함수 스코프라 예상치 못한 동작이 생긴다. 요즘은 거의 안 쓴다. let과 const는 블록 스코프라 훨씬 예측하기 쉽다. 기본적으로 const 쓰고, 값이 바뀌어야 할 때만 let 쓰는 게 습관이 된다.
데이터 타입은 크게 이렇다.
const str = '문자열'; // String
const num = 42; // Number
const bool = true; // Boolean
const arr = [1, 2, 3]; // Array
const obj = { name: 'Kim' }; // Object
const nothing = null; // null
let undef; // undefined
JavaScript
복사
함수 선언 방식
JavaScript에서 함수를 쓰는 방법도 여러 가지다.
// 함수 선언식 — 호이스팅됨
function greet(name) {
return `안녕, ${name}!`;
}
// 함수 표현식
const greet = function(name) {
return `안녕, ${name}!`;
};
// 화살표 함수 — 요즘 가장 많이 씀
const greet = (name) => `안녕, ${name}!`;
JavaScript
복사
화살표 함수는 return이 한 줄이면 중괄호와 return 키워드 생략 가능하다. 콜백 함수에서 특히 자주 쓴다.
DOM 조작
DOM(Document Object Model)은 HTML을 JavaScript로 다룰 수 있게 해주는 구조다. 여기서부터 진짜 동적인 페이지를 만들 수 있게 된다.
// 요소 선택
const btn = document.querySelector('#myBtn'); // ID
const items = document.querySelectorAll('.item'); // 클래스 전체
// 내용 변경
const title = document.querySelector('h1');
title.textContent = '바뀐 제목';
title.innerHTML = '<span>HTML도 가능</span>';
// 스타일 변경
title.style.color = 'tomato';
title.style.fontSize = '2rem';
// 클래스 조작
title.classList.add('active');
title.classList.remove('active');
title.classList.toggle('active'); // 있으면 제거, 없으면 추가
JavaScript
복사
querySelector는 CSS 선택자 문법을 그대로 쓴다. 익숙한 문법이라 금방 손에 익는다.
이벤트 처리
버튼 클릭, 입력, 키보드, 마우스 — 모든 사용자 인터랙션은 이벤트로 처리한다.
const btn = document.querySelector('#btn');
btn.addEventListener('click', function(e) {
console.log('클릭됨', e.target);
});
// 화살표 함수로
btn.addEventListener('click', (e) => {
e.preventDefault(); // 기본 동작 막기 (링크 이동, 폼 제출 등)
console.log('클릭됨');
});
JavaScript
복사
자주 쓰는 이벤트 종류는 이렇다.
•
click — 클릭
•
input — 입력값 변경
•
submit — 폼 제출
•
keydown / keyup — 키보드
•
mouseover / mouseout — 마우스 hover
•
DOMContentLoaded — HTML 파싱 완료
동적 요소 생성과 삭제
JavaScript로 HTML 요소를 직접 만들고 붙이고 지우는 것도 된다.
// 요소 생성
const newItem = document.createElement('li');
newItem.textContent = '새 항목';
newItem.classList.add('item');
// 부모에 추가
const list = document.querySelector('#list');
list.appendChild(newItem); // 마지막에 추가
list.prepend(newItem); // 맨 앞에 추가
// 삭제
newItem.remove();
JavaScript
복사
배열과 객체 다루기
JavaScript에서 데이터를 다루는 핵심은 배열 메서드다.
const nums = [1, 2, 3, 4, 5];
// map — 변환해서 새 배열
const doubled = nums.map(n => n * 2); // [2, 4, 6, 8, 10]
// filter — 조건에 맞는 것만
const evens = nums.filter(n => n % 2 === 0); // [2, 4]
// find — 첫 번째 일치하는 것
const found = nums.find(n => n > 3); // 4
// forEach — 반복 실행
nums.forEach(n => console.log(n));
// reduce — 누적
const sum = nums.reduce((acc, cur) => acc + cur, 0); // 15
JavaScript
복사
이 다섯 개만 제대로 써도 대부분의 데이터 처리가 된다.
Vanilla JS가 왜 중요하냐면
React를 배울 때 useState가 왜 존재하는지, 왜 직접 DOM을 건드리지 않는지 — 이게 이해가 안 되면 그냥 외워서 쓰게 된다. Vanilla JS로 직접 DOM을 조작해보면 왜 프레임워크가 그 문제를 해결하려 했는지 자연스럽게 납득이 된다.
기초 없이 프레임워크부터 들어가면 나중에 반드시 벽에 막힌다. 순서가 있다.
실제 구현 코드 전체
JS 기초 개념들을 전부 담은 인터랙티브 페이지다. 변수 타입 확인기, 할 일 목록(CRUD), 실시간 필터, 키보드 이벤트까지 Live Server로 바로 띄울 수 있다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vanilla JS 기초 데모</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--primary: #f59e0b;
--primary-dark: #d97706;
--bg: #0f172a;
--surface: #1e293b;
--surface2: #334155;
--text: #f1f5f9;
--sub: #94a3b8;
--border: #334155;
--radius: 10px;
--green: #22c55e;
--red: #ef4444;
}
body {
font-family: 'Segoe UI', sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.7;
}
/* ── 내비게이션 ── */
header {
position: sticky;
top: 0;
background: var(--surface);
border-bottom: 1px solid var(--border);
z-index: 100;
}
nav {
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
height: 56px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo { font-weight: 800; font-size: 1.1rem; color: var(--primary); }
.nav-tabs { display: flex; gap: 6px; list-style: none; }
.nav-tabs button {
background: none;
border: none;
color: var(--sub);
cursor: pointer;
padding: 6px 14px;
border-radius: 6px;
font-size: 0.88rem;
transition: all 0.2s;
}
.nav-tabs button:hover,
.nav-tabs button.active {
background: var(--primary);
color: #000;
font-weight: 600;
}
/* ── 공통 레이아웃 ── */
main { max-width: 960px; margin: 0 auto; padding: 40px 20px; }
.section { display: none; }
.section.active { display: block; }
.section-title {
font-size: 1.4rem;
font-weight: 700;
margin-bottom: 6px;
}
.section-desc {
color: var(--sub);
font-size: 0.9rem;
margin-bottom: 28px;
}
/* ── 카드 ── */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 24px;
margin-bottom: 16px;
}
.card h3 {
font-size: 0.95rem;
font-weight: 700;
margin-bottom: 14px;
color: var(--primary);
}
/* ── 공통 인풋/버튼 ── */
input[type=text], select {
width: 100%;
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text);
padding: 10px 14px;
font-size: 0.9rem;
outline: none;
transition: border-color 0.2s;
}
input[type=text]:focus, select:focus {
border-color: var(--primary);
}
.btn {
padding: 9px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 0.88rem;
font-weight: 600;
transition: opacity 0.2s;
}
.btn:hover { opacity: 0.85; }
.btn-primary { background: var(--primary); color: #000; }
.btn-danger { background: var(--red); color: #fff; }
.btn-sm { padding: 5px 12px; font-size: 0.8rem; }
/* ── 섹션 1: 타입 확인기 ── */
.type-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 12px;
margin-top: 16px;
}
.type-item {
background: var(--surface2);
border-radius: 8px;
padding: 14px 16px;
}
.type-item .t-label {
font-size: 0.75rem;
color: var(--sub);
margin-bottom: 4px;
}
.type-item .t-value {
font-size: 0.95rem;
font-weight: 600;
word-break: break-all;
}
.type-item .t-type {
display: inline-block;
margin-top: 6px;
padding: 2px 8px;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 700;
}
.badge-string { background: #3b82f620; color: #60a5fa; }
.badge-number { background: #22c55e20; color: #4ade80; }
.badge-boolean { background: #f59e0b20; color: #fbbf24; }
.badge-object { background: #a855f720; color: #c084fc; }
.badge-undefined { background: #64748b20; color: #94a3b8; }
#typeResult {
margin-top: 14px;
padding: 14px 16px;
background: var(--surface2);
border-radius: 8px;
font-size: 0.9rem;
min-height: 48px;
}
/* ── 섹션 2: 할 일 목록 ── */
.todo-form {
display: flex;
gap: 10px;
margin-bottom: 16px;
}
.todo-form input { flex: 1; }
#todoFilter {
display: flex;
gap: 8px;
margin-bottom: 14px;
}
#todoFilter button {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--sub);
padding: 5px 14px;
font-size: 0.82rem;
cursor: pointer;
transition: all 0.2s;
}
#todoFilter button.active {
background: var(--primary);
border-color: var(--primary);
color: #000;
font-weight: 600;
}
#todoList { list-style: none; display: flex; flex-direction: column; gap: 8px; }
.todo-item {
display: flex;
align-items: center;
gap: 12px;
background: var(--surface2);
border-radius: 8px;
padding: 12px 16px;
transition: opacity 0.2s;
}
.todo-item.done { opacity: 0.4; }
.todo-item.done .todo-text { text-decoration: line-through; }
.todo-check {
width: 18px; height: 18px;
accent-color: var(--primary);
cursor: pointer;
flex-shrink: 0;
}
.todo-text { flex: 1; font-size: 0.92rem; }
#todoCount {
margin-top: 14px;
font-size: 0.83rem;
color: var(--sub);
}
/* ── 섹션 3: 이벤트 로그 ── */
.event-box {
background: var(--surface2);
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
}
.event-box .label {
font-size: 0.8rem;
color: var(--sub);
margin-bottom: 8px;
}
#eventTarget {
width: 100%;
height: 90px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
background: var(--surface);
border: 2px dashed var(--border);
font-size: 0.9rem;
color: var(--sub);
cursor: pointer;
user-select: none;
transition: all 0.15s;
}
#eventTarget:hover { border-color: var(--primary); color: var(--primary); }
#eventTarget.pressed {
background: var(--primary);
color: #000;
border-style: solid;
border-color: var(--primary);
}
#keyDisplay {
text-align: center;
font-size: 1.6rem;
font-weight: 800;
min-height: 48px;
color: var(--primary);
margin-top: 10px;
}
#eventLog {
list-style: none;
display: flex;
flex-direction: column;
gap: 6px;
max-height: 200px;
overflow-y: auto;
}
.log-item {
display: flex;
align-items: center;
gap: 10px;
background: var(--surface2);
border-radius: 6px;
padding: 8px 12px;
font-size: 0.83rem;
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:none; } }
.log-badge {
padding: 2px 8px;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
flex-shrink: 0;
}
.log-click { background: #3b82f620; color: #60a5fa; }
.log-key { background: #22c55e20; color: #4ade80; }
.log-mouse { background: #f59e0b20; color: #fbbf24; }
/* ── 섹션 4: 배열 메서드 ── */
.method-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
@media (max-width: 600px) { .method-grid { grid-template-columns: 1fr; } }
.method-card {
background: var(--surface2);
border-radius: 8px;
padding: 16px;
}
.method-card .m-name {
font-size: 0.8rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 6px;
}
.method-card .m-code {
font-family: monospace;
font-size: 0.8rem;
color: var(--sub);
margin-bottom: 10px;
line-height: 1.6;
}
.method-card .m-result {
font-size: 0.85rem;
color: var(--green);
font-weight: 600;
}
.run-btn-wrap { text-align: center; margin-top: 20px; }
</style>
</head>
<body>
<header>
<nav>
<span class="logo">⚡ Vanilla JS</span>
<ul class="nav-tabs">
<li><button class="active" data-tab="tab-types">타입</button></li>
<li><button data-tab="tab-todo">할 일</button></li>
<li><button data-tab="tab-events">이벤트</button></li>
<li><button data-tab="tab-array">배열</button></li>
</ul>
</nav>
</header>
<main>
<!-- 섹션 1: 데이터 타입 확인기 -->
<section class="section active" id="tab-types">
<p class="section-title">데이터 타입 확인기</p>
<p class="section-desc">값을 입력하면 JavaScript가 어떤 타입으로 인식하는지 확인한다. typeof 연산자가 어떻게 동작하는지 눈으로 볼 수 있다.</p>
<div class="card">
<h3>📌 직접 입력해보기</h3>
<div style="display:flex; gap:10px; align-items:center;">
<input type="text" id="typeInput" placeholder="값을 입력하세요 (예: 42, true, "hello", null)" />
<button class="btn btn-primary" id="typeCheck">확인</button>
</div>
<div id="typeResult">여기에 결과가 표시됩니다.</div>
</div>
<div class="card">
<h3>📌 기본 타입 예시</h3>
<div class="type-grid" id="typeGrid"></div>
</div>
</section>
<!-- 섹션 2: 할 일 목록 -->
<section class="section" id="tab-todo">
<p class="section-title">할 일 목록 (CRUD)</p>
<p class="section-desc">createElement, appendChild, remove — DOM 조작 기초를 실제로 써보는 예제다. 추가/완료/삭제/필터가 전부 Vanilla JS로만 구현돼 있다.</p>
<div class="card">
<h3>📌 할 일 추가</h3>
<div class="todo-form">
<input type="text" id="todoInput" placeholder="할 일을 입력하고 Enter 또는 추가 버튼" />
<button class="btn btn-primary" id="todoAdd">추가</button>
</div>
<div id="todoFilter">
<button class="active" data-filter="all">전체</button>
<button data-filter="active">미완료</button>
<button data-filter="done">완료</button>
</div>
<ul id="todoList"></ul>
<p id="todoCount"></p>
</div>
</section>
<!-- 섹션 3: 이벤트 로그 -->
<section class="section" id="tab-events">
<p class="section-title">이벤트 처리 데모</p>
<p class="section-desc">addEventListener로 클릭, 키보드, 마우스 이벤트를 잡는 예제다. 어떤 이벤트가 어느 타이밍에 발생하는지 로그로 확인할 수 있다.</p>
<div class="card">
<h3>📌 클릭 / 마우스 이벤트</h3>
<div class="event-box">
<p class="label">아래 박스를 클릭하거나 마우스를 올려보자</p>
<div id="eventTarget">여기를 클릭하거나 마우스를 올려보자</div>
</div>
</div>
<div class="card">
<h3>📌 키보드 이벤트</h3>
<div class="event-box">
<p class="label">아무 키나 눌러보자 (이 페이지에 포커스된 상태에서)</p>
<div id="keyDisplay">—</div>
</div>
</div>
<div class="card">
<h3>📌 이벤트 로그</h3>
<ul id="eventLog"></ul>
</div>
</section>
<!-- 섹션 4: 배열 메서드 -->
<section class="section" id="tab-array">
<p class="section-title">배열 메서드 시각화</p>
<p class="section-desc">map, filter, find, reduce, forEach — 각각이 어떤 값을 돌려주는지 버튼 한 번으로 확인한다.</p>
<div class="card">
<h3>📌 원본 배열</h3>
<p style="font-family:monospace; font-size:0.95rem; color:var(--primary); margin-bottom:20px;">
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
</p>
<div class="method-grid" id="methodGrid"></div>
<div class="run-btn-wrap">
<button class="btn btn-primary" id="runAll">▶ 전체 실행</button>
</div>
</div>
</section>
</main>
<script>
// ══════════════════════════════════════════
// 1. 탭 네비게이션
// ══════════════════════════════════════════
const tabBtns = document.querySelectorAll('.nav-tabs button');
const sections = document.querySelectorAll('.section');
tabBtns.forEach(btn => {
btn.addEventListener('click', () => {
// 모든 탭 비활성화
tabBtns.forEach(b => b.classList.remove('active'));
sections.forEach(s => s.classList.remove('active'));
// 클릭한 탭 활성화
btn.classList.add('active');
document.getElementById(btn.dataset.tab).classList.add('active');
});
});
// ══════════════════════════════════════════
// 2. 데이터 타입 확인기
// ══════════════════════════════════════════
const typeExamples = [
{ label: '문자열', value: '"hello"', display: 'hello', type: 'string' },
{ label: '숫자', value: '42', display: '42', type: 'number' },
{ label: '소수', value: '3.14', display: '3.14', type: 'number' },
{ label: '불리언 참', value: 'true', display: 'true', type: 'boolean' },
{ label: '불리언 거짓',value: 'false', display: 'false', type: 'boolean' },
{ label: 'null', value: 'null', display: 'null', type: 'object' },
{ label: 'undefined', value: 'undefined', display: 'undefined', type: 'undefined' },
{ label: '배열', value: '[1,2,3]', display: '[1,2,3]', type: 'object' },
{ label: '객체', value: '{a:1}', display: '{a:1}', type: 'object' },
];
const typeGrid = document.getElementById('typeGrid');
typeExamples.forEach(ex => {
const item = document.createElement('div');
item.className = 'type-item';
item.innerHTML = `
<div class="t-label">${ex.label}</div>
<div class="t-value">${ex.display}</div>
<span class="t-type badge-${ex.type}">typeof → "${ex.type}"</span>
`;
typeGrid.appendChild(item);
});
// 직접 입력 타입 확인
const typeInput = document.getElementById('typeInput');
const typeCheck = document.getElementById('typeCheck');
const typeResult = document.getElementById('typeResult');
function checkType() {
const raw = typeInput.value.trim();
if (!raw) { typeResult.textContent = '값을 입력해주세요.'; return; }
let evaled, typeName;
try {
evaled = eval(raw);
typeName = typeof evaled;
} catch(e) {
typeName = 'error';
evaled = e.message;
}
typeResult.innerHTML = `
<span style="color:var(--sub)">입력: </span><strong style="color:var(--text)">${raw}</strong>
→
<span style="color:var(--sub)">typeof: </span><strong style="color:var(--primary)">"${typeName}"</strong>
|
<span style="color:var(--sub)">값: </span><strong style="color:var(--green)">${String(evaled)}</strong>
`;
}
typeCheck.addEventListener('click', checkType);
typeInput.addEventListener('keydown', e => { if (e.key === 'Enter') checkType(); });
// ══════════════════════════════════════════
// 3. 할 일 목록
// ══════════════════════════════════════════
let todos = [];
let filter = 'all';
const todoInput = document.getElementById('todoInput');
const todoAdd = document.getElementById('todoAdd');
const todoList = document.getElementById('todoList');
const todoCount = document.getElementById('todoCount');
function renderTodos() {
todoList.innerHTML = '';
const filtered = todos.filter(t => {
if (filter === 'active') return !t.done;
if (filter === 'done') return t.done;
return true;
});
if (filtered.length === 0) {
todoList.innerHTML = '<li style="color:var(--sub); font-size:0.88rem; padding:10px 0;">할 일이 없다.</li>';
}
filtered.forEach(t => {
const li = document.createElement('li');
li.className = 'todo-item' + (t.done ? ' done' : '');
li.innerHTML = `
<input type="checkbox" class="todo-check" ${t.done ? 'checked' : ''} data-id="${t.id}" />
<span class="todo-text">${t.text}</span>
<button class="btn btn-danger btn-sm" data-del="${t.id}">삭제</button>
`;
todoList.appendChild(li);
});
const done = todos.filter(t => t.done).length;
todoCount.textContent = `전체 ${todos.length}개 · 완료 ${done}개 · 미완료 ${todos.length - done}개`;
}
function addTodo() {
const text = todoInput.value.trim();
if (!text) return;
todos.push({ id: Date.now(), text, done: false });
todoInput.value = '';
renderTodos();
}
todoAdd.addEventListener('click', addTodo);
todoInput.addEventListener('keydown', e => { if (e.key === 'Enter') addTodo(); });
// 체크박스 & 삭제 — 이벤트 위임
todoList.addEventListener('click', e => {
const id = Number(e.target.dataset.id || e.target.dataset.del);
if (e.target.dataset.id !== undefined) {
const t = todos.find(t => t.id === id);
if (t) t.done = !t.done;
}
if (e.target.dataset.del !== undefined) {
todos = todos.filter(t => t.id !== id);
}
renderTodos();
});
// 필터 버튼
document.querySelectorAll('#todoFilter button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#todoFilter button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
filter = btn.dataset.filter;
renderTodos();
});
});
renderTodos();
// ══════════════════════════════════════════
// 4. 이벤트 로그
// ══════════════════════════════════════════
const eventTarget = document.getElementById('eventTarget');
const keyDisplay = document.getElementById('keyDisplay');
const eventLog = document.getElementById('eventLog');
function addLog(type, message) {
const li = document.createElement('li');
li.className = 'log-item';
li.innerHTML = `<span class="log-badge log-${type}">${type}</span><span>${message}</span>`;
eventLog.prepend(li);
// 최대 20개
if (eventLog.children.length > 20) eventLog.lastElementChild.remove();
}
eventTarget.addEventListener('click', () => {
addLog('click', '클릭 이벤트 발생');
});
eventTarget.addEventListener('mousedown', () => {
eventTarget.classList.add('pressed');
});
eventTarget.addEventListener('mouseup', () => {
eventTarget.classList.remove('pressed');
});
eventTarget.addEventListener('mouseover', () => {
addLog('mouse', 'mouseover — 마우스가 박스 안으로 들어왔다');
});
eventTarget.addEventListener('mouseout', () => {
addLog('mouse', 'mouseout — 마우스가 박스 밖으로 나갔다');
});
document.addEventListener('keydown', e => {
if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
keyDisplay.textContent = e.key === ' ' ? 'Space' : e.key;
addLog('key', `keydown → "${e.key === ' ' ? 'Space' : e.key}"`);
});
// ══════════════════════════════════════════
// 5. 배열 메서드 시각화
// ══════════════════════════════════════════
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const methods = [
{
name: 'map',
code: 'nums.map(n => n * 2)',
desc: '모든 요소를 2배로 변환',
run: () => nums.map(n => n * 2),
},
{
name: 'filter',
code: 'nums.filter(n => n % 2 === 0)',
desc: '짝수만 남긴다',
run: () => nums.filter(n => n % 2 === 0),
},
{
name: 'find',
code: 'nums.find(n => n > 6)',
desc: '6보다 큰 첫 번째 값',
run: () => nums.find(n => n > 6),
},
{
name: 'reduce',
code: 'nums.reduce((acc, cur) => acc + cur, 0)',
desc: '전체 합산',
run: () => nums.reduce((acc, cur) => acc + cur, 0),
},
{
name: 'every',
code: 'nums.every(n => n > 0)',
desc: '모두 0보다 큰가?',
run: () => nums.every(n => n > 0),
},
{
name: 'some',
code: 'nums.some(n => n > 8)',
desc: '8보다 큰 게 하나라도 있나?',
run: () => nums.some(n => n > 8),
},
{
name: 'includes',
code: 'nums.includes(5)',
desc: '5가 포함돼 있나?',
run: () => nums.includes(5),
},
{
name: 'slice',
code: 'nums.slice(2, 5)',
desc: '인덱스 2~4 잘라내기',
run: () => nums.slice(2, 5),
},
];
const methodGrid = document.getElementById('methodGrid');
methods.forEach(m => {
const card = document.createElement('div');
card.className = 'method-card';
card.innerHTML = `
<div class="m-name">.${m.name}()</div>
<div class="m-code">${m.code}</div>
<div class="m-desc" style="font-size:0.82rem; color:var(--sub); margin-bottom:8px;">${m.desc}</div>
<div class="m-result" id="result-${m.name}">— 실행 전 —</div>
`;
methodGrid.appendChild(card);
});
document.getElementById('runAll').addEventListener('click', () => {
methods.forEach(m => {
const result = m.run();
const el = document.getElementById(`result-${m.name}`);
el.textContent = '→ ' + JSON.stringify(result);
});
});
</script>
</body>
</html>
HTML
복사

