JavaScript 치트시트
ES6+ 최신 JavaScript 문법과 실전 패턴 모음 — 변수, 배열, 객체, 함수, 비동기, DOM, 유틸리티
변수 & 타입
6 itemslet / const
let은 재할당 가능, const는 불변 바인딩 (객체 내부는 변경 가능)
let count = 0;
count = 1; // OK
const API_URL = 'https://api.example.com';
// API_URL = '...'; // TypeError
const config = { debug: false };
config.debug = true; // OK — object mutation allowedTemplate Literals
백틱(`)으로 문자열 보간 및 멀티라인 지원
const name = 'World';
const greeting = `Hello, ${name}!`;
// Tagged template
function highlight(strings, ...values) {
return strings.reduce((acc, s, i) =>
acc + s + (values[i] ? `<b>${values[i]}</b>` : ''), '');
}
const msg = highlight`Welcome ${name} today`;typeof & Type Checking
런타임 타입 확인 방법
typeof 'hello' // 'string'
typeof 42 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof null // 'object' (legacy bug)
typeof {} // 'object'
typeof [] // 'object'
Array.isArray([]) // true — use this for arraysNullish Coalescing (??)
null 또는 undefined일 때만 기본값 적용 (|| 과 다름)
const port = process.env.PORT ?? 3000;
// '' ?? 'default' => '' (empty string kept)
// '' || 'default' => 'default' (falsy replaced)
// 0 ?? 42 => 0 (zero kept)
// 0 || 42 => 42 (zero replaced)
const user = response?.data ?? { name: 'Guest' };Optional Chaining (?.)
중첩 객체의 안전한 접근 — null/undefined에서 단락 평가
const city = user?.address?.city; // undefined if missing const first = arr?.[0]; // safe array access const result = obj?.method?.(); // safe method call // Combine with nullish coalescing const name = user?.profile?.name ?? 'Anonymous';
Destructuring Assignment
객체/배열에서 값을 간결하게 추출
// Object destructuring with rename & default
const { name: userName, age = 25 } = user;
// Array destructuring — skip elements
const [first, , third] = [1, 2, 3];
// Swap variables
let a = 1, b = 2;
[a, b] = [b, a];
// Nested destructuring
const { address: { city } } = user;배열
7 itemsmap / filter / reduce
배열 변환, 필터링, 집계의 핵심 메서드
const nums = [1, 2, 3, 4, 5]; const doubled = nums.map(n => n * 2); // [2, 4, 6, 8, 10] const evens = nums.filter(n => n % 2 === 0); // [2, 4] const sum = nums.reduce((acc, n) => acc + n, 0); // 15 // Chaining const result = nums .filter(n => n > 2) .map(n => n * 10) .reduce((acc, n) => acc + n, 0); // 120
find / findIndex
조건을 만족하는 첫 번째 요소 또는 인덱스 반환
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
];
const bob = users.find(u => u.name === 'Bob');
// { id: 2, name: 'Bob' }
const idx = users.findIndex(u => u.id === 3);
// 2 — returns -1 if not foundsome / every
some: 하나라도 true면 true, every: 모두 true여야 true
const ages = [18, 25, 30, 15]; ages.some(a => a >= 21); // true ages.every(a => a >= 18); // false // Practical: form validation const fields = ['name', 'email', 'password']; const allFilled = fields.every(f => form[f]?.length > 0);
flat / flatMap
중첩 배열 평탄화 및 매핑+평탄화 동시 수행
[[1, 2], [3, 4], [5]].flat();
// [1, 2, 3, 4, 5]
[1, [2, [3, [4]]]].flat(Infinity);
// [1, 2, 3, 4]
// flatMap = map + flat(1)
const sentences = ['hello world', 'foo bar'];
sentences.flatMap(s => s.split(' '));
// ['hello', 'world', 'foo', 'bar']Spread & Rest ([...arr])
배열 복사, 병합, 나머지 요소 수집
// Shallow copy const copy = [...original]; // Merge arrays const all = [...arr1, ...arr2, newItem]; // Rest in destructuring const [head, ...tail] = [1, 2, 3, 4]; // head = 1, tail = [2, 3, 4] // Remove duplicates const unique = [...new Set(arr)];
at() / Array.from()
at(): 음수 인덱스 지원, Array.from(): 이터러블을 배열로 변환
const arr = [10, 20, 30, 40, 50];
arr.at(-1); // 50 — last element
arr.at(-2); // 40
// Array.from with mapping
Array.from({ length: 5 }, (_, i) => i + 1);
// [1, 2, 3, 4, 5]
// Convert NodeList to Array
const divs = Array.from(document.querySelectorAll('div'));
// Array.from a Set
Array.from(new Set([1, 1, 2, 3])); // [1, 2, 3]toSorted / toReversed / with
ES2023 불변 배열 메서드 — 원본 배열 유지
const nums = [3, 1, 4, 1, 5]; // Non-mutating sort const sorted = nums.toSorted((a, b) => a - b); // [1, 1, 3, 4, 5] — nums unchanged // Non-mutating reverse const reversed = nums.toReversed(); // [5, 1, 4, 1, 3] // Replace at index (immutable) const updated = nums.with(0, 99); // [99, 1, 4, 1, 5]
객체
6 itemsObject Destructuring
객체에서 필요한 속성만 추출, 이름 변경, 기본값 설정
const response = { data: [1, 2], status: 200, error: null };
const { data, status } = response;
// Rename + default
const { data: items, count = 0 } = response;
// Rest pattern
const { error, ...rest } = response;
// rest = { data: [1, 2], status: 200 }Object.keys / values / entries
객체의 키, 값, [키, 값] 쌍을 배열로 변환
const config = { host: 'localhost', port: 3000, debug: true };
Object.keys(config); // ['host', 'port', 'debug']
Object.values(config); // ['localhost', 3000, true]
Object.entries(config); // [['host','localhost'], ...]
// Iterate with entries
for (const [key, value] of Object.entries(config)) {
console.log(`${key}: ${value}`);
}
// entries → object
const obj = Object.fromEntries([['a', 1], ['b', 2]]);Spread {...obj}
객체 복사, 병합, 속성 덮어쓰기
const defaults = { theme: 'dark', lang: 'en', debug: false };
const userPrefs = { lang: 'ko', debug: true };
// Merge (later wins)
const config = { ...defaults, ...userPrefs };
// { theme: 'dark', lang: 'ko', debug: true }
// Immutable update
const updated = { ...user, name: 'New Name' };
// Remove a property
const { password, ...safeUser } = user;Computed Property Names
대괄호로 동적 키를 가진 객체 생성
const field = 'email';
const obj = { [field]: 'test@example.com' };
// { email: 'test@example.com' }
// Dynamic state update (React pattern)
const handleChange = (e) => {
setState(prev => ({
...prev,
[e.target.name]: e.target.value,
}));
};structuredClone()
깊은 복사 — 중첩 객체, Map, Set, Date 모두 지원
const original = {
name: 'Alice',
scores: [90, 85, 92],
meta: { created: new Date() },
};
const clone = structuredClone(original);
clone.scores.push(100);
// original.scores is still [90, 85, 92]Object.freeze / Object.assign
freeze: 변경 불가 객체, assign: 속성 복사
// Freeze — prevents mutation (shallow)
const ROUTES = Object.freeze({
HOME: '/',
ABOUT: '/about',
API: '/api',
});
// ROUTES.HOME = '/new'; // silently fails (strict mode: TypeError)
// Object.assign — merge into target
const merged = Object.assign({}, defaults, overrides);
// Copy specific properties
const picked = Object.assign({}, { a: obj.a, b: obj.b });함수
6 itemsArrow Functions
간결한 함수 문법 — this를 렉시컬로 바인딩
// Expression body (implicit return)
const double = x => x * 2;
const add = (a, b) => a + b;
// Block body (explicit return)
const greet = name => {
const msg = `Hello, ${name}`;
return msg;
};
// Return object literal (wrap in parens)
const makeUser = (name, age) => ({ name, age });
// In array methods
const names = users.map(u => u.name);Default & Rest Parameters
매개변수 기본값 및 나머지 매개변수
// Default parameters
function createUser(name, role = 'viewer', active = true) {
return { name, role, active };
}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3, 4); // 10
// Combined
function log(level = 'info', ...messages) {
console[level](...messages);
}Closures
함수가 선언된 스코프의 변수를 기억하는 패턴
// Counter with private state
function createCounter(initial = 0) {
let count = initial;
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count,
};
}
const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.getCount(); // 12Currying
다인자 함수를 단일 인자 함수 체인으로 변환
// Basic curry
const multiply = a => b => a * b;
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15
// Practical: event handler factory
const handleChange = field => event => {
setForm(prev => ({ ...prev, [field]: event.target.value }));
};
// <input onChange={handleChange('email')} />Debounce
마지막 호출 후 지정 시간이 지나야 실행 (검색 입력 등)
function debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// Usage
const search = debounce(query => {
fetch(`/api/search?q=${query}`);
}, 500);
input.addEventListener('input', e => search(e.target.value));Throttle
지정 시간 간격마다 최대 1회 실행 (스크롤 등)
function throttle(fn, limit = 300) {
let inThrottle = false;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
// Usage
window.addEventListener('scroll',
throttle(() => console.log(window.scrollY), 200)
);비동기
6 itemsasync / await
프로미스를 동기 코드처럼 작성하는 문법
async function fetchUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = await res.json();
return user;
} catch (err) {
console.error('Failed to fetch user:', err);
return null;
}
}
// Arrow async
const getUsers = async () => {
const res = await fetch('/api/users');
return res.json();
};Promise.all
여러 프로미스를 동시에 실행, 하나라도 실패하면 전체 실패
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json()),
]);
// With error handling
try {
const results = await Promise.all(urls.map(u => fetch(u)));
} catch (err) {
console.error('One request failed:', err);
}Promise.allSettled
모든 프로미스 완료 대기 — 일부 실패해도 계속 진행
const results = await Promise.allSettled([
fetch('/api/a'),
fetch('/api/b'),
fetch('/api/c'), // may fail
]);
const successful = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
const failed = results
.filter(r => r.status === 'rejected')
.map(r => r.reason);AbortController
진행 중인 fetch 요청을 취소 (타임아웃, 컴포넌트 언마운트)
// Timeout pattern
async function fetchWithTimeout(url, ms = 5000) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), ms);
try {
const res = await fetch(url, { signal: controller.signal });
return await res.json();
} finally {
clearTimeout(timeout);
}
}
// React cleanup
useEffect(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(r => r.json())
.then(setData);
return () => controller.abort();
}, []);Promise.race / Promise.any
race: 첫 번째 완료된 것 반환, any: 첫 번째 성공한 것 반환
// Race — first to settle wins (resolve or reject)
const result = await Promise.race([
fetch('/api/primary'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), 3000)
),
]);
// Any — first to resolve wins (ignores rejections)
const fastest = await Promise.any([
fetch('https://cdn1.example.com/data'),
fetch('https://cdn2.example.com/data'),
fetch('https://cdn3.example.com/data'),
]);for await...of
비동기 이터러블을 순차적으로 처리
// Stream processing
async function* fetchPages(url) {
let page = 1;
while (true) {
const res = await fetch(`${url}?page=${page}`);
const data = await res.json();
if (data.length === 0) break;
yield data;
page++;
}
}
for await (const page of fetchPages('/api/items')) {
console.log(`Got ${page.length} items`);
}DOM
6 itemsquerySelector / querySelectorAll
CSS 선택자로 DOM 요소 검색
// Single element
const btn = document.querySelector('#submit-btn');
const first = document.querySelector('.card');
// All matching elements (NodeList)
const cards = document.querySelectorAll('.card');
cards.forEach(card => card.classList.add('active'));
// Scoped query
const form = document.querySelector('form');
const inputs = form.querySelectorAll('input[required]');addEventListener
이벤트 리스너 등록 및 옵션 활용
// Basic
btn.addEventListener('click', () => { /* ... */ });
// With options
el.addEventListener('scroll', handler, { passive: true });
el.addEventListener('click', handler, { once: true });
// Remove listener
const handler = () => console.log('clicked');
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler);Event Delegation
부모 요소에서 자식 이벤트를 처리하는 패턴
// Instead of adding listeners to each button...
document.querySelector('.toolbar').addEventListener('click', e => {
const btn = e.target.closest('button[data-action]');
if (!btn) return;
switch (btn.dataset.action) {
case 'save': handleSave(); break;
case 'delete': handleDelete(); break;
case 'share': handleShare(); break;
}
});classList
CSS 클래스 추가, 제거, 토글, 확인
el.classList.add('active', 'highlight');
el.classList.remove('hidden');
el.classList.toggle('dark-mode');
el.classList.contains('active'); // true/false
// Replace one class with another
el.classList.replace('old-class', 'new-class');
// Conditional toggle
el.classList.toggle('visible', isLoggedIn);dataset (data-* attributes)
HTML data-* 속성 읽기/쓰기
// HTML: <div data-user-id="42" data-role="admin">
const el = document.querySelector('[data-user-id]');
el.dataset.userId; // '42' (camelCase)
el.dataset.role; // 'admin'
// Set data attribute
el.dataset.active = 'true';
// Result: <div ... data-active="true">
// Remove
delete el.dataset.active;createElement & Fragment
DOM 요소 프로그래밍 방식으로 생성
// Create single element
const card = document.createElement('div');
card.className = 'card';
card.textContent = 'Hello';
container.appendChild(card);
// DocumentFragment for batch inserts (better perf)
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item.name;
fragment.appendChild(li);
});
list.appendChild(fragment); // single DOM updateES6+ 기능
6 itemsSet & Map
Set: 고유 값 컬렉션, Map: 모든 타입의 키-값 저장소
// Set — unique values
const unique = new Set([1, 2, 2, 3, 3]);
unique.add(4);
unique.has(2); // true
unique.delete(1);
unique.size; // 3
// Map — any key type
const cache = new Map();
cache.set(userObj, { lastSeen: Date.now() });
cache.get(userObj);
cache.has(userObj); // true
// Convert
[...unique]; // Set → Array
Object.fromEntries(map); // Map → ObjectWeakMap & WeakRef
가비지 컬렉션을 방해하지 않는 참조
// WeakMap — keys are weakly held (GC-friendly)
const metadata = new WeakMap();
function track(element) {
metadata.set(element, { clicks: 0 });
}
// When element is removed from DOM, entry is auto-cleaned
// WeakRef — weak reference to an object
let target = { data: 'important' };
const ref = new WeakRef(target);
// Later: may return undefined if GC'd
const obj = ref.deref();
if (obj) console.log(obj.data);Symbol
고유하고 변경 불가능한 값 — 메타 속성, 프로토콜 정의에 사용
// Unique property keys
const ID = Symbol('id');
const user = { [ID]: 42, name: 'Alice' };
user[ID]; // 42
// Well-known symbols
class Range {
constructor(start, end) { this.start = start; this.end = end; }
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) yield i;
}
}
[...new Range(1, 5)]; // [1, 2, 3, 4, 5]Proxy
객체 동작을 가로채서 커스터마이징하는 메타프로그래밍 도구
// Validation proxy
const validator = new Proxy({}, {
set(target, prop, value) {
if (prop === 'age' && (typeof value !== 'number' || value < 0)) {
throw new TypeError('Age must be a positive number');
}
target[prop] = value;
return true;
},
get(target, prop) {
return prop in target ? target[prop] : `No such property: ${prop}`;
},
});
validator.age = 25; // OK
// validator.age = -5; // TypeErrorGenerators
실행을 일시 중지하고 값을 순차적으로 생성하는 함수
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
fib.next().value; // 0
fib.next().value; // 1
fib.next().value; // 1
fib.next().value; // 2
// Take first N
function* take(iter, n) {
let i = 0;
for (const val of iter) {
if (i++ >= n) break;
yield val;
}
}
[...take(fibonacci(), 8)]; // [0, 1, 1, 2, 3, 5, 8, 13]Intl API
내장 국제화 — 날짜, 숫자, 통화 포맷팅
// Number formatting
new Intl.NumberFormat('en-US', {
style: 'currency', currency: 'USD',
}).format(1234.5); // '$1,234.50'
// Date formatting
new Intl.DateTimeFormat('ko-KR', {
year: 'numeric', month: 'long', day: 'numeric',
}).format(new Date()); // '2026년 4월 13일'
// Relative time
new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
.format(-1, 'day'); // 'yesterday'유틸리티 패턴
6 itemsDeep Clone
객체의 완전한 깊은 복사
// Modern (recommended)
const clone = structuredClone(original);
// JSON method (no functions, Date becomes string)
const clone2 = JSON.parse(JSON.stringify(original));
// Recursive (handles most cases)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof Map) return new Map([...obj].map(([k,v]) => [k, deepClone(v)]));
const copy = Array.isArray(obj) ? [] : {};
for (const key of Object.keys(obj)) copy[key] = deepClone(obj[key]);
return copy;
}Memoize
함수 호출 결과를 캐싱하여 반복 계산 방지
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
// Usage
const expensiveCalc = memoize((n) => {
console.log('computing...');
return n * n;
});
expensiveCalc(5); // computing... 25
expensiveCalc(5); // 25 (cached, no log)Pipe / Compose
함수를 체인으로 연결하여 데이터를 변환하는 패턴
// Pipe: left to right
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
// Compose: right to left
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const processUser = pipe(
name => name.trim(),
name => name.toLowerCase(),
name => name.replace(/\s+/g, '-'),
name => `@${name}`,
);
processUser(' John Doe '); // '@john-doe'Event Emitter
발행-구독(pub/sub) 이벤트 패턴
class EventEmitter {
#listeners = new Map();
on(event, handler) {
if (!this.#listeners.has(event)) this.#listeners.set(event, new Set());
this.#listeners.get(event).add(handler);
return () => this.off(event, handler); // unsubscribe fn
}
off(event, handler) {
this.#listeners.get(event)?.delete(handler);
}
emit(event, ...args) {
this.#listeners.get(event)?.forEach(h => h(...args));
}
}
const bus = new EventEmitter();
const unsub = bus.on('save', data => console.log(data));
bus.emit('save', { id: 1 }); // logs { id: 1 }
unsub(); // unsubscribeUUID Generation
고유 식별자 생성 (crypto API 기반)
// Modern — built-in (Chrome 92+, Node 19+)
const id = crypto.randomUUID();
// 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
// Fallback — manual v4 UUID
function uuidv4() {
return '10000000-1000-4000-8000-100000000000'
.replace(/[018]/g, c =>
(+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4)
.toString(16));
}
// NanoID-style short ID
const nanoid = (len = 21) =>
crypto.getRandomValues(new Uint8Array(len))
.reduce((s, b) => s + (b & 63).toString(36), '').slice(0, len);Sleep / Delay
지정 시간 동안 실행을 일시 중지하는 유틸리티
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
// Usage with async/await
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url);
} catch (err) {
if (i === retries - 1) throw err;
await sleep(1000 * 2 ** i); // exponential backoff
}
}
}JavaScript 치트시트 사용 가이드
ES6+ 이후 JavaScript는 매년 새로운 기능이 추가되고 있습니다. 이 치트시트는 실무에서 가장 자주 사용하는 최신 문법과 패턴을 한 곳에 정리하여 빠르게 참고할 수 있도록 만들었습니다.
왜 최신 JavaScript 문법을 배워야 하나요?
ES6(2015) 이후 도입된 화살표 함수, 구조 분해, 옵셔널 체이닝, nullish coalescing 등의 문법은 코드를 더 간결하고 안전하게 만들어 줍니다. React, Vue, Next.js 등 모든 현대 프레임워크가 이러한 문법을 기반으로 설계되어 있어, 최신 JavaScript를 익히는 것은 프론트엔드 개발의 필수 요건입니다.
비동기 프로그래밍 마스터하기
JavaScript의 비동기 모델은 콜백에서 Promise, 그리고 async/await로 발전해 왔습니다. Promise.all, Promise.allSettled, AbortController 등을 활용하면 복잡한 비동기 흐름도 깔끔하게 관리할 수 있습니다. 이 치트시트의 비동기 섹션에서 실전 패턴을 확인하세요.
실전에서 바로 쓰는 유틸리티
debounce, throttle, memoize, deep clone 등의 유틸리티 패턴은 프로젝트 규모에 관계없이 항상 필요합니다. lodash 같은 라이브러리 없이도 순수 JavaScript로 구현할 수 있으며, 이 치트시트에서 복사하여 바로 사용할 수 있습니다.