JavaScript 고급 튜토리얼 전문가를 위한 심화 과정

JavaScript 고급 튜토리얼: 전문가를 위한 심화 과정

JavaScript 고급 튜토리얼: 전문가를 위한 심화 과정

JavaScript 고급 과정 소개

JavaScript 고급 과정에서는 언어의 내부 동작 원리를 깊이 이해하고, 성능 최적화, 디자인 패턴, 그리고 최신 JavaScript 기능들을 마스터합니다. 이 튜토리얼은 이미 JavaScript의 기본을 숙지한 개발자들이 전문가 수준으로 도약하기 위한 심화 내용을 다룹니다.

📚 사전 요구사항: 이 튜토리얼을 따라오기 위해서는 JavaScript의 기본 문법, ES6+ 기능, Promise, async/await 등에 대한 이해가 필요합니다.

1. 클로저(Closures)와 스코프 체인

클로저는 JavaScript의 가장 강력한 기능 중 하나로, 함수가 자신이 생성될 때의 렌더링 환경을 기억하는 것을 의미합니다. 이를 통해 private 변수를 구현하고 고차 함수를 작성할 수 있습니다.

클로저의 심화 이해

// 클로저를 활용한 모듈 패턴
const createCounter = () => {
    // private 변수
    let count = 0;
    const changeHistory = [];
    
    // private 메서드
    const logChange = (action, value) => {
        changeHistory.push({
            action,
            value,
            timestamp: new Date().toISOString()
        });
    };
    
    // public API
    return {
        increment() {
            count++;
            logChange('increment', count);
            return count;
        },
        
        decrement() {
            count--;
            logChange('decrement', count);
            return count;
        },
        
        getCount() {
            return count;
        },
        
        getHistory() {
            return [...changeHistory]; // 배열 복사본 반환
        },
        
        reset() {
            const oldCount = count;
            count = 0;
            logChange('reset', count);
            return oldCount;
        }
    };
};

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getHistory());

// 고급 클로저 패턴: 커링(Currying)
const curry = (fn) => {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function(...nextArgs) {
                return curried.apply(this, args.concat(nextArgs));
            };
        }
    };
};

// 커링 활용 예제
const multiply = (a, b, c) => a * b * c;
const curriedMultiply = curry(multiply);

console.log(curriedMultiply(2)(3)(4)); // 24
console.log(curriedMultiply(2, 3)(4)); // 24
console.log(curriedMultiply(2)(3, 4)); // 24

스코프 체인과 실행 컨텍스트

// 실행 컨텍스트와 스코프 체인 이해
const globalVar = 'global';

function outerFunction(outerParam) {
    const outerVar = 'outer';
    
    function middleFunction(middleParam) {
        const middleVar = 'middle';
        
        function innerFunction(innerParam) {
            const innerVar = 'inner';
            
            // 스코프 체인: inner -> middle -> outer -> global
            console.log({
                innerVar,    // 현재 스코프
                innerParam,  // 현재 스코프
                middleVar,   // 상위 스코프
                middleParam, // 상위 스코프
                outerVar,    // 상위의 상위 스코프
                outerParam,  // 상위의 상위 스코프
                globalVar    // 전역 스코프
            });
        }
        
        return innerFunction;
    }
    
    return middleFunction;
}

const middle = outerFunction('outerParam');
const inner = middle('middleParam');
inner('innerParam');

// 클로저 메모리 관리 패턴
class EventManager {
    constructor() {
        this.listeners = new Map();
        this.onceListeners = new WeakMap();
    }
    
    on(event, handler, context = null) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        
        // 클로저를 활용한 컨텍스트 바인딩
        const boundHandler = context 
            ? (...args) => handler.apply(context, args)
            : handler;
            
        this.listeners.get(event).push(boundHandler);
        
        // 구독 해제 함수 반환 (클로저)
        return () => {
            const handlers = this.listeners.get(event);
            const index = handlers.indexOf(boundHandler);
            if (index > -1) {
                handlers.splice(index, 1);
            }
        };
    }
    
    emit(event, ...args) {
        const handlers = this.listeners.get(event);
        if (handlers) {
            handlers.forEach(handler => handler(...args));
        }
    }
}

2. 프로토타입과 상속

JavaScript의 프로토타입 기반 상속은 클래스 기반 언어와는 다른 독특한 메커니즘입니다. ES6 클래스는 사실 프로토타입의 문법적 설탕(syntactic sugar)입니다.

// 프로토타입 체인 심화 이해
function Vehicle(type) {
    this.type = type;
    this.fuel = 100;
}

Vehicle.prototype.refuel = function(amount) {
    this.fuel += amount;
    console.log(`Refueled ${amount}L. Current fuel: ${this.fuel}L`);
};

// 프로토타입 상속 구현
function Car(brand, model) {
    Vehicle.call(this, 'car'); // super() 역할
    this.brand = brand;
    this.model = model;
    this.speed = 0;
}

// 프로토타입 체인 설정
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;

// 메서드 오버라이딩
Car.prototype.refuel = function(amount) {
    // 부모 메서드 호출
    Vehicle.prototype.refuel.call(this, amount);
    console.log(`${this.brand} ${this.model} is ready to go!`);
};

Car.prototype.accelerate = function(increment) {
    this.speed += increment;
    this.fuel -= increment * 0.1;
    console.log(`Speed: ${this.speed}km/h, Fuel: ${this.fuel}L`);
};

// ES6 클래스와 프로토타입 비교
class ModernVehicle {
    constructor(type) {
        this.type = type;
        this.fuel = 100;
    }
    
    refuel(amount) {
        this.fuel += amount;
        console.log(`Refueled ${amount}L. Current fuel: ${this.fuel}L`);
    }
}

class ModernCar extends ModernVehicle {
    constructor(brand, model) {
        super('car');
        this.brand = brand;
        this.model = model;
        this.speed = 0;
    }
    
    refuel(amount) {
        super.refuel(amount);
        console.log(`${this.brand} ${this.model} is ready to go!`);
    }
    
    accelerate(increment) {
        this.speed += increment;
        this.fuel -= increment * 0.1;
        console.log(`Speed: ${this.speed}km/h, Fuel: ${this.fuel}L`);
    }
}

// 프로토타입 메서드 동적 추가
ModernCar.prototype.honk = function() {
    console.log('Beep beep!');
};

// 믹스인 패턴
const Drivable = {
    drive() {
        console.log(`${this.brand} is driving at ${this.speed}km/h`);
    },
    
    stop() {
        this.speed = 0;
        console.log(`${this.brand} has stopped`);
    }
};

const Flyable = {
    fly() {
        console.log(`${this.brand} is flying!`);
    },
    
    land() {
        console.log(`${this.brand} has landed`);
    }
};

// 다중 믹스인 적용
function applyMixins(targetClass, ...mixins) {
    mixins.forEach(mixin => {
        Object.getOwnPropertyNames(mixin).forEach(name => {
            const descriptor = Object.getOwnPropertyDescriptor(mixin, name);
            Object.defineProperty(targetClass.prototype, name, descriptor);
        });
    });
}

class FlyingCar extends ModernCar {
    constructor(brand, model) {
        super(brand, model);
        this.altitude = 0;
    }
}

applyMixins(FlyingCar, Drivable, Flyable);

3. 고급 비동기 프로그래밍

JavaScript의 비동기 프로그래밍은 단순한 Promise와 async/await를 넘어, 복잡한 비동기 흐름 제어와 동시성 관리를 포함합니다.

// 고급 Promise 패턴
class PromisePool {
    constructor(concurrency) {
        this.concurrency = concurrency;
        this.current = 0;
        this.queue = [];
    }
    
    async exec(fn) {
        if (this.current >= this.concurrency) {
            await new Promise(resolve => this.queue.push(resolve));
        }
        
        this.current++;
        
        try {
            const result = await fn();
            return result;
        } finally {
            this.current--;
            const resolve = this.queue.shift();
            if (resolve) resolve();
        }
    }
    
    async map(items, fn) {
        return Promise.all(
            items.map(item => this.exec(() => fn(item)))
        );
    }
}

// 사용 예제
const pool = new PromisePool(3); // 동시에 3개까지만 실행

async function fetchWithLimit(urls) {
    return pool.map(urls, async (url) => {
        const response = await fetch(url);
        return response.json();
    });
}

// 재시도 로직이 있는 Promise
async function retryPromise(fn, retries = 3, delay = 1000) {
    try {
        return await fn();
    } catch (error) {
        if (retries === 0) {
            throw error;
        }
        
        console.log(`Retrying... (${retries} attempts left)`);
        await new Promise(resolve => setTimeout(resolve, delay));
        
        return retryPromise(fn, retries - 1, delay * 2);
    }
}

// Promise 레이스 컨디션 처리
class AsyncQueue {
    constructor() {
        this.queue = [];
        this.resolvers = [];
    }
    
    enqueue(item) {
        if (this.resolvers.length > 0) {
            const resolve = this.resolvers.shift();
            resolve(item);
        } else {
            this.queue.push(item);
        }
    }
    
    dequeue() {
        return new Promise(resolve => {
            if (this.queue.length > 0) {
                resolve(this.queue.shift());
            } else {
                this.resolvers.push(resolve);
            }
        });
    }
}

// 비동기 이터레이터
class AsyncDataStream {
    constructor(dataSource) {
        this.dataSource = dataSource;
    }
    
    async *[Symbol.asyncIterator]() {
        let hasMore = true;
        let page = 1;
        
        while (hasMore) {
            const data = await this.dataSource.fetchPage(page);
            
            if (data.length === 0) {
                hasMore = false;
            } else {
                for (const item of data) {
                    yield item;
                }
                page++;
            }
        }
    }
}

// 사용 예제
async function processStream() {
    const stream = new AsyncDataStream(dataSource);
    
    for await (const item of stream) {
        console.log('Processing:', item);
        // 각 아이템 처리
    }
}

4. 제너레이터와 이터레이터

제너레이터는 함수 실행을 일시 중지하고 재개할 수 있는 특별한 함수로, 이터레이터 프로토콜을 구현하는 강력한 도구입니다.

// 제너레이터 기본과 고급 패턴
function* fibonacci(limit = Infinity) {
    let [prev, curr] = [0, 1];
    
    while (curr <= limit) {
        yield curr;
        [prev, curr] = [curr, prev + curr];
    }
}

// 제너레이터 컴포지션
function* take(n, iterable) {
    let count = 0;
    for (const item of iterable) {
        if (count++ >= n) break;
        yield item;
    }
}

function* filter(predicate, iterable) {
    for (const item of iterable) {
        if (predicate(item)) {
            yield item;
        }
    }
}

function* map(fn, iterable) {
    for (const item of iterable) {
        yield fn(item);
    }
}

// 파이프라인 구성
const pipeline = 
    take(10,
        filter(n => n % 2 === 0,
            map(n => n * 2,
                fibonacci(1000))));

console.log([...pipeline]); // [4, 16, 68, 288, 1220]

// 양방향 통신이 가능한 제너레이터
function* calculator() {
    let result = 0;
    
    while (true) {
        const input = yield result;
        
        if (!input) break;
        
        const [operation, value] = input.split(' ');
        
        switch (operation) {
            case 'add':
                result += Number(value);
                break;
            case 'subtract':
                result -= Number(value);
                break;
            case 'multiply':
                result *= Number(value);
                break;
            case 'divide':
                result /= Number(value);
                break;
        }
    }
}

const calc = calculator();
console.log(calc.next());           // { value: 0, done: false }
console.log(calc.next('add 10'));    // { value: 10, done: false }
console.log(calc.next('multiply 3')); // { value: 30, done: false }
console.log(calc.next('subtract 5')); // { value: 25, done: false }

// 커스텀 이터러블 클래스
class Range {
    constructor(start, end, step = 1) {
        this.start = start;
        this.end = end;
        this.step = step;
    }
    
    *[Symbol.iterator]() {
        for (let i = this.start; i <= this.end; i += this.step) {
            yield i;
        }
    }
    
    reverse() {
        const self = this;
        return {
            *[Symbol.iterator]() {
                for (let i = self.end; i >= self.start; i -= self.step) {
                    yield i;
                }
            }
        };
    }
}

const range = new Range(1, 10, 2);
console.log([...range]);         // [1, 3, 5, 7, 9]
console.log([...range.reverse()]); // [9, 7, 5, 3, 1]

5. Proxy와 Reflect API

Proxy는 객체의 기본 동작을 가로채고 재정의할 수 있게 해주는 강력한 메타프로그래밍 도구입니다.

// 반응형 시스템 구현
class ReactiveSystem {
    constructor() {
        this.dependencies = new Map();
        this.currentEffect = null;
    }
    
    reactive(target) {
        const self = this;
        
        return new Proxy(target, {
            get(obj, prop) {
                if (self.currentEffect) {
                    self.track(obj, prop);
                }
                return Reflect.get(obj, prop);
            },
            
            set(obj, prop, value) {
                const oldValue = obj[prop];
                const result = Reflect.set(obj, prop, value);
                
                if (oldValue !== value) {
                    self.trigger(obj, prop);
                }
                
                return result;
            },
            
            deleteProperty(obj, prop) {
                const result = Reflect.deleteProperty(obj, prop);
                self.trigger(obj, prop);
                return result;
            }
        });
    }
    
    track(obj, prop) {
        const key = `${obj.constructor.name}.${String(prop)}`;
        
        if (!this.dependencies.has(key)) {
            this.dependencies.set(key, new Set());
        }
        
        this.dependencies.get(key).add(this.currentEffect);
    }
    
    trigger(obj, prop) {
        const key = `${obj.constructor.name}.${String(prop)}`;
        const effects = this.dependencies.get(key);
        
        if (effects) {
            effects.forEach(effect => effect());
        }
    }
    
    effect(fn) {
        this.currentEffect = fn;
        fn();
        this.currentEffect = null;
    }
}

// 사용 예제
const system = new ReactiveSystem();
const state = system.reactive({
    count: 0,
    message: 'Hello'
});

system.effect(() => {
    console.log(`Count is: ${state.count}`);
});

system.effect(() => {
    console.log(`Message is: ${state.message}`);
});

state.count++; // "Count is: 1" 출력
state.message = 'World'; // "Message is: World" 출력

// 검증 프록시
function createValidatedObject(schema) {
    return new Proxy({}, {
        set(obj, prop, value) {
            const validator = schema[prop];
            
            if (!validator) {
                throw new Error(`Property "${prop}" is not defined in schema`);
            }
            
            if (!validator.validate(value)) {
                throw new TypeError(
                    `Invalid value for "${prop}": ${validator.message}`
                );
            }
            
            return Reflect.set(obj, prop, value);
        }
    });
}

const userSchema = {
    name: {
        validate: v => typeof v === 'string' && v.length > 0,
        message: 'Name must be a non-empty string'
    },
    age: {
        validate: v => typeof v === 'number' && v >= 0 && v <= 120,
        message: 'Age must be a number between 0 and 120'
    },
    email: {
        validate: v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
        message: 'Invalid email format'
    }
};

const user = createValidatedObject(userSchema);
user.name = 'John'; // OK
user.age = 30; // OK
// user.age = 150; // Error: Invalid value for "age"

6. 메모리 관리와 성능 최적화

JavaScript의 메모리 관리와 가비지 컬렉션을 이해하고, 메모리 누수를 방지하며 성능을 최적화하는 기법들을 살펴봅니다.

// WeakMap을 활용한 메모리 효율적인 캐싱
class MemoryEfficientCache {
    constructor() {
        this.cache = new WeakMap();
    }
    
    memoize(fn) {
        return (obj, ...args) => {
            if (!this.cache.has(obj)) {
                this.cache.set(obj, new Map());
            }
            
            const objCache = this.cache.get(obj);
            const key = JSON.stringify(args);
            
            if (!objCache.has(key)) {
                objCache.set(key, fn.call(obj, ...args));
            }
            
            return objCache.get(key);
        };
    }
}

// 객체 풀링 패턴
class ObjectPool {
    constructor(createFn, resetFn, maxSize = 10) {
        this.createFn = createFn;
        this.resetFn = resetFn;
        this.maxSize = maxSize;
        this.pool = [];
        this.activeObjects = new Set();
    }
    
    acquire() {
        let obj;
        
        if (this.pool.length > 0) {
            obj = this.pool.pop();
        } else {
            obj = this.createFn();
        }
        
        this.activeObjects.add(obj);
        return obj;
    }
    
    release(obj) {
        if (!this.activeObjects.has(obj)) {
            return;
        }
        
        this.resetFn(obj);
        this.activeObjects.delete(obj);
        
        if (this.pool.length < this.maxSize) {
            this.pool.push(obj);
        }
    }
    
    clear() {
        this.pool = [];
        this.activeObjects.clear();
    }
}

// 사용 예제: 파티클 시스템
const particlePool = new ObjectPool(
    () => ({ x: 0, y: 0, vx: 0, vy: 0, life: 1 }),
    (particle) => {
        particle.x = 0;
        particle.y = 0;
        particle.vx = 0;
        particle.vy = 0;
        particle.life = 1;
    },
    1000
);

// 지연 로딩과 코드 스플리팅
class LazyLoader {
    constructor() {
        this.modules = new Map();
    }
    
    register(name, loader) {
        this.modules.set(name, {
            loader,
            module: null,
            loading: false
        });
    }
    
    async load(name) {
        const entry = this.modules.get(name);
        
        if (!entry) {
            throw new Error(`Module "${name}" not found`);
        }
        
        if (entry.module) {
            return entry.module;
        }
        
        if (entry.loading) {
            // 이미 로딩 중이면 대기
            return new Promise(resolve => {
                const checkInterval = setInterval(() => {
                    if (entry.module) {
                        clearInterval(checkInterval);
                        resolve(entry.module);
                    }
                }, 10);
            });
        }
        
        entry.loading = true;
        entry.module = await entry.loader();
        entry.loading = false;
        
        return entry.module;
    }
}

// 성능 측정 유틸리티
class PerformanceMonitor {
    constructor() {
        this.marks = new Map();
        this.measures = new Map();
    }
    
    mark(name) {
        this.marks.set(name, performance.now());
    }
    
    measure(name, startMark, endMark) {
        const start = this.marks.get(startMark);
        const end = endMark ? this.marks.get(endMark) : performance.now();
        
        const duration = end - start;
        
        if (!this.measures.has(name)) {
            this.measures.set(name, []);
        }
        
        this.measures.get(name).push(duration);
        
        return duration;
    }
    
    getStats(name) {
        const measurements = this.measures.get(name) || [];
        
        if (measurements.length === 0) {
            return null;
        }
        
        const sorted = [...measurements].sort((a, b) => a - b);
        const sum = sorted.reduce((acc, val) => acc + val, 0);
        
        return {
            count: sorted.length,
            min: sorted[0],
            max: sorted[sorted.length - 1],
            mean: sum / sorted.length,
            median: sorted[Math.floor(sorted.length / 2)],
            p95: sorted[Math.floor(sorted.length * 0.95)],
            p99: sorted[Math.floor(sorted.length * 0.99)]
        };
    }
}

7. JavaScript 디자인 패턴

소프트웨어 설계에서 자주 발생하는 문제들에 대한 재사용 가능한 해결책인 디자인 패턴을 JavaScript로 구현합니다.

// 싱글톤 패턴 (ES6 모듈 방식)
class DatabaseConnection {
    constructor() {
        if (DatabaseConnection.instance) {
            return DatabaseConnection.instance;
        }
        
        this.connection = this.connect();
        DatabaseConnection.instance = this;
    }
    
    connect() {
        console.log('Creating new database connection');
        return { id: Math.random() };
    }
    
    query(sql) {
        console.log(`Executing: ${sql}`);
        return 'Result';
    }
}

// 옵저버 패턴 (이벤트 에미터)
class EventEmitter {
    constructor() {
        this.events = {};
    }
    
    on(event, listener) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener);
        
        // 이벤트 제거 함수 반환
        return () => this.off(event, listener);
    }
    
    off(event, listenerToRemove) {
        if (!this.events[event]) return;
        
        this.events[event] = this.events[event].filter(
            listener => listener !== listenerToRemove
        );
    }
    
    emit(event, ...args) {
        if (!this.events[event]) return;
        
        this.events[event].forEach(listener => {
            listener.apply(this, args);
        });
    }
    
    once(event, listener) {
        const onceWrapper = (...args) => {
            listener.apply(this, args);
            this.off(event, onceWrapper);
        };
        this.on(event, onceWrapper);
    }
}

// 팩토리 패턴
class VehicleFactory {
    static vehicleTypes = new Map();
    
    static register(type, vehicleClass) {
        this.vehicleTypes.set(type, vehicleClass);
    }
    
    static create(type, ...args) {
        const VehicleClass = this.vehicleTypes.get(type);
        
        if (!VehicleClass) {
            throw new Error(`Vehicle type "${type}" not registered`);
        }
        
        return new VehicleClass(...args);
    }
}

// 전략 패턴
class PaymentProcessor {
    constructor() {
        this.strategies = new Map();
    }
    
    registerStrategy(name, strategy) {
        this.strategies.set(name, strategy);
    }
    
    async process(method, amount, details) {
        const strategy = this.strategies.get(method);
        
        if (!strategy) {
            throw new Error(`Payment method "${method}" not supported`);
        }
        
        return strategy.process(amount, details);
    }
}

// 사용 예제
const processor = new PaymentProcessor();

processor.registerStrategy('credit-card', {
    async process(amount, { cardNumber, cvv }) {
        console.log(`Processing ${amount} via credit card ${cardNumber}`);
        // 실제 결제 로직
        return { success: true, transactionId: 'CC123' };
    }
});

processor.registerStrategy('paypal', {
    async process(amount, { email }) {
        console.log(`Processing ${amount} via PayPal for ${email}`);
        // PayPal 결제 로직
        return { success: true, transactionId: 'PP456' };
    }
});

// 데코레이터 패턴
function memoize(target, propertyKey, descriptor) {
    const originalMethod = descriptor.value;
    const cache = new Map();
    
    descriptor.value = function(...args) {
        const key = JSON.stringify(args);
        
        if (cache.has(key)) {
            console.log('Returning cached result');
            return cache.get(key);
        }
        
        const result = originalMethod.apply(this, args);
        cache.set(key, result);
        return result;
    };
    
    return descriptor;
}

function throttle(delay) {
    return function(target, propertyKey, descriptor) {
        const originalMethod = descriptor.value;
        let lastCall = 0;
        
        descriptor.value = function(...args) {
            const now = Date.now();
            
            if (now - lastCall < delay) {
                console.log('Throttled');
                return;
            }
            
            lastCall = now;
            return originalMethod.apply(this, args);
        };
        
        return descriptor;
    };
}

8. 함수형 프로그래밍 고급

함수형 프로그래밍 패러다임을 JavaScript에서 구현하고, 불변성, 순수 함수, 함수 합성 등의 개념을 활용합니다.

// 함수 합성과 파이프라인
const compose = (...fns) => x => 
    fns.reduceRight((acc, fn) => fn(acc), x);

const pipe = (...fns) => x => 
    fns.reduce((acc, fn) => fn(acc), x);

// 펑터(Functor) 구현
class Box {
    constructor(value) {
        this.value = value;
    }
    
    static of(value) {
        return new Box(value);
    }
    
    map(fn) {
        return Box.of(fn(this.value));
    }
    
    flatMap(fn) {
        return fn(this.value);
    }
    
    fold(fn) {
        return fn(this.value);
    }
}

// Maybe 모나드
class Maybe {
    constructor(value) {
        this.value = value;
    }
    
    static of(value) {
        return value === null || value === undefined
            ? new Nothing()
            : new Just(value);
    }
    
    isNothing() {
        return false;
    }
}

class Just extends Maybe {
    map(fn) {
        return Maybe.of(fn(this.value));
    }
    
    flatMap(fn) {
        return fn(this.value);
    }
    
    getOrElse() {
        return this.value;
    }
}

class Nothing extends Maybe {
    map() {
        return this;
    }
    
    flatMap() {
        return this;
    }
    
    getOrElse(defaultValue) {
        return defaultValue;
    }
    
    isNothing() {
        return true;
    }
}

// Either 모나드 (에러 처리)
class Either {
    static of(value) {
        return new Right(value);
    }
    
    static left(value) {
        return new Left(value);
    }
    
    static right(value) {
        return new Right(value);
    }
}

class Left extends Either {
    constructor(value) {
        super();
        this.value = value;
    }
    
    map() {
        return this;
    }
    
    flatMap() {
        return this;
    }
    
    fold(leftFn, rightFn) {
        return leftFn(this.value);
    }
}

class Right extends Either {
    constructor(value) {
        super();
        this.value = value;
    }
    
    map(fn) {
        return Either.of(fn(this.value));
    }
    
    flatMap(fn) {
        return fn(this.value);
    }
    
    fold(leftFn, rightFn) {
        return rightFn(this.value);
    }
}

// 실제 사용 예제
const safeDivide = (a, b) => {
    if (b === 0) {
        return Either.left('Division by zero');
    }
    return Either.right(a / b);
};

const result = safeDivide(10, 2)
    .map(x => x * 2)
    .flatMap(x => safeDivide(x, 5))
    .fold(
        error => console.error('Error:', error),
        value => console.log('Result:', value)
    );

// 렌즈(Lens) 구현
const lens = (getter, setter) => ({
    get: getter,
    set: setter,
    modify: f => obj => setter(f(getter(obj)), obj)
});

const prop = key => lens(
    obj => obj[key],
    (val, obj) => ({ ...obj, [key]: val })
);

const compose2 = (lens1, lens2) => lens(
    obj => lens2.get(lens1.get(obj)),
    (val, obj) => lens1.set(lens2.set(val, lens1.get(obj)), obj)
);

// 사용 예제
const user = {
    name: 'John',
    address: {
        city: 'New York',
        zip: '10001'
    }
};

const addressLens = prop('address');
const cityLens = prop('city');
const addressCityLens = compose2(addressLens, cityLens);

console.log(addressCityLens.get(user)); // 'New York'
const updatedUser = addressCityLens.set('Los Angeles', user);
console.log(updatedUser.address.city); // 'Los Angeles'

9. 메타프로그래밍

메타프로그래밍은 프로그램이 자기 자신을 조작하거나 다른 프로그램을 생성/수정할 수 있게 하는 프로그래밍 기법입니다.

// Symbol과 Well-known Symbols
class CustomArray {
    constructor(...items) {
        this.items = items;
    }
    
    // 이터러블 프로토콜 구현
    *[Symbol.iterator]() {
        for (const item of this.items) {
            yield item;
        }
    }
    
    // 프리미티브 변환
    [Symbol.toPrimitive](hint) {
        switch (hint) {
            case 'number':
                return this.items.length;
            case 'string':
                return `CustomArray(${this.items.join(', ')})`;
            default:
                return this.items;
        }
    }
    
    // 커스텀 인스턴스 확인
    static [Symbol.hasInstance](instance) {
        return Array.isArray(instance.items);
    }
}

// Reflect API 활용
class ObservableObject {
    constructor(target, onChange) {
        return new Proxy(target, {
            set(obj, prop, value, receiver) {
                const oldValue = obj[prop];
                const result = Reflect.set(obj, prop, value, receiver);
                
                if (result && oldValue !== value) {
                    onChange(prop, oldValue, value);
                }
                
                return result;
            },
            
            deleteProperty(obj, prop) {
                const oldValue = obj[prop];
                const result = Reflect.deleteProperty(obj, prop);
                
                if (result) {
                    onChange(prop, oldValue, undefined);
                }
                
                return result;
            },
            
            has(obj, prop) {
                console.log(`Checking if property "${prop}" exists`);
                return Reflect.has(obj, prop);
            }
        });
    }
}

// 동적 속성 접근자 생성
function createAccessors(obj, properties) {
    properties.forEach(prop => {
        const privateKey = Symbol(prop);
        obj[privateKey] = obj[prop];
        
        Object.defineProperty(obj, prop, {
            get() {
                console.log(`Getting ${prop}`);
                return this[privateKey];
            },
            set(value) {
                console.log(`Setting ${prop} to ${value}`);
                const oldValue = this[privateKey];
                this[privateKey] = value;
                
                // 변경 이벤트 발생
                if (this.emit) {
                    this.emit('change', { prop, oldValue, newValue: value });
                }
            },
            enumerable: true,
            configurable: true
        });
    });
}

// 태그드 템플릿 리터럴
function sql(strings, ...values) {
    let query = strings[0];
    const params = [];
    
    for (let i = 0; i < values.length; i++) {
        params.push(values[i]);
        query += '?' + strings[i + 1];
    }
    
    return {
        query,
        params,
        execute(connection) {
            return connection.query(query, params);
        }
    };
}

// 사용 예제
const userId = 123;
const status = 'active';
const query = sql`
    SELECT * FROM users 
    WHERE id = ${userId} 
    AND status = ${status}
`;

console.log(query.query);  // SELECT * FROM users WHERE id = ? AND status = ?
console.log(query.params); // [123, 'active']

10. Web Workers와 멀티스레딩

Web Workers를 사용하여 JavaScript에서 진정한 병렬 처리를 구현하고, 메인 스레드를 블로킹하지 않는 무거운 연산을 수행합니다.

// Worker 풀 구현
class WorkerPool {
    constructor(workerScript, poolSize = navigator.hardwareConcurrency) {
        this.workers = [];
        this.queue = [];
        this.poolSize = poolSize;
        
        for (let i = 0; i < poolSize; i++) {
            const worker = new Worker(workerScript);
            worker.idle = true;
            worker.id = i;
            this.workers.push(worker);
        }
    }
    
    async execute(data) {
        return new Promise((resolve, reject) => {
            const task = { data, resolve, reject };
            
            const idleWorker = this.workers.find(w => w.idle);
            
            if (idleWorker) {
                this.runTask(idleWorker, task);
            } else {
                this.queue.push(task);
            }
        });
    }
    
    runTask(worker, task) {
        worker.idle = false;
        
        const handleMessage = (event) => {
            worker.removeEventListener('message', handleMessage);
            worker.removeEventListener('error', handleError);
            
            worker.idle = true;
            task.resolve(event.data);
            
            // 큐에서 다음 작업 처리
            if (this.queue.length > 0) {
                const nextTask = this.queue.shift();
                this.runTask(worker, nextTask);
            }
        };
        
        const handleError = (error) => {
            worker.removeEventListener('message', handleMessage);
            worker.removeEventListener('error', handleError);
            
            worker.idle = true;
            task.reject(error);
        };
        
        worker.addEventListener('message', handleMessage);
        worker.addEventListener('error', handleError);
        worker.postMessage(task.data);
    }
    
    terminate() {
        this.workers.forEach(worker => worker.terminate());
    }
}

// SharedArrayBuffer를 사용한 공유 메모리 (worker.js)
// 주의: SharedArrayBuffer는 보안상의 이유로 특정 조건에서만 사용 가능
self.addEventListener('message', (event) => {
    const { type, data } = event.data;
    
    switch (type) {
        case 'init':
            // SharedArrayBuffer 초기화
            self.sharedBuffer = data.buffer;
            self.sharedArray = new Int32Array(self.sharedBuffer);
            self.postMessage({ type: 'ready' });
            break;
            
        case 'process':
            const { start, end } = data;
            
            // 병렬 처리 작업
            for (let i = start; i < end; i++) {
                // Atomics를 사용한 안전한 연산
                Atomics.add(self.sharedArray, i, 1);
            }
            
            self.postMessage({ 
                type: 'complete', 
                result: { start, end } 
            });
            break;
    }
});

// 메인 스레드에서 사용
async function parallelComputation() {
    const workerPool = new WorkerPool('worker.js', 4);
    
    // 큰 배열을 병렬로 처리
    const size = 1000000;
    const chunkSize = Math.ceil(size / 4);
    
    const promises = [];
    
    for (let i = 0; i < 4; i++) {
        const start = i * chunkSize;
        const end = Math.min(start + chunkSize, size);
        
        promises.push(workerPool.execute({
            type: 'process',
            data: { start, end }
        }));
    }
    
    const results = await Promise.all(promises);
    console.log('Parallel computation complete:', results);
    
    workerPool.terminate();
}

// Transferable Objects를 사용한 효율적인 데이터 전송
class ImageProcessor {
    constructor() {
        this.worker = new Worker('image-processor.js');
    }
    
    async processImage(imageData) {
        return new Promise((resolve, reject) => {
            const buffer = imageData.data.buffer;
            
            this.worker.onmessage = (event) => {
                const processedData = new ImageData(
                    new Uint8ClampedArray(event.data.buffer),
                    event.data.width,
                    event.data.height
                );
                resolve(processedData);
            };
            
            this.worker.onerror = reject;
            
            // Transferable로 버퍼 전송 (복사 대신 이동)
            this.worker.postMessage({
                buffer: buffer,
                width: imageData.width,
                height: imageData.height
            }, [buffer]);
        });
    }
}

11. WebAssembly 통합

WebAssembly(WASM)를 JavaScript와 통합하여 네이티브에 가까운 성능을 달성하는 방법을 알아봅니다.

// WebAssembly 모듈 로더
class WasmLoader {
    constructor() {
        this.modules = new Map();
    }
    
    async load(url, importObject = {}) {
        if (this.modules.has(url)) {
            return this.modules.get(url);
        }
        
        try {
            const response = await fetch(url);
            const buffer = await response.arrayBuffer();
            
            // WebAssembly 인스턴스화
            const module = await WebAssembly.compile(buffer);
            const instance = await WebAssembly.instantiate(module, importObject);
            
            const wasmModule = {
                module,
                instance,
                exports: instance.exports
            };
            
            this.modules.set(url, wasmModule);
            return wasmModule;
            
        } catch (error) {
            console.error('Failed to load WASM module:', error);
            throw error;
        }
    }
    
    async loadStreaming(url, importObject = {}) {
        try {
            const result = await WebAssembly.instantiateStreaming(
                fetch(url),
                importObject
            );
            
            return {
                module: result.module,
                instance: result.instance,
                exports: result.instance.exports
            };
        } catch (error) {
            // 스트리밍이 실패하면 일반 로드로 폴백
            return this.load(url, importObject);
        }
    }
}

// WASM과 JavaScript 간 메모리 공유
class WasmMemoryManager {
    constructor(initialPages = 1, maxPages = 10) {
        this.memory = new WebAssembly.Memory({
            initial: initialPages,
            maximum: maxPages
        });
        
        this.HEAP8 = new Int8Array(this.memory.buffer);
        this.HEAP16 = new Int16Array(this.memory.buffer);
        this.HEAP32 = new Int32Array(this.memory.buffer);
        this.HEAPU8 = new Uint8Array(this.memory.buffer);
        this.HEAPF32 = new Float32Array(this.memory.buffer);
        this.HEAPF64 = new Float64Array(this.memory.buffer);
    }
    
    updateViews() {
        // 메모리가 증가한 경우 뷰 업데이트
        this.HEAP8 = new Int8Array(this.memory.buffer);
        this.HEAP16 = new Int16Array(this.memory.buffer);
        this.HEAP32 = new Int32Array(this.memory.buffer);
        this.HEAPU8 = new Uint8Array(this.memory.buffer);
        this.HEAPF32 = new Float32Array(this.memory.buffer);
        this.HEAPF64 = new Float64Array(this.memory.buffer);
    }
    
    allocateString(str) {
        const encoder = new TextEncoder();
        const encoded = encoder.encode(str);
        const ptr = this.allocate(encoded.length + 1);
        
        this.HEAPU8.set(encoded, ptr);
        this.HEAPU8[ptr + encoded.length] = 0; // null terminator
        
        return ptr;
    }
    
    readString(ptr) {
        let end = ptr;
        while (this.HEAPU8[end] !== 0) end++;
        
        const decoder = new TextDecoder();
        return decoder.decode(this.HEAPU8.subarray(ptr, end));
    }
    
    allocate(size) {
        // 간단한 할당 (실제로는 더 복잡한 메모리 관리 필요)
        if (!this.nextPtr) this.nextPtr = 1024; // 처음 1KB는 예약
        
        const ptr = this.nextPtr;
        this.nextPtr += size;
        
        return ptr;
    }
}

// 실제 사용 예제
async function initializeWasm() {
    const loader = new WasmLoader();
    const memoryManager = new WasmMemoryManager();
    
    // JavaScript에서 WASM으로 제공할 함수들
    const importObject = {
        env: {
            memory: memoryManager.memory,
            
            // WASM에서 호출할 JavaScript 함수
            jsLog: (ptr) => {
                const message = memoryManager.readString(ptr);
                console.log('From WASM:', message);
            },
            
            jsPerformanceNow: () => performance.now(),
            
            abort: (msg, file, line, column) => {
                console.error('WASM abort:', {
                    message: memoryManager.readString(msg),
                    file: memoryManager.readString(file),
                    line,
                    column
                });
            }
        },
        
        math: {
            random: () => Math.random(),
            sin: Math.sin,
            cos: Math.cos
        }
    };
    
    const wasm = await loader.loadStreaming('module.wasm', importObject);
    
    // WASM 함수 호출
    const result = wasm.exports.fibonacci(40);
    console.log('Fibonacci(40):', result);
    
    // 배열 처리
    const array = new Float32Array([1, 2, 3, 4, 5]);
    const ptr = memoryManager.allocate(array.length * 4);
    memoryManager.HEAPF32.set(array, ptr / 4);
    
    const sum = wasm.exports.sumArray(ptr, array.length);
    console.log('Sum of array:', sum);
}

12. 실전 프로젝트: 고성능 상태 관리 시스템

지금까지 배운 고급 기법들을 종합하여 실제 애플리케이션에서 사용할 수 있는 고성능 상태 관리 시스템을 구현합니다.

// 고성능 상태 관리 시스템
class StateManager {
    constructor(initialState = {}) {
        this.state = this.createReactiveState(initialState);
        this.subscribers = new Map();
        this.middleware = [];
        this.history = [];
        this.historyIndex = -1;
        this.maxHistorySize = 50;
        this.transactions = [];
        this.computed = new Map();
        this.asyncActions = new Map();
        
        // 성능 모니터링
        this.performanceMonitor = new PerformanceMonitor();
        
        // Worker 풀 초기화
        this.workerPool = null;
        
        // 디바운스/쓰로틀 맵
        this.debouncedActions = new Map();
        this.throttledActions = new Map();
    }
    
    createReactiveState(target, path = []) {
        const self = this;
        
        return new Proxy(target, {
            get(obj, prop) {
                const value = Reflect.get(obj, prop);
                
                if (typeof value === 'object' && value !== null) {
                    return self.createReactiveState(value, [...path, prop]);
                }
                
                return value;
            },
            
            set(obj, prop, value) {
                const fullPath = [...path, prop].join('.');
                const oldValue = obj[prop];
                
                if (oldValue === value) return true;
                
                // 미들웨어 실행
                const context = {
                    path: fullPath,
                    oldValue,
                    newValue: value,
                    preventDefault: false
                };
                
                for (const middleware of self.middleware) {
                    middleware(context);
                    if (context.preventDefault) return false;
                }
                
                // 트랜잭션 처리
                if (self.transactions.length > 0) {
                    const transaction = self.transactions[self.transactions.length - 1];
                    transaction.changes.push({ path: fullPath, oldValue, newValue: value });
                }
                
                const result = Reflect.set(obj, prop, value);
                
                // 구독자 알림
                self.notify(fullPath, value, oldValue);
                
                // computed 값 무효화
                self.invalidateComputed(fullPath);
                
                return result;
            },
            
            deleteProperty(obj, prop) {
                const fullPath = [...path, prop].join('.');
                const oldValue = obj[prop];
                
                const result = Reflect.deleteProperty(obj, prop);
                
                if (result) {
                    self.notify(fullPath, undefined, oldValue);
                    self.invalidateComputed(fullPath);
                }
                
                return result;
            }
        });
    }
    
    subscribe(path, callback, options = {}) {
        const { immediate = false, debounce = 0, throttle = 0 } = options;
        
        if (!this.subscribers.has(path)) {
            this.subscribers.set(path, new Set());
        }
        
        let wrappedCallback = callback;
        
        if (debounce > 0) {
            wrappedCallback = this.debounce(callback, debounce);
        } else if (throttle > 0) {
            wrappedCallback = this.throttle(callback, throttle);
        }
        
        this.subscribers.get(path).add(wrappedCallback);
        
        if (immediate) {
            const value = this.getValueByPath(path);
            callback(value, undefined);
        }
        
        // Unsubscribe 함수 반환
        return () => {
            const subs = this.subscribers.get(path);
            if (subs) {
                subs.delete(wrappedCallback);
            }
        };
    }
    
    notify(path, newValue, oldValue) {
        this.performanceMonitor.mark(`notify-${path}-start`);
        
        // 정확한 경로 구독자
        const exactSubscribers = this.subscribers.get(path);
        if (exactSubscribers) {
            exactSubscribers.forEach(callback => callback(newValue, oldValue));
        }
        
        // 와일드카드 구독자
        this.subscribers.forEach((callbacks, subscribedPath) => {
            if (subscribedPath.includes('*')) {
                const regex = new RegExp(
                    '^' + subscribedPath.replace(/\*/g, '.*') + '$'
                );
                if (regex.test(path)) {
                    callbacks.forEach(callback => callback(newValue, oldValue));
                }
            }
        });
        
        const duration = this.performanceMonitor.measure(
            `notify-${path}`,
            `notify-${path}-start`
        );
        
        if (duration > 16) { // 16ms = 60fps
            console.warn(`Slow notification for ${path}: ${duration}ms`);
        }
    }
    
    computed(name, computeFn, dependencies = []) {
        this.computed.set(name, {
            compute: computeFn,
            dependencies,
            cache: null,
            valid: false
        });
        
        // Getter 정의
        Object.defineProperty(this.state, name, {
            get: () => {
                const computed = this.computed.get(name);
                
                if (!computed.valid) {
                    computed.cache = computed.compute(this.state);
                    computed.valid = true;
                }
                
                return computed.cache;
            },
            configurable: true
        });
    }
    
    invalidateComputed(changedPath) {
        this.computed.forEach((computed, name) => {
            const shouldInvalidate = computed.dependencies.some(dep => 
                changedPath.startsWith(dep) || dep.startsWith(changedPath)
            );
            
            if (shouldInvalidate) {
                computed.valid = false;
                this.notify(name, this.state[name], computed.cache);
            }
        });
    }
    
    transaction(fn) {
        const transaction = { changes: [] };
        this.transactions.push(transaction);
        
        try {
            fn(this.state);
            
            // 트랜잭션 커밋
            this.transactions.pop();
            
            // 모든 변경사항을 한 번에 알림
            transaction.changes.forEach(change => {
                this.notify(change.path, change.newValue, change.oldValue);
            });
            
        } catch (error) {
            // 트랜잭션 롤백
            this.transactions.pop();
            
            transaction.changes.reverse().forEach(change => {
                this.setValueByPath(change.path, change.oldValue);
            });
            
            throw error;
        }
    }
    
    async dispatch(actionName, payload) {
        const action = this.asyncActions.get(actionName);
        
        if (!action) {
            throw new Error(`Action "${actionName}" not found`);
        }
        
        this.performanceMonitor.mark(`action-${actionName}-start`);
        
        try {
            const result = await action(this.state, payload);
            
            const duration = this.performanceMonitor.measure(
                `action-${actionName}`,
                `action-${actionName}-start`
            );
            
            console.log(`Action ${actionName} completed in ${duration}ms`);
            
            return result;
            
        } catch (error) {
            console.error(`Action ${actionName} failed:`, error);
            throw error;
        }
    }
    
    registerAction(name, handler) {
        this.asyncActions.set(name, handler);
    }
    
    useMiddleware(middleware) {
        this.middleware.push(middleware);
    }
    
    getValueByPath(path) {
        return path.split('.').reduce((obj, key) => obj?.[key], this.state);
    }
    
    setValueByPath(path, value) {
        const keys = path.split('.');
        const lastKey = keys.pop();
        const target = keys.reduce((obj, key) => obj[key], this.state);
        target[lastKey] = value;
    }
    
    debounce(fn, delay) {
        let timeoutId;
        return (...args) => {
            clearTimeout(timeoutId);
            timeoutId = setTimeout(() => fn(...args), delay);
        };
    }
    
    throttle(fn, delay) {
        let lastCall = 0;
        return (...args) => {
            const now = Date.now();
            if (now - lastCall >= delay) {
                lastCall = now;
                fn(...args);
            }
        };
    }
    
    saveSnapshot() {
        const snapshot = JSON.stringify(this.state);
        
        if (this.historyIndex < this.history.length - 1) {
            this.history = this.history.slice(0, this.historyIndex + 1);
        }
        
        this.history.push(snapshot);
        
        if (this.history.length > this.maxHistorySize) {
            this.history.shift();
        } else {
            this.historyIndex++;
        }
    }
    
    undo() {
        if (this.historyIndex > 0) {
            this.historyIndex--;
            const snapshot = JSON.parse(this.history[this.historyIndex]);
            Object.assign(this.state, snapshot);
        }
    }
    
    redo() {
        if (this.historyIndex < this.history.length - 1) {
            this.historyIndex++;
            const snapshot = JSON.parse(this.history[this.historyIndex]);
            Object.assign(this.state, snapshot);
        }
    }
    
    getPerformanceStats() {
        const stats = {};
        
        this.performanceMonitor.measures.forEach((measurements, name) => {
            stats[name] = this.performanceMonitor.getStats(name);
        });
        
        return stats;
    }
}

// 사용 예제
const store = new StateManager({
    user: {
        id: 1,         name: 'John Doe',
        email: 'john@example.com',
        preferences: {
            theme: 'dark',
            language: 'ko'
        }
    },
    products: [],
    cart: {
        items: [],
        total: 0
    },
    ui: {
        loading: false,
        modal: null,
        notifications: []
    }
});

// 미들웨어 등록
store.useMiddleware((context) => {
    console.log(`State change: ${context.path}`, {
        old: context.oldValue,
        new: context.newValue
    });
});

// 유효성 검사 미들웨어
store.useMiddleware((context) => {
    if (context.path === 'user.email') {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(context.newValue)) {
            console.error('Invalid email format');
            context.preventDefault = true;
        }
    }
});

// Computed 속성 정의
store.computed('cartItemCount', (state) => {
    return state.cart.items.reduce((sum, item) => sum + item.quantity, 0);
}, ['cart.items']);

store.computed('isLoggedIn', (state) => {
    return state.user.id !== null;
}, ['user.id']);

// 비동기 액션 등록
store.registerAction('fetchProducts', async (state, { category }) => {
    state.ui.loading = true;
    
    try {
        const response = await fetch(`/api/products?category=${category}`);
        const products = await response.json();
        
        state.products = products;
        state.ui.loading = false;
        
        return products;
    } catch (error) {
        state.ui.loading = false;
        state.ui.notifications.push({
            type: 'error',
            message: 'Failed to fetch products',
            timestamp: Date.now()
        });
        throw error;
    }
});

store.registerAction('addToCart', async (state, product) => {
    const existingItem = state.cart.items.find(item => item.id === product.id);
    
    if (existingItem) {
        existingItem.quantity++;
    } else {
        state.cart.items.push({
            ...product,
            quantity: 1
        });
    }
    
    // 총액 재계산
    state.cart.total = state.cart.items.reduce(
        (sum, item) => sum + (item.price * item.quantity),
        0
    );
    
    // 알림 추가
    state.ui.notifications.push({
        type: 'success',
        message: `${product.name} added to cart`,
        timestamp: Date.now()
    });
});

// 구독 예제
const unsubscribe = store.subscribe('cart.total', (newTotal, oldTotal) => {
    console.log(`Cart total changed: ${oldTotal} → ${newTotal}`);
    
    if (newTotal > 100000) {
        console.log('Free shipping available!');
    }
}, { debounce: 500 });

// 와일드카드 구독
store.subscribe('user.*', (newValue, oldValue) => {
    console.log('User data changed', { newValue, oldValue });
    store.saveSnapshot(); // 변경사항 저장
});

// 트랜잭션 사용
store.transaction((state) => {
    // 여러 상태를 원자적으로 업데이트
    state.user.name = 'Jane Doe';
    state.user.email = 'jane@example.com';
    state.user.preferences.theme = 'light';
});

// 성능 최적화된 대량 데이터 처리
class OptimizedDataProcessor {
    constructor(stateManager) {
        this.stateManager = stateManager;
        this.batchQueue = [];
        this.batchTimer = null;
        this.batchSize = 100;
        this.batchDelay = 16; // 60fps
    }
    
    processBatch(items, processor) {
        return new Promise((resolve) => {
            const chunks = this.chunkArray(items, this.batchSize);
            let currentChunk = 0;
            
            const processNextChunk = () => {
                if (currentChunk >= chunks.length) {
                    resolve();
                    return;
                }
                
                // 현재 청크 처리
                this.stateManager.transaction((state) => {
                    chunks[currentChunk].forEach(item => processor(state, item));
                });
                
                currentChunk++;
                
                // 다음 프레임에서 계속
                requestAnimationFrame(processNextChunk);
            };
            
            processNextChunk();
        });
    }
    
    chunkArray(array, size) {
        const chunks = [];
        for (let i = 0; i < array.length; i += size) {
            chunks.push(array.slice(i, i + size));
        }
        return chunks;
    }
    
    async virtualScroll(container, items, renderItem) {
        const itemHeight = 50; // 각 아이템의 높이
        const containerHeight = container.clientHeight;
        const visibleCount = Math.ceil(containerHeight / itemHeight);
        const bufferCount = 5; // 버퍼 아이템 수
        
        let scrollTop = 0;
        let startIndex = 0;
        let endIndex = visibleCount + bufferCount;
        
        const updateView = () => {
            startIndex = Math.floor(scrollTop / itemHeight);
            endIndex = startIndex + visibleCount + bufferCount * 2;
            
            const visibleItems = items.slice(startIndex, endIndex);
            const offsetY = startIndex * itemHeight;
            
            // DOM 업데이트
            container.innerHTML = '';
            const wrapper = document.createElement('div');
            wrapper.style.transform = `translateY(${offsetY}px)`;
            
            visibleItems.forEach(item => {
                wrapper.appendChild(renderItem(item));
            });
            
            container.appendChild(wrapper);
        };
        
        container.addEventListener('scroll', () => {
            scrollTop = container.scrollTop;
            requestAnimationFrame(updateView);
        });
        
        updateView();
    }
}

// 실제 사용 시나리오
async function initializeApp() {
    // 초기 데이터 로드
    await store.dispatch('fetchProducts', { category: 'electronics' });
    
    // UI 컴포넌트와 연결
    class UIComponent {
        constructor(store, selector) {
            this.store = store;
            this.element = document.querySelector(selector);
            this.subscriptions = [];
        }
        
        bindState(path, updateFn) {
            const unsubscribe = this.store.subscribe(path, (newValue) => {
                updateFn.call(this, this.element, newValue);
            }, { immediate: true });
            
            this.subscriptions.push(unsubscribe);
        }
        
        destroy() {
            this.subscriptions.forEach(unsub => unsub());
        }
    }
    
    // 카트 UI 컴포넌트
    const cartComponent = new UIComponent(store, '#cart');
    
    cartComponent.bindState('cartItemCount', (element, count) => {
        element.querySelector('.badge').textContent = count;
    });
    
    cartComponent.bindState('cart.total', (element, total) => {
        element.querySelector('.total').textContent = 
            new Intl.NumberFormat('ko-KR', {
                style: 'currency',
                currency: 'KRW'
            }).format(total);
    });
    
    // 성능 모니터링
    setInterval(() => {
        const stats = store.getPerformanceStats();
        console.table(stats);
    }, 5000);
}

// 앱 초기화
document.addEventListener('DOMContentLoaded', initializeApp);

마무리

이 튜토리얼에서는 JavaScript의 고급 기능과 패턴들을 깊이 있게 다뤘습니다. 클로저, 프로토타입, 비동기 프로그래밍, 제너레이터, Proxy API, 메모리 관리, 디자인 패턴, 함수형 프로그래밍, 메타프로그래밍, Web Workers, WebAssembly 통합까지 현대 JavaScript 개발에 필요한 핵심 고급 주제들을 실제 예제와 함께 살펴보았습니다.

🚀 다음 단계를 위한 제안:
  • 각 개념을 실제 프로젝트에 적용해보며 경험을 쌓으세요
  • TypeScript를 학습하여 타입 안정성을 추가하세요
  • Node.js 환경에서의 고급 패턴들도 탐구해보세요
  • 성능 프로파일링 도구를 사용하여 최적화를 연습하세요
  • 오픈소스 프로젝트에 기여하며 실전 경험을 늘리세요
⚠️ 주의사항: 고급 기능들은 강력하지만, 과도한 사용은 코드의 복잡성을 증가시킬 수 있습니다. 항상 가독성과 유지보수성을 고려하여 적절한 수준에서 사용하세요.

댓글 남기기

AI, 코딩, 일상 및 다양한 정보 공유에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기