ES6+ JavaScript는 강력한 메타프로그래밍 기능을 제공합니다. 모듈 시스템, 클로저, Proxy, 제너레이터로 프로페셔널한 코드를 작성하세요.
ES6+ JavaScript provides powerful metaprogramming capabilities. Write professional code with modules, closures, Proxy, and generators.
ES6 모듈 시스템 ES6 Module System
import와 export로 코드를 모듈화하여 재사용성과 유지보수성을 높입니다.
Modularize code with import and export for better reusability and
maintainability.
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// Default export
export default class Calculator {
calculate(expr) { /* ... */ }
}
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// Default export
export default class Calculator {
calculate(expr) { /* ... */ }
}
// Named import
import { add, multiply } from './math.js';
// 별칭 사용
import { PI as pi } from './math.js';
// Default import
import Calculator from './math.js';
// 전체 import
import * as math from './math.js';
console.log(math.PI, math.add(1, 2));
// Dynamic import (코드 스플리팅)
const module = await import('./math.js');
// Named import
import { add, multiply } from './math.js';
// Using alias
import { PI as pi } from './math.js';
// Default import
import Calculator from './math.js';
// Import all
import * as math from './math.js';
console.log(math.PI, math.add(1, 2));
// Dynamic import (code splitting)
const module = await import('./math.js');
클로저 (Closure) Closures
클로저는 함수가 자신이 생성될 때의 환경(스코프)를 기억하는 기능입니다. 프라이빗 변수, 팩토리 패턴, 커링 등에 활용됩니다.
Closures allow functions to remember the environment (scope) in which they were created. Used for private variables, factory patterns, currying, etc.
function createCounter() {
let count = 0; // 프라이빗 변수
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; }
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
console.log(counter.getCount()); // 2
function createCounter() {
let count = 0; // private variable
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; }
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
console.log(counter.getCount()); // 2
const multiply = (a) => (b) => a * b;
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// 함수 합성
const compose = (...fns) => (x) =>
fns.reduceRight((acc, fn) => fn(acc), x);
const addOne = x => x + 1;
const square = x => x * x;
const addOneThenSquare = compose(square, addOne);
console.log(addOneThenSquare(4)); // 25
const multiply = (a) => (b) => a * b;
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Function composition
const compose = (...fns) => (x) =>
fns.reduceRight((acc, fn) => fn(acc), x);
const addOne = x => x + 1;
const square = x => x * x;
const addOneThenSquare = compose(square, addOne);
console.log(addOneThenSquare(4)); // 25
Proxy: 객체 동작 가로채기 Proxy: Intercepting Object Operations
Proxy는 객체의 기본 동작(get, set, delete 등)을 가로채고 재정의할 수 있습니다. 유효성 검사, 로깅, 반응형 시스템 구현에 활용됩니다.
Proxy can intercept and redefine fundamental object operations (get, set, delete, etc.). Used for validation, logging, reactive systems implementation.
const handler = {
get(target, prop) {
console.log(`읽기: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`쓰기: ${prop} = ${value}`);
target[prop] = value;
return true;
}
};
const user = new Proxy({}, handler);
user.name = "홍길동"; // 쓰기: name = 홍길동
console.log(user.name); // 읽기: name → "홍길동"
const handler = {
get(target, prop) {
console.log(`Reading: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Writing: ${prop} = ${value}`);
target[prop] = value;
return true;
}
};
const user = new Proxy({}, handler);
user.name = "John"; // Writing: name = John
console.log(user.name); // Reading: name → "John"
const createValidator = (schema) => {
return new Proxy({}, {
set(target, prop, value) {
if (schema[prop]) {
const isValid = schema[prop](value);
if (!isValid) {
throw new Error(`Invalid ${prop}`);
}
}
target[prop] = value;
return true;
}
});
};
const user = createValidator({
age: (v) => typeof v === 'number' && v >= 0,
email: (v) => v.includes('@')
});
user.age = 25; // OK
user.age = -5; // Error: Invalid age
const createValidator = (schema) => {
return new Proxy({}, {
set(target, prop, value) {
if (schema[prop]) {
const isValid = schema[prop](value);
if (!isValid) {
throw new Error(`Invalid ${prop}`);
}
}
target[prop] = value;
return true;
}
});
};
const user = createValidator({
age: (v) => typeof v === 'number' && v >= 0,
email: (v) => v.includes('@')
});
user.age = 25; // OK
user.age = -5; // Error: Invalid age
제너레이터 (Generator) Generators
제너레이터는 실행을 중간에 멈추고 재개할 수 있는 특수한 함수입니다. 이터레이터 생성, 비동기 흐름 제어, 무한 시퀀스에 활용됩니다.
Generators are special functions that can pause and resume execution. Used for iterator creation, async flow control, infinite sequences.
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
const seq = infiniteSequence();
console.log(seq.next().value); // 0
console.log(seq.next().value); // 1
console.log(seq.next().value); // 2
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
const seq = infiniteSequence();
console.log(seq.next().value); // 0
console.log(seq.next().value); // 1
console.log(seq.next().value); // 2
Symbol: 유일한 식별자 Symbol: Unique Identifiers
const id = Symbol('id');
const id2 = Symbol('id');
console.log(id === id2); // false
// 객체에서 Symbol 키 사용
const user = {
name: "홍길동",
[id]: 12345 // Symbol을 키로 사용
};
console.log(user[id]); // 12345
console.log(Object.keys(user)); // ['name'] - Symbol 키는 열거되지 않음
// Well-known Symbols
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
const id = Symbol('id');
const id2 = Symbol('id');
console.log(id === id2); // false
// Using Symbol keys in objects
const user = {
name: "John",
[id]: 12345 // Using Symbol as key
};
console.log(user[id]); // 12345
console.log(Object.keys(user)); // ['name'] - Symbol keys not enumerated
// Well-known Symbols
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
✨ ES6+ 패턴 활용 사례 ✨ ES6+ Pattern Use Cases
- 모듈: 코드 조직화, 트리 셰이킹
- 클로저: 상태 캡슐화, 팩토리 패턴
- Proxy: 반응형 시스템 (Vue.js), 유효성 검사
- 제너레이터: Redux-Saga, 이터레이터
- Symbol: 프라이빗 속성, 커스텀 이터레이터
- Modules: Code organization, tree shaking
- Closures: State encapsulation, factory patterns
- Proxy: Reactive systems (Vue.js), validation
- Generators: Redux-Saga, iterators
- Symbol: Private properties, custom iterators
💡 프로페셔널 팁 💡 Professional Tips
- Named export를 기본으로 사용 (트리 셰이킹 최적화)
- 클로저로 인한 메모리 누수 주의 (불필요한 참조 해제)
- Proxy는 오버헤드가 있으므로 성능 중요 시 주의
- 제너레이터는 async/await와 함께 사용 가능
- Prefer named exports (tree shaking optimization)
- Watch for closure memory leaks (release unnecessary references)
- Proxy has overhead, use carefully in performance-critical code
- Generators can be used with async/await
