← 가이드 목록으로 ← Back to guides

JavaScript ES6+ 고급 패턴 완벽 가이드 JavaScript ES6+ Advanced Patterns Complete Guide

ES6+ JavaScript는 강력한 메타프로그래밍 기능을 제공합니다. 모듈 시스템, 클로저, Proxy, 제너레이터로 프로페셔널한 코드를 작성하세요.

ES6+ JavaScript provides powerful metaprogramming capabilities. Write professional code with modules, closures, Proxy, and generators.

ES6 모듈 시스템 ES6 Module System

importexport로 코드를 모듈화하여 재사용성과 유지보수성을 높입니다.

Modularize code with import and export for better reusability and maintainability.

// math.js - Named exports
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) { /* ... */ }
}
// math.js - Named exports
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) { /* ... */ }
}
// app.js - 다양한 import 방법

// 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');
// app.js - Various import methods

// 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
// Private variable pattern
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
// 커링 (Currying)
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
// Currying
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.

// 기본 Proxy 사용법
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 → "홍길동"
// Basic Proxy usage
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"
// 유효성 검사 Proxy
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
// Validation Proxy
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 }
// Basic generator
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
// Infinite sequence
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 }
// Creating unique keys
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