Ver3.0 ๐ ๋ธ๋ผ์ฐ์ API์ Web API๋ก ๋ค์ดํฐ๋ธ ๊ธฐ๋ฅ ์ ๋ณตํ๊ธฐ: ์น์ด ์ฑ์ฒ๋ผ ๋์ํ๋ ๋ง๋ฒ

๐ ๋ธ๋ผ์ฐ์ API์ Web API๋ก ๋ค์ดํฐ๋ธ ๊ธฐ๋ฅ ์ ๋ณตํ๊ธฐ: ์น์ด ์ฑ์ฒ๋ผ ๋์ํ๋ ๋ง๋ฒ
JavaScript๋ก ์นด๋ฉ๋ผ, ์์น์ ๋ณด, ์๋ฆผ๊น์ง! ์น ๋ธ๋ผ์ฐ์ ์ ์จ๊ฒจ์ง ํ์๋ฅผ 100% ํ์ฉํ๋ ์ค์ ๊ฐ์ด๋ ๐ฑโจ
๐ฏ ์น ๋ธ๋ผ์ฐ์ , ์๊ฐ๋ณด๋ค ํจ์ฌ ๋๋ํ๋ค!
์ผ, ์์งํ ๋งํด์ ์์ ์๋ ์น ๋ธ๋ผ์ฐ์ ๊ฐ ๊ทธ๋ฅ ๋ฌธ์ ๋ณด๋ ์ฉ๋์์์? ๊ทธ๋ฐ๋ฐ ์ง๊ธ์ ์์ ๋ค๋ฅด๋ค๊ณ ! ๐ฎ
์์ฆ ๋ธ๋ผ์ฐ์ ๋ ๋ค์ดํฐ๋ธ ์ฑ ๋ชป์ง์์ ๊ธฐ๋ฅ์ ์ ๊ณตํด. ์นด๋ฉ๋ผ ์ ๊ทผ, GPS ์์น ์ถ์ , ํธ์ ์๋ฆผ, ์ฌ์ง์ด ๋ธ๋ฃจํฌ์ค ๊ธฐ๊ธฐ ์ฐ๊ฒฐ๊น์ง ๊ฐ๋ฅํ๋ค๋๊น?
์ด ๋ชจ๋ ๊ฒ ๊ฐ๋ฅํ ์ด์ ๋ ๋ฐ๋ก ๋ธ๋ผ์ฐ์ API์ Web API ๋๋ถ์ด์ผ. ์ด ์น๊ตฌ๋ค์ JavaScript๋ฅผ ํตํด ๋ธ๋ผ์ฐ์ ์ ๊ฐ๋ ฅํ ๋ค์ดํฐ๋ธ ๊ธฐ๋ฅ๋ค์ ์ฐ๋ฆฌ๊ฐ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ์ธํฐํ์ด์ค์ผ. ๐จ
์๋ฅผ ๋ค์ด, ์นด๋ฉ๋ผ๋ฅผ ์ผ๋ ค๋ฉด ์นด๋ฉ๋ผ ํ๋์จ์ด๋ฅผ ์ง์ ์ ์ดํ ํ์ ์์ด navigator.mediaDevices.getUserMedia() ์ด๋ ๊ฒ๋ง ํธ์ถํ๋ฉด ๋! ๊ฐ๋จํ์ง? ๐
๐ ๋ธ๋ผ์ฐ์ API vs Web API, ๋ญ๊ฐ ๋ค๋ฅธ ๊ฑฐ์ผ?
์ด ๋์ ํท๊ฐ๋ คํ๋ ์ฌ๋๋ค์ด ์์ฒญ ๋ง์. ์ฌ์ค ๋๋ ์ฒ์์ ๊ทธ๋ฌ๊ฑฐ๋ ? ๐
๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๋ฉด ์ด๋:
๐ Web API
๋ ๋์ ๊ฐ๋
!
์น ํ๊ฒฝ์์ ์ฌ์ฉํ ์ ์๋ ๋ชจ๋ API๋ฅผ ํต์นญํด. ๋ธ๋ผ์ฐ์ API๋ ํฌํจ๋๊ณ , ์๋ฒ์ ํต์ ํ๋ REST API, ์ธ๋ถ ์๋น์ค API(๊ตฌ๊ธ ๋งต, ํ์ด์ค๋ถ ๋ก๊ทธ์ธ ๋ฑ)๋ ๋ค ํฌํจ๋๋ ํฐ ์ฐ์ฐ ๊ฐ์ ๊ฐ๋
์ด์ผ. โ๏ธ
๐ฅ๏ธ ๋ธ๋ผ์ฐ์ API
๋ธ๋ผ์ฐ์ ๊ฐ ์ ๊ณตํ๋ ๊ฒ!
๋ธ๋ผ์ฐ์ ์์ฒด์ ๋ด์ฅ๋์ด ์๋ API๋ค์ด์ผ. DOM ์กฐ์, ์์น์ ๋ณด, ๋ฏธ๋์ด ์ฅ์น ์ ๊ทผ ๋ฑ ๋ธ๋ผ์ฐ์ ๊ฐ ์ง์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ๋ค. ๋ณ๋ ์ค์น๋ ์ธ๋ถ ์๋น์ค ์์ด ๋ฐ๋ก ์ฌ์ฉ ๊ฐ๋ฅ! ๐
์ฆ, ๋ธ๋ผ์ฐ์ API๋ Web API์ ๋ถ๋ถ์งํฉ์ด์ผ. ๋ชจ๋ ๋ธ๋ผ์ฐ์ API๋ Web API์ง๋ง, ๋ชจ๋ Web API๊ฐ ๋ธ๋ผ์ฐ์ API๋ ์๋๋ผ๋ ๊ฑฐ!
๐ฑ ์ค์ ์์ ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ๋ธ๋ผ์ฐ์ API ๋ฒ ์คํธ 10
์, ์ด์ ์ง์ง ์ค์ํ ๋ถ๋ถ์ด์ผ. ์ค๋ฌด์์ ์ ๋ง ์์ฃผ ์ฐ์ด๋ API๋ค์ ํ๋์ฉ ํํค์ณ๋ณด์! ๐ช
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ API๋ค์ ํ์ฉํด์ ์ฌ์ฉ์ ๊ฒฝํ์ ์์ฒญ ๊ฐ์ ํ ์ ์๊ฑฐ๋ .
์ฌ์ฉ์์ ํ์ฌ ์์น๋ฅผ ์์๋ด๋ API์ผ. ๋ฐฐ๋ฌ ์ฑ, ๋ ์จ ์ฑ, ์ฃผ๋ณ ๋ง์ง ์ฐพ๊ธฐ ๋ฑ ์์น ๊ธฐ๋ฐ ์๋น์ค์ ํ์! ๐บ๏ธ
// ํ์ฌ ์์น ๊ฐ์ ธ์ค๊ธฐ - ์ง์ง ๊ฐ๋จํ๋ค!
navigator.geolocation.getCurrentPosition(
(position) => {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
console.log(`ํ์ฌ ์์น: ${latitude}, ${longitude}`);
console.log(`์ ํ๋: ${position.coords.accuracy}m`);
},
(error) => {
console.error('์์น ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ด์:', error.message);
},
{
enableHighAccuracy: true, // ๊ณ ์ ๋ฐ ๋ชจ๋
timeout: 5000, // 5์ด ํ์์์
maximumAge: 0 // ์บ์ ์ฌ์ฉ ์ ํจ
}
);
// ์ค์๊ฐ ์์น ์ถ์ (์์ง์ ๊ฐ์ง)
const watchId = navigator.geolocation.watchPosition(
(position) => {
console.log('์์น ์
๋ฐ์ดํธ:', position.coords);
}
);
// ์ถ์ ์ค์ง
navigator.geolocation.clearWatch(watchId);
โข ์ฌ์ฉ์๊ฐ ์์น ๊ถํ์ ํ์ฉํด์ผ ํจ
โข ์ฒซ ์์ฒญ ์ ๋ธ๋ผ์ฐ์ ๊ฐ ๊ถํ ํ์ ์ ๋์
โข ์ ํ๋๋ GPS, Wi-Fi, IP ๋ฑ์ ๋ฐ๋ผ ๋ฌ๋ผ์ ธ (10m~์km)
ํ์ ์ฑํ , ์ฌ์ง ์ดฌ์, ์์ฑ ๋ น์ ๋ฑ ๋ฏธ๋์ด ๊ธฐ๊ธฐ๋ฅผ ์ ์ดํ๋ ๊ฐ๋ ฅํ API! ๐น๐ค
// ์นด๋ฉ๋ผ + ๋ง์ดํฌ ๋์์ ์ผ๊ธฐ
async function startCamera() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 1920 },
height: { ideal: 1080 },
facingMode: 'user' // 'user' = ์ ๋ฉด, 'environment' = ํ๋ฉด
},
audio: {
echoCancellation: true, // ์์ฝ ์ ๊ฑฐ
noiseSuppression: true // ๋
ธ์ด์ฆ ์ ๊ฑฐ
}
});
// ๋น๋์ค ์์์ ์คํธ๋ฆผ ์ฐ๊ฒฐ
const videoElement = document.querySelector('#myVideo');
videoElement.srcObject = stream;
console.log('์นด๋ฉ๋ผ ์ผ์ง! ๐ฅ');
} catch (error) {
console.error('๋ฏธ๋์ด ์ ๊ทผ ์คํจ:', error);
}
}
// ์ฌ์ง ์บก์ฒํ๊ธฐ
function capturePhoto(videoElement) {
const canvas = document.createElement('canvas');
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(videoElement, 0, 0);
// ์ด๋ฏธ์ง ๋ฐ์ดํฐ URL๋ก ๋ณํ
const photoData = canvas.toDataURL('image/png');
console.log('์ฌ์ง ์ฐ์๋ค! ๐ธ');
return photoData;
}
// ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฏธ๋์ด ์ฅ์น ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ
async function getDevices() {
const devices = await navigator.mediaDevices.enumerateDevices();
const cameras = devices.filter(d => d.kind === 'videoinput');
const microphones = devices.filter(d => d.kind === 'audioinput');
console.log(`์นด๋ฉ๋ผ ${cameras.length}๊ฐ, ๋ง์ดํฌ ${microphones.length}๊ฐ ๋ฐ๊ฒฌ!`);
}
โข ํ๋กํ ์ฌ์ง ์ดฌ์ ๊ธฐ๋ฅ
โข QR ์ฝ๋ ์ค์บ๋
โข ์์ฑ ๋ฉ์์ง ๋ น์
โข ์ค์๊ฐ ํํฐ/์ดํํธ ์ ์ฉ
์ฌ์ฉ์์๊ฒ ์ค์ํ ์ ๋ณด๋ฅผ ์๋ ค์ฃผ๋ ๋ฐ์คํฌํฑ/๋ชจ๋ฐ์ผ ์๋ฆผ! ๐
๋ธ๋ผ์ฐ์ ๊ฐ ๋ฐฑ๊ทธ๋ผ์ด๋์ ์์ด๋ ์๋ฆผ์ด ๋ฌ๋ค๋ ๊ฒ ํต์ฌ์ด์ผ.
// ์๋ฆผ ๊ถํ ์์ฒญ
async function requestNotificationPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('์๋ฆผ ๊ถํ ํ๋! โ
');
showNotification();
} else if (permission === 'denied') {
console.log('์๋ฆผ ๊ถํ ๊ฑฐ๋ถ๋จ โ');
}
}
// ์๋ฆผ ํ์ํ๊ธฐ
function showNotification() {
const notification = new Notification('์ ๋ฉ์์ง๊ฐ ๋์ฐฉํ์ด์! ๐', {
body: '์ฌ๋ฅ๋ท์์ ๋๊ตฐ๊ฐ ๋น์ ์ ์ฌ๋ฅ์ ๊ด์ฌ์ ๋ณด์์ด์!',
icon: '/icon.png',
badge: '/badge.png',
tag: 'message-notification', // ๊ฐ์ ํ๊ทธ๋ ํ๋๋ง ํ์
requireInteraction: false, // ์๋์ผ๋ก ์ฌ๋ผ์ง
silent: false, // ์๋ฆฌ ์ฌ์
vibrate: [200, 100, 200] // ์ง๋ ํจํด (๋ชจ๋ฐ์ผ)
});
// ์๋ฆผ ํด๋ฆญ ์ด๋ฒคํธ
notification.onclick = (event) => {
event.preventDefault();
window.open('https://www.jaenung.net/messages', '_blank');
notification.close();
};
// 3์ด ํ ์๋ ๋ซ๊ธฐ
setTimeout(() => notification.close(), 3000);
}
// ํ์ฌ ๊ถํ ์ํ ํ์ธ
console.log('ํ์ฌ ์๋ฆผ ๊ถํ:', Notification.permission);
โข ๋๋ฌด ์์ฃผ ์๋ฆผ ๋ณด๋ด๋ฉด ์ฌ์ฉ์๊ฐ ์ง์ฆ๋์ ์ฐจ๋จํจ
โข iOS Safari๋ PWA๋ก ์ค์น๋ ๊ฒฝ์ฐ์๋ง ์๋ฆผ ์ง์
โข ์๋ฆผ ๊ถํ์ ๋๋ฉ์ธ๋ณ๋ก ๊ด๋ฆฌ๋จ
์ฟ ํค๋ณด๋ค ํจ์ฌ ๊ฐ๋ ฅํ๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์ด ์ ์ฅ์! localStorage์ sessionStorage ๋ ๊ฐ์ง๊ฐ ์์ด. ๐พ
| ๊ตฌ๋ถ | localStorage | sessionStorage |
|---|---|---|
| ์๋ช | ์๊ตฌ ์ ์ฅ (์ง์ ์ญ์ ์ ๊น์ง) | ํญ/์ฐฝ ๋ซ์ผ๋ฉด ์ญ์ |
| ์ฉ๋ | ๋ณดํต 5~10MB | ๋ณดํต 5~10MB |
| ๊ณต์ ๋ฒ์ | ๊ฐ์ ๋๋ฉ์ธ์ ๋ชจ๋ ํญ/์ฐฝ | ๊ฐ์ ํญ/์ฐฝ ๋ด์์๋ง |
| ์ฉ๋ | ์ค์ , ํ ๋ง, ๋ก๊ทธ์ธ ์ ๋ณด | ์์ ๋ฐ์ดํฐ, ํผ ์ ๋ ฅ๊ฐ |
// localStorage ์ฌ์ฉ๋ฒ - ์ง์ง ์ฝ๋ค!
// ์ ์ฅ
localStorage.setItem('username', '์ฝ๋ฉ๋ง์คํฐ');
localStorage.setItem('theme', 'dark');
// ์ฝ๊ธฐ
const username = localStorage.getItem('username');
console.log(username); // '์ฝ๋ฉ๋ง์คํฐ'
// ์ญ์
localStorage.removeItem('theme');
// ์ ์ฒด ์ญ์
localStorage.clear();
// ๊ฐ์ฒด ์ ์ฅํ๊ธฐ (JSON ๋ณํ ํ์)
const userInfo = {
name: '๊น๊ฐ๋ฐ',
age: 25,
skills: ['JavaScript', 'React', 'Node.js']
};
localStorage.setItem('userInfo', JSON.stringify(userInfo));
// ๊ฐ์ฒด ์ฝ๊ธฐ
const savedUser = JSON.parse(localStorage.getItem('userInfo'));
console.log(savedUser.skills); // ['JavaScript', 'React', 'Node.js']
// sessionStorage๋ ์ฌ์ฉ๋ฒ ๋๊ฐ์!
sessionStorage.setItem('tempData', '์์ ๋ฐ์ดํฐ');
// ์ ์ฅ ๊ณต๊ฐ ํ์ธ
console.log(`์ ์ฅ๋ ํญ๋ชฉ ๊ฐ์: ${localStorage.length}`);
// ๋ชจ๋ ํค ์ํ
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
console.log(`${key}: ${localStorage.getItem(key)}`);
}
โข ์ฉ๋ ์ ํ์ด ์์ผ๋ ํฐ ๋ฐ์ดํฐ๋ IndexedDB ์ฌ์ฉ
โข ์ ์ฅ ์ ์ try-catch๋ก ๊ฐ์ธ์ ์ฉ๋ ์ด๊ณผ ์๋ฌ ์ฒ๋ฆฌ
โข ๋คํฌ๋ชจ๋ ์ค์ , ์ธ์ด ์ ํ, ์ต๊ทผ ๊ฒ์์ด ๋ฑ์ ์๋ฒฝ!
XMLHttpRequest์ ํ๋์ ๋์ฒดํ! ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๊ฐ์ฅ ์ธ๋ จ๋ ๋ฐฉ๋ฒ์ด์ผ. ๐
// ๊ธฐ๋ณธ GET ์์ฒญ
async function fetchData() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error(`HTTP ์๋ฌ! ์ํ: ${response.status}`);
}
const data = await response.json();
console.log('๋ฐ์ดํฐ ๋ฐ์์ด:', data);
return data;
} catch (error) {
console.error('์์ฒญ ์คํจ:', error);
}
}
// POST ์์ฒญ (๋ฐ์ดํฐ ์ ์ก)
async function createUser(userData) {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here'
},
body: JSON.stringify(userData)
});
const result = await response.json();
return result;
}
// ํ์ผ ์
๋ก๋
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('description', 'ํ๋กํ ์ฌ์ง');
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData // Content-Type ์๋ ์ค์ ๋จ
});
return await response.json();
}
// ํ์์์ ์ค์
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
signal: controller.signal
});
clearTimeout(timeoutId);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('์์ฒญ ์๊ฐ ์ด๊ณผ!');
}
throw error;
}
}
// ๋ณ๋ ฌ ์์ฒญ (์ฌ๋ฌ API ๋์ ํธ์ถ)
async function fetchMultiple() {
const [users, posts, comments] = await Promise.all([
fetch('https://api.example.com/users').then(r => r.json()),
fetch('https://api.example.com/posts').then(r => r.json()),
fetch('https://api.example.com/comments').then(r => r.json())
]);
console.log('๋ชจ๋ ๋ฐ์ดํฐ ๋ฐ์์ด!', { users, posts, comments });
}
- โ Promise ๊ธฐ๋ฐ์ด๋ผ async/await ์ฌ์ฉ ๊ฐ๋ฅ
- โ ์ฝ๋๊ฐ ๊น๋ํ๊ณ ์ฝ๊ธฐ ์ฌ์
- โ Request/Response ๊ฐ์ฒด๋ก ์ธ๋ฐํ ์ ์ด
- โ CORS ์ฒ๋ฆฌ๊ฐ ๊ฐํธํจ
- โ ์คํธ๋ฆฌ๋ฐ ์ง์์ผ๋ก ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๊ฐ๋ฅ
2D ๊ทธ๋ํฝ์ ๊ทธ๋ฆฌ๊ณ , ์ด๋ฏธ์ง๋ฅผ ์กฐ์ํ๊ณ , ๊ฒ์์ ๋ง๋ค ์ ์๋ ๊ฐ๋ ฅํ API! ๐จ
// Canvas ๊ธฐ๋ณธ ์ค์
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
ctx.fillStyle = '#a8d5e2';
ctx.fillRect(50, 50, 200, 100); // x, y, width, height
// ์ ๊ทธ๋ฆฌ๊ธฐ
ctx.beginPath();
ctx.arc(300, 200, 50, 0, Math.PI * 2); // x, y, radius, startAngle, endAngle
ctx.fillStyle = '#c5a8e2';
ctx.fill();
// ํ
์คํธ ๊ทธ๋ฆฌ๊ธฐ
ctx.font = 'bold 30px Arial';
ctx.fillStyle = '#5a9fb8';
ctx.fillText('Hello Canvas!', 100, 300);
// ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๊ธฐ
const img = new Image();
img.onload = () => {
ctx.drawImage(img, 0, 0, 200, 200);
};
img.src = 'image.jpg';
// ๊ทธ๋ผ๋์ธํธ ํจ๊ณผ
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, '#a8d5e2');
gradient.addColorStop(1, '#c5a8e2');
ctx.fillStyle = gradient;
ctx.fillRect(50, 400, 200, 100);
// ์ด๋ฏธ์ง ํํฐ ์ ์ฉ (ํฝ์
์กฐ์)
function applyGrayscale() {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // Red
data[i + 1] = avg; // Green
data[i + 2] = avg; // Blue
}
ctx.putImageData(imageData, 0, 0);
}
// Canvas๋ฅผ ์ด๋ฏธ์ง๋ก ์ ์ฅ
function saveCanvas() {
const dataURL = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = 'my-drawing.png';
link.href = dataURL;
link.click();
}
โข ์ฐจํธ ๋ฐ ๊ทธ๋ํ ์๊ฐํ
โข ์ด๋ฏธ์ง ํธ์ง๊ธฐ (ํํฐ, ํฌ๋กญ, ํ์ )
โข ์๋ช ํจ๋
โข ์ค์๊ฐ ๊ทธ๋ฆผํ
โข ๋ฐ์ดํฐ ์๊ฐํ ๋์๋ณด๋
์ค๋์ค๋ฅผ ์ฌ์ํ๊ณ , ์กฐ์ํ๊ณ , ๋ถ์ํ๋ ๊ฐ๋ ฅํ API! ์์ ์ฑ, ๊ฒ์ ์ฌ์ด๋, ์ค๋์ค ์ดํํธ ๋ฑ์ ํ์์ผ. ๐ต
// Audio Context ์์ฑ
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// ์ค๋์ค ํ์ผ ๋ก๋ ๋ฐ ์ฌ์
async function playSound(url) {
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start(0); // ์ฆ์ ์ฌ์
}
// ๋ณผ๋ฅจ ์กฐ์
function playWithVolume(audioBuffer, volume) {
const source = audioContext.createBufferSource();
const gainNode = audioContext.createGain();
source.buffer = audioBuffer;
gainNode.gain.value = volume; // 0.0 ~ 1.0
source.connect(gainNode);
gainNode.connect(audioContext.destination);
source.start(0);
}
// ์ฃผํ์ ๋ถ์ (์์
์๊ฐํ)
function createVisualizer() {
const analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
function draw() {
analyser.getByteTimeDomainData(dataArray);
// Canvas์ ํํ ๊ทธ๋ฆฌ๊ธฐ
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.lineWidth = 2;
ctx.strokeStyle = '#a8d5e2';
ctx.beginPath();
const sliceWidth = canvas.width / bufferLength;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const v = dataArray[i] / 128.0;
const y = v * canvas.height / 2;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
x += sliceWidth;
}
ctx.stroke();
requestAnimationFrame(draw);
}
draw();
}
// ๋ง์ดํฌ ์
๋ ฅ ๋ฐ๊ธฐ
async function getMicrophoneInput() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
source.connect(analyser);
// ์ค์๊ฐ ์๋ ์ธก์
const dataArray = new Uint8Array(analyser.frequencyBinCount);
function checkVolume() {
analyser.getByteFrequencyData(dataArray);
const average = dataArray.reduce((a, b) => a + b) / dataArray.length;
console.log('ํ์ฌ ์๋:', average);
requestAnimationFrame(checkVolume);
}
checkVolume();
}
ํด๋ฆฝ๋ณด๋์ ์ ๊ทผํด์ ํ ์คํธ๋ ์ด๋ฏธ์ง๋ฅผ ๋ณต์ฌ/๋ถ์ฌ๋ฃ๊ธฐ ํ ์ ์์ด! ๐
// ํ
์คํธ ๋ณต์ฌํ๊ธฐ
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ๋จ! โ
');
} catch (error) {
console.error('๋ณต์ฌ ์คํจ:', error);
}
}
// ํด๋ฆฝ๋ณด๋์์ ํ
์คํธ ์ฝ๊ธฐ
async function pasteFromClipboard() {
try {
const text = await navigator.clipboard.readText();
console.log('๋ถ์ฌ๋ฃ๊ธฐ:', text);
return text;
} catch (error) {
console.error('์ฝ๊ธฐ ์คํจ:', error);
}
}
// ์ด๋ฏธ์ง ๋ณต์ฌํ๊ธฐ
async function copyImageToClipboard(imageBlob) {
try {
await navigator.clipboard.write([
new ClipboardItem({
'image/png': imageBlob
})
]);
console.log('์ด๋ฏธ์ง ๋ณต์ฌ ์๋ฃ! ๐ผ๏ธ');
} catch (error) {
console.error('์ด๋ฏธ์ง ๋ณต์ฌ ์คํจ:', error);
}
}
// ํด๋ฆฝ๋ณด๋ ๋ณ๊ฒฝ ๊ฐ์ง (์คํ์ ๊ธฐ๋ฅ)
navigator.clipboard.addEventListener('clipboardchange', () => {
console.log('ํด๋ฆฝ๋ณด๋๊ฐ ๋ณ๊ฒฝ๋์์ด์!');
});
// ๊ตฌํ ๋ธ๋ผ์ฐ์ ํธํ ๋ฐฉ๋ฒ (fallback)
function copyTextFallback(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
console.log('๋ณต์ฌ ์ฑ๊ณต (fallback)');
} catch (error) {
console.error('๋ณต์ฌ ์คํจ:', error);
}
document.body.removeChild(textarea);
}
์์๊ฐ ํ๋ฉด์ ๋ณด์ด๋์ง ๊ฐ์งํ๋ API! ๋ฌดํ ์คํฌ๋กค, ๋ ์ด์ง ๋ก๋ฉ, ์ ๋๋ฉ์ด์ ํธ๋ฆฌ๊ฑฐ ๋ฑ์ ์๋ฒฝํด. ๐
// ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('์์๊ฐ ํ๋ฉด์ ๋ํ๋จ!', entry.target);
entry.target.classList.add('visible');
} else {
console.log('์์๊ฐ ํ๋ฉด์์ ์ฌ๋ผ์ง!', entry.target);
}
});
}, {
root: null, // viewport ๊ธฐ์ค
rootMargin: '0px', // ์ฌ๋ฐฑ
threshold: 0.5 // 50% ๋ณด์ผ ๋ ํธ๋ฆฌ๊ฑฐ
});
// ๊ด์ฐฐ ์์
const targetElement = document.querySelector('.my-element');
observer.observe(targetElement);
// ์ด๋ฏธ์ง ๋ ์ด์ง ๋ก๋ฉ
function lazyLoadImages() {
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // data-src๋ฅผ ์ค์ src๋ก
img.classList.add('loaded');
imageObserver.unobserve(img); // ๋ก๋ฉ ํ ๊ด์ฐฐ ์ค์ง
}
});
});
const lazyImages = document.querySelectorAll('img[data-src]');
lazyImages.forEach(img => imageObserver.observe(img));
}
// ๋ฌดํ ์คํฌ๋กค ๊ตฌํ
function infiniteScroll() {
const sentinel = document.querySelector('.scroll-sentinel');
const scrollObserver = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadMoreContent();
}
}, {
rootMargin: '200px' // 200px ์ ์ ๋ฏธ๋ฆฌ ๋ก๋ฉ
});
scrollObserver.observe(sentinel);
}
// ์คํฌ๋กค ์ ๋๋ฉ์ด์
function animateOnScroll() {
const animateObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, {
threshold: 0.3
});
document.querySelectorAll('.animate-on-scroll').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(50px)';
el.style.transition = 'all 0.6s ease';
animateObserver.observe(el);
});
}
โข ์ ํํ ๊ฐ์์ฑ ํ๋จ ๊ฐ๋ฅ
โข ์ฝ๋๊ฐ ๊ฐ๊ฒฐํ๊ณ ์ดํดํ๊ธฐ ์ฌ์
โข ๋ฐฐํฐ๋ฆฌ ์๋ชจ๊ฐ ์ ์ (๋ชจ๋ฐ์ผ์ ์ ๋ฆฌ)
๊ธฐ๊ธฐ์ ๋ฐฐํฐ๋ฆฌ ์ํ๋ฅผ ํ์ธํ ์ ์์ด! ๋ฐฐํฐ๋ฆฌ๊ฐ ๋ถ์กฑํ ๋ ๊ธฐ๋ฅ์ ์ ํํ๋ ๋ฑ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ ์ ์์ง. ๐
// ๋ฐฐํฐ๋ฆฌ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
async function checkBattery() {
if ('getBattery' in navigator) {
const battery = await navigator.getBattery();
console.log(`๋ฐฐํฐ๋ฆฌ ์๋: ${battery.level * 100}%`);
console.log(`์ถฉ์ ์ค: ${battery.charging ? '์' : '์๋์ค'}`);
console.log(`์์ถฉ๊น์ง: ${battery.chargingTime}์ด`);
console.log(`๋ฐฉ์ ๊น์ง: ${battery.dischargingTime}์ด`);
// ๋ฐฐํฐ๋ฆฌ ๋ณํ ๊ฐ์ง
battery.addEventListener('levelchange', () => {
console.log('๋ฐฐํฐ๋ฆฌ ์๋ ๋ณ๊ฒฝ:', battery.level * 100 + '%');
});
battery.addEventListener('chargingchange', () => {
console.log('์ถฉ์ ์ํ ๋ณ๊ฒฝ:', battery.charging);
});
// ๋ฐฐํฐ๋ฆฌ ๋ถ์กฑ ์ ๊ฒฝ๊ณ
if (battery.level < 0.2 && !battery.charging) {
alert('๋ฐฐํฐ๋ฆฌ๊ฐ ๋ถ์กฑํฉ๋๋ค! โ ๏ธ');
// ๋ฌด๊ฑฐ์ด ์์
์ค๋จ, ํ๋ฉด ๋ฐ๊ธฐ ์กฐ์ ๋ฑ
}
} else {
console.log('Battery API ๋ฏธ์ง์');
}
}
// ๋ฐฐํฐ๋ฆฌ ์ํ์ ๋ฐ๋ฅธ ์ต์ ํ
async function optimizeByBattery() {
const battery = await navigator.getBattery();
if (battery.level < 0.3 && !battery.charging) {
// ์ ์ ๋ชจ๋ ํ์ฑํ
disableAnimations();
reduceUpdateFrequency();
console.log('์ ์ ๋ชจ๋ ํ์ฑํ๋จ ๐');
}
}
๐ก๏ธ ๋ณด์๊ณผ ๊ถํ ๊ด๋ฆฌ - ์์ ํ๊ฒ ์ฌ์ฉํ๊ธฐ
๊ฐ๋ ฅํ API๋ค์ ๋ณด์ ์ํ๋ ํจ๊ป ๊ฐ์ ธ์. ๊ทธ๋์ ๋ธ๋ผ์ฐ์ ๋ ์ฌ๋ฌ ๋ณด์ ์ฅ์น๋ฅผ ๋ง๋ จํด๋์ด! ๐
2๋จ๊ณ: ์น์ธ ์ฌ์ฉ์๊ฐ ๋ธ๋ผ์ฐ์ ํ์ ์์ ํ์ฉ/๊ฑฐ๋ถ ์ ํ
3๋จ๊ณ: ์คํ ํ์ฉ๋ ๊ฒฝ์ฐ์๋ง ๊ธฐ๋ฅ ์๋
// Permissions API๋ก ๊ถํ ์ํ ํ์ธ
async function checkPermission(permissionName) {
try {
const result = await navigator.permissions.query({ name: permissionName });
console.log(`${permissionName} ๊ถํ:`, result.state);
// 'granted' (ํ์ฉ), 'denied' (๊ฑฐ๋ถ), 'prompt' (๋ฌผ์ด๋ณผ ์์ )
// ๊ถํ ๋ณ๊ฒฝ ๊ฐ์ง
result.addEventListener('change', () => {
console.log('๊ถํ ์ํ ๋ณ๊ฒฝ:', result.state);
});
return result.state;
} catch (error) {
console.error('๊ถํ ํ์ธ ์คํจ:', error);
}
}
// ์ฌ์ฉ ์์
await checkPermission('geolocation');
await checkPermission('notifications');
await checkPermission('camera');
// ๊ถํ ์์ฒญ ์ ์ฌ์ฉ์์๊ฒ ์ค๋ช
ํ๊ธฐ (UX ๊ฐ์ )
async function requestLocationWithExplanation() {
const permissionStatus = await checkPermission('geolocation');
if (permissionStatus === 'prompt') {
// ๊ถํ ์์ฒญ ์ ์ ์ ํ์ํ์ง ์ค๋ช
const userConfirmed = confirm(
'์ฃผ๋ณ ์ฌ๋ฅ ๊ณต์ ์๋ฅผ ์ฐพ๊ธฐ ์ํด ์์น ์ ๋ณด๊ฐ ํ์ํฉ๋๋ค. ํ์ฉํ์๊ฒ ์ด์?'
);
if (userConfirmed) {
navigator.geolocation.getCurrentPosition(
(position) => console.log('์์น ํ๋ ์ฑ๊ณต!'),
(error) => console.error('์์น ํ๋ ์คํจ:', error)
);
}
} else if (permissionStatus === 'denied') {
alert('์์น ๊ถํ์ด ๊ฑฐ๋ถ๋์์ต๋๋ค. ๋ธ๋ผ์ฐ์ ์ค์ ์์ ๋ณ๊ฒฝํด์ฃผ์ธ์.');
}
}
- โ HTTPS ํ์ (๋๋ถ๋ถ์ API๋ ๋ณด์ ์ฐ๊ฒฐ์์๋ง ์๋)
- โ ์ฌ์ฉ์ ๋์ ์์ด ๊ถํ ์์ฒญ ๊ธ์ง
- โ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ ์ํธํํด์ ์ ์ฅ
- โ API ํธ์ถ ์คํจ ์ ์ ์ ํ ์๋ฌ ์ฒ๋ฆฌ
- โ ๊ถํ ๊ฑฐ๋ถ ์ ๋์ฒด ๊ธฐ๋ฅ ์ ๊ณต
- โ ์ฌ์ฉ์์๊ฒ ์ ๊ถํ์ด ํ์ํ์ง ๋ช ํํ ์ค๋ช
๐ CORS์ Same-Origin Policy
๋ธ๋ผ์ฐ์ ๋ ๋ณด์์ ์ํด ๋ค๋ฅธ ๋๋ฉ์ธ์ ๋ฆฌ์์ค ์ ๊ทผ์ ์ ํํด. ์ด๊ฒ ๋ฐ๋ก CORS(Cross-Origin Resource Sharing)์ผ!
// CORS ์๋ฌ ํด๊ฒฐ ๋ฐฉ๋ฒ
// 1. ์๋ฒ์์ CORS ํค๋ ์ค์ (๋ฐฑ์๋ ์์
)
// Access-Control-Allow-Origin: *
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE
// Access-Control-Allow-Headers: Content-Type
// 2. ํ๋ก์ ์๋ฒ ์ฌ์ฉ
const proxyUrl = 'https://cors-anywhere.herokuapp.com/';
const targetUrl = 'https://api.example.com/data';
fetch(proxyUrl + targetUrl)
.then(response => response.json())
.then(data => console.log(data));
// 3. JSONP ๋ฐฉ์ (๊ตฌํ ๋ฐฉ๋ฒ, ๊ถ์ฅํ์ง ์์)
function loadJSONP(url, callback) {
const script = document.createElement('script');
script.src = url + '?callback=' + callback;
document.body.appendChild(script);
}
// 4. ๊ฐ๋ฐ ์ค์๋ ๋ธ๋ผ์ฐ์ CORS ๋นํ์ฑํ (ํ๋ก๋์
์์๋ ์ ๋ ๊ธ์ง!)
// Chrome: --disable-web-security ํ๋๊ทธ๋ก ์คํ
๐ ์ค์ ํ๋ก์ ํธ ์์ - ์ข ํฉ ํ์ฉ
์, ์ด์ ๋ฐฐ์ด API๋ค์ ์กฐํฉํด์ ์ค์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์! ๐ช
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ฌ์ฉํ ์ ์๋ ์ค์ฉ์ ์ธ ์์ ๋ค์ด์ผ.
class PhotoApp {
constructor() {
this.stream = null;
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.video = document.getElementById('video');
}
// ์นด๋ฉ๋ผ ์์
async startCamera() {
try {
this.stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'user', width: 1280, height: 720 },
audio: false
});
this.video.srcObject = this.stream;
await this.video.play();
console.log('์นด๋ฉ๋ผ ์์๋จ! ๐ท');
} catch (error) {
console.error('์นด๋ฉ๋ผ ์ ๊ทผ ์คํจ:', error);
alert('์นด๋ฉ๋ผ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.');
}
}
// ์ฌ์ง ์ดฌ์
capturePhoto() {
this.canvas.width = this.video.videoWidth;
this.canvas.height = this.video.videoHeight;
this.ctx.drawImage(this.video, 0, 0);
// ์ดฌ์ ํจ๊ณผ์ (Web Audio API)
this.playShutterSound();
return this.canvas.toDataURL('image/jpeg', 0.9);
}
// ํํฐ ์ ์ฉ
applyFilter(filterType) {
const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
const data = imageData.data;
switch(filterType) {
case 'grayscale':
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i+1] + data[i+2]) / 3;
data[i] = data[i+1] = data[i+2] = avg;
}
break;
case 'sepia':
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i+1], b = data[i+2];
data[i] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189);
data[i+1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
data[i+2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131);
}
break;
case 'brightness':
const brightness = 50;
for (let i = 0; i < data.length; i += 4) {
data[i] += brightness;
data[i+1] += brightness;
data[i+2] += brightness;
}
break;
}
this.ctx.putImageData(imageData, 0, 0);
}
// ์ฌ์ง ์ ์ฅ
async savePhoto() {
const dataURL = this.canvas.toDataURL('image/jpeg');
// Clipboard API๋ก ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
const blob = await (await fetch(dataURL)).blob();
await navigator.clipboard.write([
new ClipboardItem({ 'image/png': blob })
]);
// ์๋ฆผ ํ์
new Notification('์ฌ์ง ์ ์ฅ ์๋ฃ! ๐ธ', {
body: 'ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ๋์์ต๋๋ค.',
icon: dataURL
});
// localStorage์ ์ ์ฅ (์ต๊ทผ ์ฌ์ง ๋ชฉ๋ก)
const recentPhotos = JSON.parse(localStorage.getItem('recentPhotos') || '[]');
recentPhotos.unshift({ url: dataURL, timestamp: Date.now() });
localStorage.setItem('recentPhotos', JSON.stringify(recentPhotos.slice(0, 10)));
}
// ์นด๋ฉ๋ผ ์ข
๋ฃ
stopCamera() {
if (this.stream) {
this.stream.getTracks().forEach(track => track.stop());
this.video.srcObject = null;
console.log('์นด๋ฉ๋ผ ์ข
๋ฃ๋จ');
}
}
playShutterSound() {
const audioContext = new AudioContext();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = 800;
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.1);
}
}
// ์ฌ์ฉ๋ฒ
const app = new PhotoApp();
document.getElementById('startBtn').addEventListener('click', () => app.startCamera());
document.getElementById('captureBtn').addEventListener('click', () => app.capturePhoto());
document.getElementById('saveBtn').addEventListener('click', () => app.savePhoto());
class TalentFinder {
constructor() {
this.userLocation = null;
this.talents = [];
}
// ํ์ฌ ์์น ๊ฐ์ ธ์ค๊ธฐ
async getCurrentLocation() {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error('Geolocation API ๋ฏธ์ง์'));
return;
}
navigator.geolocation.getCurrentPosition(
(position) => {
this.userLocation = {
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy
};
resolve(this.userLocation);
},
(error) => reject(error),
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 60000 // 1๋ถ๊ฐ ์บ์ ์ฌ์ฉ
}
);
});
}
// ์ฃผ๋ณ ์ฌ๋ฅ ๊ณต์ ์ ๊ฒ์
async findNearbyTalents(radius = 5000) { // ๋ฐ๊ฒฝ 5km
try {
await this.getCurrentLocation();
// ์๋ฒ์ ์์น ์ ๋ณด ์ ์กํ๊ณ ์ฃผ๋ณ ์ฌ๋ฅ์ ๋ชฉ๋ก ๋ฐ๊ธฐ
const response = await fetch('https://www.jaenung.net/api/nearby-talents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
latitude: this.userLocation.lat,
longitude: this.userLocation.lng,
radius: radius
})
});
this.talents = await response.json();
// ๊ฑฐ๋ฆฌ ๊ณ์ฐ ๋ฐ ์ ๋ ฌ
this.talents.forEach(talent => {
talent.distance = this.calculateDistance(
this.userLocation.lat,
this.userLocation.lng,
talent.latitude,
talent.longitude
);
});
this.talents.sort((a, b) => a.distance - b.distance);
// ์๋ฆผ ์ ์ก
if (this.talents.length > 0) {
new Notification('์ฃผ๋ณ ์ฌ๋ฅ ๋ฐ๊ฒฌ! ๐ฏ', {
body: `${this.talents.length}๋ช
์ ์ฌ๋ฅ ๊ณต์ ์๋ฅผ ์ฐพ์์ด์!`,
icon: '/icon.png'
});
}
return this.talents;
} catch (error) {
console.error('์ฌ๋ฅ ๊ฒ์ ์คํจ:', error);
throw error;
}
}
// ๋ ์ง์ ๊ฐ ๊ฑฐ๋ฆฌ ๊ณ์ฐ (Haversine ๊ณต์)
calculateDistance(lat1, lng1, lat2, lng2) {
const R = 6371; // ์ง๊ตฌ ๋ฐ์ง๋ฆ (km)
const dLat = this.toRad(lat2 - lat1);
const dLng = this.toRad(lng2 - lng1);
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
Math.sin(dLng/2) * Math.sin(dLng/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
const distance = R * c;
return Math.round(distance * 1000); // ๋ฏธํฐ ๋จ์๋ก ๋ฐํ
}
toRad(degrees) {
return degrees * Math.PI / 180;
}
// ์ค์๊ฐ ์์น ์ถ์ (์ด๋ ์ค)
watchLocation(callback) {
return navigator.geolocation.watchPosition(
(position) => {
this.userLocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
callback(this.userLocation);
},
(error) => console.error('์์น ์ถ์ ์คํจ:', error),
{ enableHighAccuracy: true }
);
}
// ๊ฒฐ๊ณผ๋ฅผ ํ๋ฉด์ ํ์
displayResults() {
const container = document.getElementById('results');
container.innerHTML = '';
this.talents.forEach(talent => {
const card = document.createElement('div');
card.className = 'talent-card';
card.innerHTML = `
<h3>${talent.name}</h3>
<p>${talent.skill}</p>
<p>๐ ${talent.distance}m ๊ฑฐ๋ฆฌ</p>
<button onclick="contactTalent('${talent.id}')">์ฐ๋ฝํ๊ธฐ</button>
`;
container.appendChild(card);
});
}
}
// ์ฌ์ฉ๋ฒ
const finder = new TalentFinder();
document.getElementById('searchBtn').addEventListener('click', async () => {
try {
await finder.findNearbyTalents(5000);
finder.displayResults();
} catch (error) {
alert('์์น ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.');
}
});
class NotificationManager {
constructor() {
this.permission = Notification.permission;
this.notifications = [];
}
// ์๋ฆผ ๊ถํ ์์ฒญ
async requestPermission() {
if (this.permission === 'granted') {
return true;
}
const result = await Notification.requestPermission();
this.permission = result;
if (result === 'granted') {
this.showWelcomeNotification();
return true;
}
return false;
}
// ํ์ ์๋ฆผ
showWelcomeNotification() {
const notification = new Notification('์๋ฆผ ์ค์ ์๋ฃ! ๐', {
body: '์ด์ ์ค์ํ ์์์ ๋์น์ง ๋ง์ธ์!',
icon: '/icon.png',
badge: '/badge.png'
});
setTimeout(() => notification.close(), 3000);
}
// ์ ๋ฉ์์ง ์๋ฆผ
notifyNewMessage(sender, message) {
if (this.permission !== 'granted') return;
const notification = new Notification(`${sender}๋์ ๋ฉ์์ง`, {
body: message.substring(0, 100),
icon: sender.profileImage,
tag: 'message-' + sender.id, // ๊ฐ์ ๋ฐ์ ์๋ ํ๋๋ง ํ์
requireInteraction: true, // ์ฌ์ฉ์๊ฐ ๋ซ์ ๋๊น์ง ์ ์ง
actions: [
{ action: 'reply', title: '๋ต์ฅํ๊ธฐ' },
{ action: 'view', title: '๋ณด๊ธฐ' }
]
});
notification.onclick = () => {
window.focus();
window.location.href = '/messages/' + sender.id;
notification.close();
};
this.notifications.push(notification);
}
// ์์ฝ ์๋ฆผ (ํน์ ์๊ฐ์)
scheduleNotification(title, body, timestamp) {
const now = Date.now();
const delay = timestamp - now;
if (delay > 0) {
setTimeout(() => {
new Notification(title, {
body: body,
icon: '/icon.png',
vibrate: [200, 100, 200]
});
}, delay);
// localStorage์ ์ ์ฅ (ํ์ด์ง ์๋ก๊ณ ์นจ ๋๋น)
const scheduled = JSON.parse(localStorage.getItem('scheduledNotifications') || '[]');
scheduled.push({ title, body, timestamp });
localStorage.setItem('scheduledNotifications', JSON.stringify(scheduled));
}
}
// ์ฃผ๊ธฐ์ ์๋ฆผ (๋งค์ผ, ๋งค์ฃผ ๋ฑ)
setRecurringNotification(title, body, intervalMs) {
setInterval(() => {
if (this.permission === 'granted') {
new Notification(title, { body, icon: '/icon.png' });
}
}, intervalMs);
}
// ๋ฐฐํฐ๋ฆฌ ๋ถ์กฑ ์๋ฆผ
async checkBatteryAndNotify() {
if ('getBattery' in navigator) {
const battery = await navigator.getBattery();
if (battery.level < 0.15 && !battery.charging) {
new Notification('๋ฐฐํฐ๋ฆฌ ๋ถ์กฑ! ๐', {
body: `๋ฐฐํฐ๋ฆฌ๊ฐ ${Math.round(battery.level * 100)}% ๋จ์์ต๋๋ค.`,
icon: '/battery-low.png',
requireInteraction: true
});
}
}
}
// ๋ชจ๋ ์๋ฆผ ๋ซ๊ธฐ
closeAll() {
this.notifications.forEach(n => n.close());
this.notifications = [];
}
}
// ์ฌ์ฉ๋ฒ
const notificationManager = new NotificationManager();
// ์ด๊ธฐํ
notificationManager.requestPermission();
// ์ ๋ฉ์์ง ์์ ์
socket.on('newMessage', (data) => {
notificationManager.notifyNewMessage(data.sender, data.message);
});
// 30๋ถ ํ ์๋ฆผ ์์ฝ
const futureTime = Date.now() + (30 * 60 * 1000);
notificationManager.scheduleNotification(
'ํ์ ์๊ฐ์
๋๋ค!',
'10๋ถ ํ ์จ๋ผ์ธ ๋ฏธํ
์ด ์์๋ฉ๋๋ค.',
futureTime
);
// ๋งค์ผ ์ค์ 9์ ์๋ฆผ (24์๊ฐ ๊ฐ๊ฒฉ)
notificationManager.setRecurringNotification(
'์ข์ ์์นจ์
๋๋ค! โ๏ธ',
'์ค๋๋ ์ฌ๋ฅ๋ท์์ ์๋ก์ด ๊ธฐํ๋ฅผ ์ฐพ์๋ณด์ธ์!',
24 * 60 * 60 * 1000
);
โก ์ฑ๋ฅ ์ต์ ํ ํ
API๋ฅผ ์ ์ฐ๋ ๊ฒ๋ ์ค์ํ์ง๋ง, ์ฑ๋ฅ์ ๊ณ ๋ คํด์ ์จ์ผ ์ง์ง ํ๋ก์ผ! ๐ฏ
๐ ์๋ ๊ฐ์
1. API ํธ์ถ ์ต์ํ
๋ถํ์ํ ํธ์ถ์ ์ค์ด๊ณ ์บ์ฑ ํ์ฉ
2. Debounce/Throttle
์ฐ์๋ ์ด๋ฒคํธ๋ฅผ ์ ์ดํด์ ํธ์ถ ํ์ ๊ฐ์
3. Lazy Loading
ํ์ํ ๋๋ง ๋ฆฌ์์ค ๋ก๋
๐พ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
1. ์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ ๋ฆฌ
์ฌ์ฉ ํ removeEventListener ํธ์ถ
2. ์คํธ๋ฆผ ์ข
๋ฃ
๋ฏธ๋์ด ์คํธ๋ฆผ์ ๋ฐ๋์ stop() ํธ์ถ
3. ํฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
Web Worker๋ก ๋ฐฑ๊ทธ๋ผ์ด๋ ์ฒ๋ฆฌ
๐ ๋ฐฐํฐ๋ฆฌ ์ ์ฝ
1. ์์น ์ถ์ ๊ฐ๊ฒฉ ์กฐ์
watchPosition ๋์ ํ์์๋ง getCurrentPosition
2. ์ ๋๋ฉ์ด์
์ต์ ํ
requestAnimationFrame ์ฌ์ฉ
3. ์ ์ ๋ชจ๋
๋ฐฐํฐ๋ฆฌ ๋ถ์กฑ ์ ๊ธฐ๋ฅ ์ ํ
// Debounce ํจ์ (์ฐ์ ํธ์ถ ๋ฐฉ์ง)
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
// ๊ฒ์ ์
๋ ฅ ์ API ํธ์ถ ์ต์ ํ
const searchInput = document.getElementById('search');
const debouncedSearch = debounce(async (query) => {
const results = await fetch(`/api/search?q=${query}`);
displayResults(await results.json());
}, 300); // 300ms ๋๊ธฐ
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
// Throttle ํจ์ (์ผ์ ๊ฐ๊ฒฉ์ผ๋ก๋ง ์คํ)
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// ์คํฌ๋กค ์ด๋ฒคํธ ์ต์ ํ
const handleScroll = throttle(() => {
console.log('์คํฌ๋กค ์์น:', window.scrollY);
}, 100); // 100ms๋ง๋ค ํ ๋ฒ๋ง ์คํ
window.addEventListener('scroll', handleScroll);
// ์บ์ฑ ์ ๋ต
class APICache {
constructor(ttl = 60000) { // ๊ธฐ๋ณธ 1๋ถ
this.cache = new Map();
this.ttl = ttl;
}
async get(key, fetchFunction) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
console.log('์บ์์์ ๊ฐ์ ธ์ด:', key);
return cached.data;
}
console.log('์๋ก ์์ฒญ:', key);
const data = await fetchFunction();
this.cache.set(key, { data, timestamp: Date.now() });
return data;
}
clear() {
this.cache.clear();
}
}
// ์ฌ์ฉ ์์
const cache = new APICache(300000); // 5๋ถ ์บ์
async function getUserData(userId) {
return cache.get(`user-${userId}`, async () => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
});
}
// Web Worker๋ก ๋ฌด๊ฑฐ์ด ์์
์ฒ๋ฆฌ
// worker.js ํ์ผ
self.addEventListener('message', (e) => {
const { imageData } = e.data;
// ๋ฌด๊ฑฐ์ด ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์์
const processed = processImage(imageData);
self.postMessage({ result: processed });
});
// ๋ฉ์ธ ์ค๋ ๋
const worker = new Worker('worker.js');
worker.postMessage({ imageData: myImageData });
worker.addEventListener('message', (e) => {
console.log('์ฒ๋ฆฌ ์๋ฃ:', e.data.result);
});
โข Lighthouse ์ ์ ํ์ธ
โข performance.now()๋ก ์คํ ์๊ฐ ์ธก์
โข Network ํญ์์ API ํธ์ถ ์๊ฐ ๋ถ์
โข Memory ํญ์์ ๋ฉ๋ชจ๋ฆฌ ๋์ ํ์ธ
๐ ๋ธ๋ผ์ฐ์ ํธํ์ฑ ์ฒดํฌ
๋ชจ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ๋ชจ๋ API๋ฅผ ์ง์ํ๋ ๊ฑด ์๋์ผ. ํธํ์ฑ ์ฒดํฌ๋ ํ์! ๐
| API | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Geolocation | โ 5+ | โ 3.5+ | โ 5+ | โ 9+ |
| MediaDevices | โ 53+ | โ 36+ | โ 11+ | โ 12+ |
| Notification | โ 22+ | โ 22+ | โ ๏ธ 16+ (PWA๋ง) | โ 14+ |
| Web Storage | โ 4+ | โ 3.5+ | โ 4+ | โ 8+ |
| Fetch | โ 42+ | โ 39+ | โ 10.1+ | โ 14+ |
| Canvas | โ 4+ | โ 2+ | โ 3.1+ | โ 9+ |
| Web Audio | โ 35+ | โ 25+ | โ 14.1+ | โ 12+ |
| Clipboard | โ 66+ | โ 63+ | โ 13.1+ | โ 79+ |
// ๊ธฐ๋ฅ ๊ฐ์ง (Feature Detection)
function checkAPISupport() {
const support = {
geolocation: 'geolocation' in navigator,
notification: 'Notification' in window,
mediaDevices: 'mediaDevices' in navigator,
localStorage: typeof Storage !== 'undefined',
fetch: 'fetch' in window,
webAudio: 'AudioContext' in window || 'webkitAudioContext' in window,
canvas: (() => {
const canvas = document.createElement('canvas');
return !!(canvas.getContext && canvas.getContext('2d'));
})(),
clipboard: 'clipboard' in navigator,
battery: 'getBattery' in navigator,
intersectionObserver: 'IntersectionObserver' in window
};
console.log('๋ธ๋ผ์ฐ์ API ์ง์ ํํฉ:', support);
return support;
}
// ํด๋ฆฌํ ๋ก๋ (๊ตฌํ ๋ธ๋ผ์ฐ์ ์ง์)
function loadPolyfills() {
// Fetch API ํด๋ฆฌํ
if (!window.fetch) {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.js';
document.head.appendChild(script);
}
// Intersection Observer ํด๋ฆฌํ
if (!('IntersectionObserver' in window)) {
const script = document.createElement('script');
script.src = 'https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver';
document.head.appendChild(script);
}
}
// ์์ ํ API ์ฌ์ฉ
async function safeGetLocation() {
if (!('geolocation' in navigator)) {
console.error('Geolocation API ๋ฏธ์ง์');
// ๋์ฒด ๋ฐฉ๋ฒ: IP ๊ธฐ๋ฐ ์์น ์ถ์
return await getLocationByIP();
}
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
}
// ๋์ฒด ๊ธฐ๋ฅ ์ ๊ณต
function showNotificationFallback(title, message) {
if ('Notification' in window && Notification.permission === 'granted') {
new Notification(title, { body: message });
} else {
// ๋์ฒด: ํ์ด์ง ๋ด ์๋ฆผ ํ์
const toast = document.createElement('div');
toast.className = 'toast-notification';
toast.innerHTML = `<strong>${title}</strong><p>${message}</p>`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
}
โข MDN Web Docs - ๊ฐ API ๋ฌธ์์ ๋ธ๋ผ์ฐ์ ์ง์ ํ ํฌํจ
โข Modernizr - ์๋์ผ๋ก ๊ธฐ๋ฅ ๊ฐ์งํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๐ ํ์ต ๋ฆฌ์์ค ๋ฐ ๋ค์ ๋จ๊ณ
์ฌ๊ธฐ๊น์ง ์๋ค๋ฉด ์ด๋ฏธ ๋ธ๋ผ์ฐ์ API์ 80%๋ ๋ง์คํฐํ ๊ฑฐ์ผ! ๐
์ด์ ๋ ๊น์ด ํ๊ณ ๋ค ์ฐจ๋ก์ง.
โข MDN Web Docs (developer.mozilla.org) - ๊ฐ์ฅ ์ ํํ๊ณ ์์ธํ ๋ฌธ์
โข Web.dev by Google - ์ค์ ์์ ์ ๋ฒ ์คํธ ํ๋ํฐ์ค
โข W3C Specifications - ํ์ค ์คํ ๋ฌธ์
์ค์ต ํ๋ซํผ
โข CodePen - ๋ธ๋ผ์ฐ์ API ์คํํ๊ธฐ ์ข์
โข JSFiddle - ๋น ๋ฅธ ํ๋กํ ํ์ดํ
โข Glitch - ํ์คํ ํ๋ก์ ํธ ํธ์คํ
์ปค๋ฎค๋ํฐ
โข Stack Overflow - ๋ฌธ์ ํด๊ฒฐ
โข Reddit r/webdev - ์ต์ ํธ๋ ๋
โข Dev.to - ๊ฐ๋ฐ์ ๋ธ๋ก๊ทธ ํ๋ซํผ
๐ ๋ค์ ๋จ๊ณ๋ก ๋์๊ฐ๊ธฐ
์ด๊ธ โ ์ค๊ธ
โ
๋ชจ๋ ์ฃผ์ API ํ ๋ฒ์ฉ ์ฌ์ฉํด๋ณด๊ธฐ
โ
๊ฐ๋จํ ํ๋ก์ ํธ 3๊ฐ ์ด์ ์์ฑ
โ
์๋ฌ ์ฒ๋ฆฌ ๋ฐ ์์ธ ์ํฉ ๋์
โ
๋ธ๋ผ์ฐ์ ํธํ์ฑ ๊ณ ๋ ค
์ค๊ธ โ ๊ณ ๊ธ
โ
์ฌ๋ฌ API ์กฐํฉํด์ ๋ณต์กํ ๊ธฐ๋ฅ ๊ตฌํ
โ
์ฑ๋ฅ ์ต์ ํ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
โ
PWA(Progressive Web App) ๊ฐ๋ฐ
โ
WebRTC, WebGL ๋ฑ ๊ณ ๊ธ API ํ์ต
๊ณ ๊ธ โ ์ ๋ฌธ๊ฐ
โ
์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ธฐ์ฌ
โ
ํฌ๋ก์ค ๋ธ๋ผ์ฐ์ ์ด์ ํด๊ฒฐ ๋ฅ๋ ฅ
โ
๋ณด์ ์ทจ์ฝ์ ๋ถ์ ๋ฐ ๋์
โ
์๋ก์ด API ์คํ ๋ฌธ์ ์ฝ๊ณ ์ ์ฉ
๐ฎ ๋ฏธ๋์ Web API๋ค
์น ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด! ์์ผ๋ก ์ฃผ๋ชฉํด์ผ ํ API๋ค์ ์๊ฐํ ๊ฒ. ๐
Web NFC API NFC ํ๊ทธ ์ฝ๊ธฐ/์ฐ๊ธฐ
Web USB API USB ์ฅ์น ์ ์ด
File System Access API ๋ก์ปฌ ํ์ผ ์์คํ ์ ๊ทผ
WebXR Device API VR/AR ๊ฒฝํ ์ ๊ณต
Web Share API ๋ค์ดํฐ๋ธ ๊ณต์ ๊ธฐ๋ฅ
Payment Request API ๊ฐํธ ๊ฒฐ์
Web Authentication API ์์ฒด ์ธ์ฆ
// Web Share API ์์ (๋ชจ๋ฐ์ผ์์ ํนํ ์ ์ฉ)
async function shareContent() {
if (navigator.share) {
try {
await navigator.share({
title: '์ฌ๋ฅ๋ท์์ ๋ฐ๊ฒฌํ ๋ฉ์ง ์ฌ๋ฅ!',
text: '์ด ์ฌ๋ฅ ๊ณต์ ์๋ฅผ ํ์ธํด๋ณด์ธ์!',
url: 'https://www.jaenung.net/talent/12345'
});
console.log('๊ณต์ ์ฑ๊ณต! ๐');
} catch (error) {
console.error('๊ณต์ ์คํจ:', error);
}
} else {
// ๋์ฒด: ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ
await navigator.clipboard.writeText(window.location.href);
alert('๋งํฌ๊ฐ ๋ณต์ฌ๋์์ต๋๋ค!');
}
}
// File System Access API ์์ (Chrome 86+)
async function saveFile() {
try {
const handle = await window.showSaveFilePicker({
suggestedName: 'my-file.txt',
types: [{
description: 'Text Files',
accept: { 'text/plain': ['.txt'] }
}]
});
const writable = await handle.createWritable();
await writable.write('Hello, World!');
await writable.close();
console.log('ํ์ผ ์ ์ฅ ์๋ฃ! ๐พ');
} catch (error) {
console.error('ํ์ผ ์ ์ฅ ์คํจ:', error);
}
}
// Web Bluetooth API ์์ (์คํ์ )
async function connectBluetooth() {
try {
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['heart_rate'] }]
});
const server = await device.gatt.connect();
console.log('๋ธ๋ฃจํฌ์ค ์ฐ๊ฒฐ๋จ:', device.name);
// ์ฌ๋ฐ์ ์๋น์ค ์ฌ์ฉ
const service = await server.getPrimaryService('heart_rate');
const characteristic = await service.getCharacteristic('heart_rate_measurement');
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const value = event.target.value;
console.log('์ฌ๋ฐ์:', value.getUint8(1));
});
await characteristic.startNotifications();
} catch (error) {
console.error('๋ธ๋ฃจํฌ์ค ์ฐ๊ฒฐ ์คํจ:', error);
}
}
๐ก ์ค๋ฌด ํ & ๋ฒ ์คํธ ํ๋ํฐ์ค
๋ง์ง๋ง์ผ๋ก ์ค๋ฌด์์ ์ ๋ง ์ค์ํ ํ๋ค์ ์ ๋ฆฌํด๋ณผ๊ฒ! ์ด๊ฑด ๊ฒฝํ์์ ๋์จ ์ง์ง ๋ ธํ์ฐ์ผ. ๐ฏ
- โ ๊ถํ ์์ฒญ์ ํ์ด์ง ๋ก๋ ์ฆ์ ํ๊ธฐ (์ฌ์ฉ์ ๊ฒฝํ ๋์จ)
- โ ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ ํ๊ณ API ํธ์ถ๋ง ํ๊ธฐ
- โ ๋ฏธ๋์ด ์คํธ๋ฆผ์ ์ข ๋ฃํ์ง ์๊ณ ๋ฐฉ์น
- โ localStorage์ ๋ฏผ๊ฐํ ์ ๋ณด ์ ์ฅ
- โ ๋ชจ๋ ๋ธ๋ผ์ฐ์ ์์ ๋์ผํ๊ฒ ์๋ํ ๊ฑฐ๋ผ ๊ฐ์
- โ API ํธ์ถ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ์ง ์๊ณ ๋งค๋ฒ ์๋ก ์์ฒญ
โจ ํญ์ try-catch๋ก ์๋ฌ ์ฒ๋ฆฌ
โจ ์ฌ์ฉ ์๋ฃ ํ ๋ฆฌ์์ค ์ ๋ฆฌ (stop, close, remove ๋ฑ)
โจ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ ์๋ฒ์ ์ ์ฅํ๊ณ ํ ํฐ๋ง ํด๋ผ์ด์ธํธ์
โจ ๊ธฐ๋ฅ ๊ฐ์ง(Feature Detection)๋ก ํธํ์ฑ ์ฒดํฌ
โจ ์ ์ ํ ์บ์ฑ ์ ๋ต์ผ๋ก ์ฑ๋ฅ ๊ฐ์
โจ ์ฌ์ฉ์์๊ฒ ๋ช ํํ ํผ๋๋ฐฑ ์ ๊ณต (๋ก๋ฉ, ์ฑ๊ณต, ์คํจ)
// ์๋ฒฝํ API ํธ์ถ ํจํด
class APIHandler {
constructor() {
this.cache = new Map();
this.loading = new Set();
}
async callAPI(endpoint, options = {}) {
const cacheKey = `${endpoint}-${JSON.stringify(options)}`;
// 1. ์บ์ ํ์ธ
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (Date.now() - cached.timestamp < 60000) { // 1๋ถ ์บ์
return cached.data;
}
}
// 2. ์ค๋ณต ์์ฒญ ๋ฐฉ์ง
if (this.loading.has(cacheKey)) {
return new Promise((resolve) => {
const checkInterval = setInterval(() => {
if (!this.loading.has(cacheKey)) {
clearInterval(checkInterval);
resolve(this.cache.get(cacheKey)?.data);
}
}, 100);
});
}
this.loading.add(cacheKey);
try {
// 3. ๋ก๋ฉ UI ํ์
this.showLoading();
// 4. API ํธ์ถ (ํ์์์ ํฌํจ)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
const response = await fetch(endpoint, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
// 5. ์๋ต ๊ฒ์ฆ
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
// 6. ์บ์ ์ ์ฅ
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
// 7. ์ฑ๊ณต ํผ๋๋ฐฑ
this.showSuccess('๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์์ต๋๋ค!');
return data;
} catch (error) {
// 8. ์๋ฌ ์ฒ๋ฆฌ
console.error('API ํธ์ถ ์คํจ:', error);
if (error.name === 'AbortError') {
this.showError('์์ฒญ ์๊ฐ์ด ์ด๊ณผ๋์์ต๋๋ค.');
} else if (!navigator.onLine) {
this.showError('์ธํฐ๋ท ์ฐ๊ฒฐ์ ํ์ธํด์ฃผ์ธ์.');
} else {
this.showError('๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋๋ฐ ์คํจํ์ต๋๋ค.');
}
throw error;
} finally {
// 9. ์ ๋ฆฌ ์์
this.loading.delete(cacheKey);
this.hideLoading();
}
}
showLoading() {
document.getElementById('loader')?.classList.add('active');
}
hideLoading() {
document.getElementById('loader')?.classList.remove('active');
}
showSuccess(message) {
this.showToast(message, 'success');
}
showError(message) {
this.showToast(message, 'error');
}
showToast(message, type) {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
}
// ์ฌ์ฉ ์์
const api = new APIHandler();
async function loadUserData() {
try {
const userData = await api.callAPI('/api/user/profile');
displayUserProfile(userData);
} catch (error) {
// ์๋ฌ๋ ์ด๋ฏธ APIHandler์์ ์ฒ๋ฆฌ๋จ
console.log('์ฌ์ฉ์ ๋ฐ์ดํฐ ๋ก๋ ์คํจ');
}
}
๐จ UX ๊ฐ์ ํ
โข ํฐ์น ์ด๋ฒคํธ ์ง์
โข ํ๋ฉด ํฌ๊ธฐ์ ๋ฐ๋ฅธ ๋ฐ์ํ ๋์์ธ
โข ๋ชจ๋ฐ์ผ ๋ฐ์ดํฐ ์ฌ์ฉ๋ ๊ณ ๋ ค
โฑ๏ธ ๋ก๋ฉ ์ํ ํ์
โข ์คํผ๋, ํ๋ก๊ทธ๋ ์ค ๋ฐ ๋ฑ ์๊ฐ์ ํผ๋๋ฐฑ
โข ์์ ์์ ์๊ฐ ํ์
โข ์ค์ผ๋ ํค UI๋ก ๋ ์ด์์ ๋ฏธ๋ฆฌ ๋ณด์ฌ์ฃผ๊ธฐ
๐ ๋ช ํํ ํผ๋๋ฐฑ
โข ์ฑ๊ณต/์คํจ ๋ฉ์์ง ํ์
โข ์๋ฌ ๋ฐ์ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ ์๋ด
โข ๊ถํ ์์ฒญ ์ ์ด์ ์ค๋ช
โฟ ์ ๊ทผ์ฑ ๊ณ ๋ ค
โข ํค๋ณด๋ ๋ค๋น๊ฒ์ด์ ์ง์
โข ์คํฌ๋ฆฐ ๋ฆฌ๋ ํธํ์ฑ
โข ์ถฉ๋ถํ ์์ ๋๋น
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐
๋ธ๋ผ์ฐ์ API์ Web API๋ ์น ๊ฐ๋ฐ์ ํต์ฌ์ด์ผ. ์ด ์น๊ตฌ๋ค์ ์ ํ์ฉํ๋ฉด ๋ค์ดํฐ๋ธ ์ฑ ๋ชป์ง์์, ์๋ ์ด์ฉ๋ฉด ๋ ๋์ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค ์ ์์ด.
๊ธฐ์ตํด, API๋ ๋๊ตฌ์ผ ๋ฟ์ด์ผ. ์ค์ํ ๊ฑด ์ด ๋๊ตฌ๋ค์ ์ด๋ป๊ฒ ์กฐํฉํด์ ์ฌ์ฉ์์๊ฒ ๊ฐ์น๋ฅผ ์ ๊ณตํ๋๋์ง. ๐
์ฒ์์ ์ด๋ ค์ ๋ณด์ผ ์ ์์ง๋ง, ํ๋์ฉ ์ค์ตํ๋ค ๋ณด๋ฉด ์ด๋์ ์์ฐ์ค๋ฝ๊ฒ ์ฌ์ฉํ๊ฒ ๋ ๊ฑฐ์ผ. ์คํจ๋ฅผ ๋๋ ค์ํ์ง ๋ง๊ณ , ๊ณ์ ์คํํ๊ณ ๋ฐฐ์๋๊ฐ!
๊ทธ๋ฆฌ๊ณ ํน์ ๋งํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด? ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํ๊ฑฐ๋, ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๊ฑฐ๋, ์๋๋ฉด ์ฌ๋ฅ๋ท์์ ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐
๐ ์ด์ ๋น์ ์ฐจ๋ก!
์ค๋ ๋ฐฐ์ด ๋ด์ฉ ์ค ํ๋๋ฅผ ๊ณจ๋ผ์
์ง์ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์!
์์ ํ๋ก์ ํธ๋ผ๋ ์์ฑํ๋ ๊ฒ ์ค์ํด.
Happy Coding! ๐ปโจ
์ด ๊ธ์ด ๋์์ด ๋์๋ค๋ฉด, ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ๋ ๊ณต์ ํด์ฃผ์ธ์! ๐ข
๋ ๋ง์ ๊ฐ๋ฐ ์ง์์ ์ฌ๋ฅ๋ท ์ง์์ธ์ ์ฒ์์ ๋ง๋๋ณด์ธ์.
๐ฒ๐ณ๐ฒ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.