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}