script.js

Download
javascript 564 lines 17.2 KB
  1/*
  2 * ๋น„๋™๊ธฐ JavaScript ์˜ˆ์ œ
  3 * - ์ฝœ๋ฐฑ, Promise, async/await
  4 * - Fetch API
  5 * - ํƒ€์ด๋จธ ํ•จ์ˆ˜
  6 */
  7
  8// ============================================
  9// ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜
 10// ============================================
 11function log(outputId, message, clear = false) {
 12    const output = document.getElementById(outputId);
 13    if (clear) {
 14        output.textContent = '';
 15    }
 16    output.textContent += message + '\n';
 17    output.scrollTop = output.scrollHeight;
 18}
 19
 20function delay(ms) {
 21    return new Promise(resolve => setTimeout(resolve, ms));
 22}
 23
 24// ============================================
 25// 1. ์ฝœ๋ฐฑ ํ•จ์ˆ˜
 26// ============================================
 27function runCallback() {
 28    const output = 'callbackOutput';
 29    log(output, '=== ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰ ===', true);
 30    log(output, '[์‹œ์ž‘] ๋ฐ์ดํ„ฐ ์š”์ฒญ...');
 31
 32    function fetchData(callback) {
 33        setTimeout(() => {
 34            const data = { id: 1, name: 'ํ™๊ธธ๋™', email: 'hong@example.com' };
 35            callback(null, data);
 36        }, 1000);
 37    }
 38
 39    fetchData((error, data) => {
 40        if (error) {
 41            log(output, `[์—๋Ÿฌ] ${error.message}`);
 42        } else {
 43            log(output, `[์™„๋ฃŒ] ๋ฐ์ดํ„ฐ ์ˆ˜์‹ : ${JSON.stringify(data, null, 2)}`);
 44        }
 45    });
 46
 47    log(output, '[๋น„๋™๊ธฐ] ๋‹ค๋ฅธ ์ž‘์—… ์ˆ˜ํ–‰ ์ค‘...');
 48}
 49
 50function runCallbackHell() {
 51    const output = 'callbackOutput';
 52    log(output, '=== ์ฝœ๋ฐฑ ์ง€์˜ฅ ์˜ˆ์‹œ ===', true);
 53    log(output, '์‚ฌ์šฉ์ž ์ •๋ณด ์กฐํšŒ ์‹œ์ž‘...');
 54
 55    // ์ฝœ๋ฐฑ ์ง€์˜ฅ (ํ”ผ๋ผ๋ฏธ๋“œ ํ˜•ํƒœ)
 56    setTimeout(() => {
 57        log(output, '1. ์‚ฌ์šฉ์ž ID ์กฐํšŒ ์™„๋ฃŒ: user_123');
 58        setTimeout(() => {
 59            log(output, '2. ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์กฐํšŒ ์™„๋ฃŒ: { name: "ํ™๊ธธ๋™" }');
 60            setTimeout(() => {
 61                log(output, '3. ์‚ฌ์šฉ์ž ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ์™„๋ฃŒ: 15๊ฐœ');
 62                setTimeout(() => {
 63                    log(output, '4. ๊ฒŒ์‹œ๊ธ€ ๋Œ“๊ธ€ ์กฐํšŒ ์™„๋ฃŒ: 42๊ฐœ');
 64                    log(output, '--- ๋ชจ๋“  ์ž‘์—… ์™„๋ฃŒ ---');
 65                    log(output, '\nโš ๏ธ ์ด๋Ÿฐ ์ค‘์ฒฉ ๊ตฌ์กฐ๋Š” ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.');
 66                    log(output, 'โ†’ Promise๋‚˜ async/await๋กœ ๊ฐœ์„ ํ•˜์„ธ์š”!');
 67                }, 500);
 68            }, 500);
 69        }, 500);
 70    }, 500);
 71}
 72
 73// ============================================
 74// 2. Promise
 75// ============================================
 76function runPromise() {
 77    const output = 'promiseOutput';
 78    log(output, '=== Promise ๊ธฐ๋ณธ ===', true);
 79
 80    const promise = new Promise((resolve, reject) => {
 81        log(output, '์ž‘์—… ์‹œ์ž‘...');
 82        setTimeout(() => {
 83            const success = Math.random() > 0.3;
 84            if (success) {
 85                resolve({ status: 'success', data: '์ฒ˜๋ฆฌ ์™„๋ฃŒ!' });
 86            } else {
 87                reject(new Error('์ฒ˜๋ฆฌ ์‹คํŒจ'));
 88            }
 89        }, 1000);
 90    });
 91
 92    promise
 93        .then(result => {
 94            log(output, `โœ… ์„ฑ๊ณต: ${JSON.stringify(result)}`);
 95        })
 96        .catch(error => {
 97            log(output, `โŒ ์‹คํŒจ: ${error.message}`);
 98        })
 99        .finally(() => {
100            log(output, '๐Ÿ Promise ์™„๋ฃŒ (์„ฑ๊ณต/์‹คํŒจ ๋ฌด๊ด€)');
101        });
102}
103
104function runPromiseChain() {
105    const output = 'promiseOutput';
106    log(output, '=== Promise ์ฒด์ด๋‹ ===', true);
107
108    function step1() {
109        return new Promise(resolve => {
110            setTimeout(() => resolve(1), 300);
111        });
112    }
113
114    function step2(prev) {
115        return new Promise(resolve => {
116            setTimeout(() => resolve(prev + 10), 300);
117        });
118    }
119
120    function step3(prev) {
121        return new Promise(resolve => {
122            setTimeout(() => resolve(prev * 2), 300);
123        });
124    }
125
126    log(output, '์ฒด์ด๋‹ ์‹œ์ž‘...');
127
128    step1()
129        .then(result => {
130            log(output, `Step 1: ${result}`);
131            return step2(result);
132        })
133        .then(result => {
134            log(output, `Step 2: ${result}`);
135            return step3(result);
136        })
137        .then(result => {
138            log(output, `Step 3: ${result}`);
139            log(output, `์ตœ์ข… ๊ฒฐ๊ณผ: ${result}`);
140        });
141}
142
143function runPromiseError() {
144    const output = 'promiseOutput';
145    log(output, '=== Promise ์—๋Ÿฌ ์ฒ˜๋ฆฌ ===', true);
146
147    function riskyOperation(shouldFail) {
148        return new Promise((resolve, reject) => {
149            setTimeout(() => {
150                if (shouldFail) {
151                    reject(new Error('์˜๋„์  ์—๋Ÿฌ ๋ฐœ์ƒ!'));
152                } else {
153                    resolve('์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋จ');
154                }
155            }, 500);
156        });
157    }
158
159    log(output, '์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹คํ–‰...');
160
161    riskyOperation(true)
162        .then(result => {
163            log(output, `๊ฒฐ๊ณผ: ${result}`);
164        })
165        .catch(error => {
166            log(output, `โŒ ์—๋Ÿฌ ์บ์น˜: ${error.message}`);
167            log(output, 'โ†’ ์—๋Ÿฌ๊ฐ€ catch ๋ธ”๋ก์—์„œ ์ฒ˜๋ฆฌ๋จ');
168            return '์—๋Ÿฌ ๋ณต๊ตฌ๋จ';
169        })
170        .then(result => {
171            log(output, `๋ณต๊ตฌ ํ›„ ์ง„ํ–‰: ${result}`);
172        });
173}
174
175// ============================================
176// 3. async/await
177// ============================================
178async function runAsyncAwait() {
179    const output = 'asyncOutput';
180    log(output, '=== async/await ๊ธฐ๋ณธ ===', true);
181
182    async function fetchUserData() {
183        log(output, '์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ์š”์ฒญ ์ค‘...');
184        await delay(500);
185        return { id: 1, name: '๊น€์ฒ ์ˆ˜' };
186    }
187
188    async function fetchUserPosts(userId) {
189        log(output, `์‚ฌ์šฉ์ž ${userId}์˜ ๊ฒŒ์‹œ๊ธ€ ์š”์ฒญ ์ค‘...`);
190        await delay(500);
191        return ['๊ฒŒ์‹œ๊ธ€ 1', '๊ฒŒ์‹œ๊ธ€ 2', '๊ฒŒ์‹œ๊ธ€ 3'];
192    }
193
194    try {
195        const user = await fetchUserData();
196        log(output, `์‚ฌ์šฉ์ž: ${JSON.stringify(user)}`);
197
198        const posts = await fetchUserPosts(user.id);
199        log(output, `๊ฒŒ์‹œ๊ธ€: ${posts.join(', ')}`);
200
201        log(output, 'โœ… ๋ชจ๋“  ์ž‘์—… ์™„๋ฃŒ!');
202    } catch (error) {
203        log(output, `โŒ ์—๋Ÿฌ: ${error.message}`);
204    }
205}
206
207async function runAsyncError() {
208    const output = 'asyncOutput';
209    log(output, '=== async/await ์—๋Ÿฌ ์ฒ˜๋ฆฌ ===', true);
210
211    async function unstableOperation() {
212        await delay(500);
213        throw new Error('๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์‹คํŒจ');
214    }
215
216    log(output, '๋ถˆ์•ˆ์ •ํ•œ ์ž‘์—… ์‹œ๋„...');
217
218    try {
219        await unstableOperation();
220        log(output, '์ž‘์—… ์„ฑ๊ณต');
221    } catch (error) {
222        log(output, `โŒ try-catch๋กœ ์—๋Ÿฌ ์บ์น˜: ${error.message}`);
223        log(output, 'โ†’ async/await์—์„œ๋Š” try-catch ์‚ฌ์šฉ');
224    } finally {
225        log(output, '๐Ÿ finally ๋ธ”๋ก ์‹คํ–‰');
226    }
227}
228
229// ============================================
230// 4. Promise.all, Promise.race, Promise.allSettled
231// ============================================
232async function runPromiseAll() {
233    const output = 'promiseAllOutput';
234    log(output, '=== Promise.all ===', true);
235    log(output, '3๊ฐœ์˜ ์ž‘์—…์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค...');
236
237    const startTime = Date.now();
238
239    const task1 = delay(1000).then(() => '์ž‘์—… 1 ์™„๋ฃŒ');
240    const task2 = delay(1500).then(() => '์ž‘์—… 2 ์™„๋ฃŒ');
241    const task3 = delay(800).then(() => '์ž‘์—… 3 ์™„๋ฃŒ');
242
243    try {
244        const results = await Promise.all([task1, task2, task3]);
245        const elapsed = Date.now() - startTime;
246
247        log(output, `\n๊ฒฐ๊ณผ: ${results.join(', ')}`);
248        log(output, `์ด ์†Œ์š” ์‹œ๊ฐ„: ${elapsed}ms`);
249        log(output, '\n๐Ÿ’ก Promise.all์€ ๋ชจ๋“  Promise๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ');
250        log(output, 'โ†’ ๊ฐ€์žฅ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…(1500ms) ๊ธฐ์ค€์œผ๋กœ ์™„๋ฃŒ');
251    } catch (error) {
252        log(output, `์—๋Ÿฌ: ${error.message}`);
253    }
254}
255
256async function runPromiseRace() {
257    const output = 'promiseAllOutput';
258    log(output, '=== Promise.race ===', true);
259    log(output, '๊ฐ€์žฅ ๋น ๋ฅธ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค...');
260
261    const startTime = Date.now();
262
263    const slow = delay(2000).then(() => '๋А๋ฆฐ ์ž‘์—…');
264    const medium = delay(1000).then(() => '์ค‘๊ฐ„ ์ž‘์—…');
265    const fast = delay(500).then(() => '๋น ๋ฅธ ์ž‘์—…');
266
267    const winner = await Promise.race([slow, medium, fast]);
268    const elapsed = Date.now() - startTime;
269
270    log(output, `\n๐Ÿ† ์Šน์ž: ${winner}`);
271    log(output, `์†Œ์š” ์‹œ๊ฐ„: ${elapsed}ms`);
272    log(output, '\n๐Ÿ’ก Promise.race๋Š” ๊ฐ€์žฅ ๋จผ์ € ์™„๋ฃŒ๋˜๋Š” Promise ๋ฐ˜ํ™˜');
273}
274
275async function runPromiseAllSettled() {
276    const output = 'promiseAllOutput';
277    log(output, '=== Promise.allSettled ===', true);
278    log(output, '์„ฑ๊ณต/์‹คํŒจ ์ƒ๊ด€์—†์ด ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค...');
279
280    const tasks = [
281        delay(500).then(() => '์„ฑ๊ณต 1'),
282        delay(300).then(() => { throw new Error('์‹คํŒจ 1'); }),
283        delay(400).then(() => '์„ฑ๊ณต 2'),
284        delay(600).then(() => { throw new Error('์‹คํŒจ 2'); }),
285    ];
286
287    const results = await Promise.allSettled(tasks);
288
289    log(output, '\n๊ฒฐ๊ณผ:');
290    results.forEach((result, index) => {
291        if (result.status === 'fulfilled') {
292            log(output, `  ${index + 1}. โœ… ${result.value}`);
293        } else {
294            log(output, `  ${index + 1}. โŒ ${result.reason.message}`);
295        }
296    });
297
298    log(output, '\n๐Ÿ’ก Promise.allSettled๋Š” ๋ชจ๋“  Promise์˜ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์ง‘');
299    log(output, 'โ†’ ์ผ๋ถ€๊ฐ€ ์‹คํŒจํ•ด๋„ ๋‚˜๋จธ์ง€ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธ ๊ฐ€๋Šฅ');
300}
301
302// ============================================
303// 5. Fetch API
304// ============================================
305async function fetchUsers() {
306    const output = 'fetchOutput';
307    const btn = document.getElementById('fetchUsersBtn');
308
309    log(output, '=== ์‚ฌ์šฉ์ž ๋ชฉ๋ก ์กฐํšŒ ===', true);
310    btn.disabled = true;
311    btn.innerHTML = '๋กœ๋”ฉ ์ค‘... <span class="loading"></span>';
312
313    try {
314        const response = await fetch('https://jsonplaceholder.typicode.com/users?_limit=5');
315
316        if (!response.ok) {
317            throw new Error(`HTTP ${response.status}`);
318        }
319
320        const users = await response.json();
321
322        log(output, `${users.length}๋ช…์˜ ์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค:\n`);
323
324        users.forEach(user => {
325            log(output, `๐Ÿ‘ค ${user.name}`);
326            log(output, `   ์ด๋ฉ”์ผ: ${user.email}`);
327            log(output, `   ํšŒ์‚ฌ: ${user.company.name}`);
328            log(output, '');
329        });
330    } catch (error) {
331        log(output, `โŒ ์—๋Ÿฌ: ${error.message}`);
332    } finally {
333        btn.disabled = false;
334        btn.textContent = '์‚ฌ์šฉ์ž ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ';
335    }
336}
337
338async function fetchPost() {
339    const output = 'fetchOutput';
340    log(output, '=== ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ===', true);
341
342    const postId = Math.floor(Math.random() * 100) + 1;
343    log(output, `๊ฒŒ์‹œ๊ธ€ #${postId} ์š”์ฒญ ์ค‘...`);
344
345    try {
346        const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`);
347        const post = await response.json();
348
349        log(output, `\n๐Ÿ“„ ์ œ๋ชฉ: ${post.title}`);
350        log(output, `\n๋ณธ๋ฌธ:\n${post.body}`);
351    } catch (error) {
352        log(output, `โŒ ์—๋Ÿฌ: ${error.message}`);
353    }
354}
355
356// ============================================
357// 6. POST ์š”์ฒญ
358// ============================================
359async function createPost() {
360    const output = 'postOutput';
361    const title = document.getElementById('postTitle').value || '์ œ๋ชฉ ์—†์Œ';
362    const body = document.getElementById('postBody').value || '๋‚ด์šฉ ์—†์Œ';
363
364    log(output, '=== POST ์š”์ฒญ ===', true);
365    log(output, '๊ฒŒ์‹œ๊ธ€ ์ƒ์„ฑ ์ค‘...');
366
367    try {
368        const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
369            method: 'POST',
370            headers: {
371                'Content-Type': 'application/json',
372            },
373            body: JSON.stringify({
374                title: title,
375                body: body,
376                userId: 1,
377            }),
378        });
379
380        const data = await response.json();
381
382        log(output, '\nโœ… ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ฑ ์„ฑ๊ณต!');
383        log(output, `์‘๋‹ต ๋ฐ์ดํ„ฐ:`);
384        log(output, JSON.stringify(data, null, 2));
385    } catch (error) {
386        log(output, `โŒ ์—๋Ÿฌ: ${error.message}`);
387    }
388}
389
390// ============================================
391// 7. ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํŒจํ„ด
392// ============================================
393async function testErrorHandling() {
394    const output = 'errorOutput';
395    log(output, '=== ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํŒจํ„ด ===', true);
396
397    // ํŒจํ„ด 1: try-catch
398    log(output, '1. try-catch ํŒจํ„ด:');
399    try {
400        const result = await fetch('https://jsonplaceholder.typicode.com/posts/1');
401        const data = await result.json();
402        log(output, `   โœ… ์„ฑ๊ณต: ${data.title.substring(0, 20)}...`);
403    } catch (error) {
404        log(output, `   โŒ ์‹คํŒจ: ${error.message}`);
405    }
406
407    // ํŒจํ„ด 2: ์—๋Ÿฌ ๋ž˜ํ•‘
408    log(output, '\n2. ์—๋Ÿฌ ๋ž˜ํ•‘ ํŒจํ„ด:');
409
410    async function safeFetch(url) {
411        try {
412            const response = await fetch(url);
413            if (!response.ok) {
414                throw new Error(`HTTP ${response.status}`);
415            }
416            return { data: await response.json(), error: null };
417        } catch (error) {
418            return { data: null, error: error.message };
419        }
420    }
421
422    const { data, error } = await safeFetch('https://jsonplaceholder.typicode.com/posts/1');
423    if (error) {
424        log(output, `   โŒ ์—๋Ÿฌ: ${error}`);
425    } else {
426        log(output, `   โœ… ๋ฐ์ดํ„ฐ: ${data.title.substring(0, 20)}...`);
427    }
428
429    log(output, '\n๐Ÿ’ก ์—๋Ÿฌ ๋ž˜ํ•‘ ํŒจํ„ด์€ Go ์–ธ์–ด ์Šคํƒ€์ผ');
430    log(output, 'โ†’ ๋ช…์‹œ์ ์ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅ');
431}
432
433async function testNetworkError() {
434    const output = 'errorOutput';
435    log(output, '=== ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ ํ…Œ์ŠคํŠธ ===', true);
436    log(output, '์กด์žฌํ•˜์ง€ ์•Š๋Š” URL์— ์š”์ฒญ...\n');
437
438    try {
439        // ํƒ€์ž„์•„์›ƒ ๊ตฌํ˜„
440        const controller = new AbortController();
441        const timeoutId = setTimeout(() => controller.abort(), 3000);
442
443        const response = await fetch('https://invalid-url-that-does-not-exist.com/api', {
444            signal: controller.signal,
445        });
446
447        clearTimeout(timeoutId);
448        const data = await response.json();
449        log(output, `์„ฑ๊ณต: ${data}`);
450    } catch (error) {
451        if (error.name === 'AbortError') {
452            log(output, 'โŒ ํƒ€์ž„์•„์›ƒ: 3์ดˆ ๋‚ด ์‘๋‹ต ์—†์Œ');
453        } else {
454            log(output, `โŒ ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ: ${error.message}`);
455        }
456        log(output, '\n๐Ÿ’ก ์‹ค์ œ ์•ฑ์—์„œ๋Š” ์žฌ์‹œ๋„ ๋กœ์ง ์ถ”๊ฐ€ ๊ถŒ์žฅ');
457    }
458}
459
460// ============================================
461// 8. ์‚ฌ์šฉ์ž ์นด๋“œ ๋กœ๋“œ
462// ============================================
463async function loadUserCards() {
464    const container = document.getElementById('userCards');
465    const btn = document.getElementById('loadCardsBtn');
466
467    container.innerHTML = '<p>๋กœ๋”ฉ ์ค‘...</p>';
468    btn.disabled = true;
469
470    try {
471        const response = await fetch('https://jsonplaceholder.typicode.com/users?_limit=6');
472        const users = await response.json();
473
474        container.innerHTML = users.map(user => `
475            <div class="user-card">
476                <img src="https://i.pravatar.cc/80?u=${user.id}" alt="${user.name}">
477                <h4>${user.name}</h4>
478                <p>@${user.username}</p>
479                <p>${user.email}</p>
480            </div>
481        `).join('');
482    } catch (error) {
483        container.innerHTML = `<p style="color: red;">์—๋Ÿฌ: ${error.message}</p>`;
484    } finally {
485        btn.disabled = false;
486    }
487}
488
489// ============================================
490// 9. setTimeout, setInterval
491// ============================================
492let timerCount = 0;
493let timerInterval = null;
494
495function startTimer() {
496    const output = document.getElementById('timerOutput');
497
498    if (timerInterval) {
499        output.textContent = 'ํƒ€์ด๋จธ๊ฐ€ ์ด๋ฏธ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค.';
500        return;
501    }
502
503    timerCount = 0;
504    output.textContent = `ํƒ€์ด๋จธ: ${timerCount}`;
505
506    timerInterval = setInterval(() => {
507        timerCount++;
508        output.textContent = `ํƒ€์ด๋จธ: ${timerCount}์ดˆ`;
509
510        if (timerCount >= 60) {
511            stopTimer();
512            output.textContent += ' (์ตœ๋Œ€ ์‹œ๊ฐ„ ๋„๋‹ฌ)';
513        }
514    }, 1000);
515}
516
517function stopTimer() {
518    const output = document.getElementById('timerOutput');
519
520    if (timerInterval) {
521        clearInterval(timerInterval);
522        timerInterval = null;
523        output.textContent = `ํƒ€์ด๋จธ ์ค‘์ง€: ${timerCount}์ดˆ์—์„œ ๋ฉˆ์ถค`;
524    } else {
525        output.textContent = '์‹คํ–‰ ์ค‘์ธ ํƒ€์ด๋จธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.';
526    }
527}
528
529// Debounce ๊ตฌํ˜„
530function debounce(func, wait) {
531    let timeout;
532    return function executedFunction(...args) {
533        const later = () => {
534            clearTimeout(timeout);
535            func(...args);
536        };
537        clearTimeout(timeout);
538        timeout = setTimeout(later, wait);
539    };
540}
541
542function runDebounceDemo() {
543    const output = document.getElementById('timerOutput');
544    let callCount = 0;
545
546    const debouncedLog = debounce(() => {
547        output.textContent = `Debounce ์™„๋ฃŒ! ์‹ค์ œ ์‹คํ–‰ ํšŸ์ˆ˜: 1ํšŒ (์‹œ๋„: ${callCount}ํšŒ)`;
548    }, 500);
549
550    output.textContent = '๋น ๋ฅธ ํ˜ธ์ถœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์ค‘...';
551
552    // 100ms ๊ฐ„๊ฒฉ์œผ๋กœ 10๋ฒˆ ํ˜ธ์ถœ ์‹œ๋„
553    for (let i = 0; i < 10; i++) {
554        setTimeout(() => {
555            callCount++;
556            debouncedLog();
557        }, i * 100);
558    }
559
560    setTimeout(() => {
561        output.textContent += '\n๐Ÿ’ก Debounce: ๋งˆ์ง€๋ง‰ ํ˜ธ์ถœ ํ›„ 500ms ๋Œ€๊ธฐ ํ›„ ์‹คํ–‰';
562    }, 2000);
563}