์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํƒ€์ž ๊ฒŒ์ž„ ํ”„๋กœ์ ํŠธ

๐Ÿ’ก 1๋‹จ๊ณ„: ํ”„๋กœ์ ํŠธ ๊ฐœ์š” (What & Why)

ํ•ญ๋ชฉ ๋‚ด์šฉ
ํ”„๋กœ์ ํŠธ๋ช… JavaScript ๊ฐœ๋… ํƒ€์ž ๊ฒŒ์ž„ (Typing Game for JS Concept)
๋ชฉํ‘œ JavaScript ํ•ต์‹ฌ ๊ฐœ๋…์„ ํƒ€์ž ์ž…๋ ฅ๊ณผ ์‹œ๊ฐ ํ”ผ๋“œ๋ฐฑ์œผ๋กœ ๋ฐ˜๋ณต ํ•™์Šตํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•œ ์‹ค์Šตํ˜• ํ•™์Šต ๊ฒŒ์ž„ ์›น์•ฑ
๊ฐœ๋ฐœ ๋ฐฐ๊ฒฝ ๋‹จ์ˆœ ์•”๊ธฐ๋ณด๋‹ค ์ž…๋ ฅ-ํ”ผ๋“œ๋ฐฑ-์„ค๋ช… ๋ฐ˜๋ณต ๋ฃจํ”„๊ฐ€ ํ•™์Šต์— ๋” ํšจ๊ณผ์ ์ด๋ผ ํŒ๋‹จํ•˜์—ฌ, ์ง์ ‘ ๊ฒฝํ—˜ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐœ๋… ํ•™์Šตํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๋ฅผ ์ œ์ž‘

๐Ÿ“Œ ํ•ต์‹ฌ

โ€œ์ด ํ”„๋กœ์ ํŠธ๋Š” JavaScript ํ•ต์‹ฌ ๊ฐœ๋…์„ ๋ˆˆ์œผ๋กœ ๋ณด๊ณ  ์†์œผ๋กœ ์ž…๋ ฅํ•˜๋ฉฐ ๊ธฐ์–ตํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ๋งŒ๋“  ํ•™์Šต์šฉ ํƒ€์ž ๊ฒŒ์ž„์ž…๋‹ˆ๋‹ค. ์˜๋ฌธ ํƒ€์ž์˜ ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค๊ณ , โ€˜๊ณต๋ถ€ ๋ชจ๋“œโ€™์™€ โ€˜๊ฒŒ์ž„ ๋ชจ๋“œโ€™๋กœ ๋‚˜๋ˆ„์–ด ๋‹ค์–‘ํ•œ ํ•™์Šต ์Šคํƒ€์ผ์„ ์ง€์›ํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.โ€


๐Ÿ’ก 2๋‹จ๊ณ„: ์ฃผ์š” ๊ธฐ๋Šฅ ๋ฐ ์‚ฌ์šฉ ๊ธฐ์ˆ  (How)

๐Ÿ”ง ์ฃผ์š” ๊ธฐ๋Šฅ ์š”์•ฝ

๊ธฐ๋Šฅ ์„ค๋ช…
โœ… ๊ฐœ๋… ํƒ€์ดํ•‘ ๊ฒŒ์ž„ JS ์ฝ”๋“œ๊ฐ€ ํ™”๋ฉด ์œ„์—์„œ ๋–จ์–ด์ง€๊ณ , ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํƒ€์ดํ•‘ํ•˜๋ฉด ์ •๋‹ต ์ฒ˜๋ฆฌ
โœ… ๊ฐœ๋… ๋ฒ„๋ธ” ํ‘œ์‹œ ๊ฐ ์ฝ”๋“œ์— ๋Œ€ํ•œ ๊ฐœ๋… ํ’์„ ์ด ๋– ์˜ค๋ฅด๋ฉฐ ์‹œ๊ฐ์  ๊ฐœ๋… ์—ฐ๊ฒฐ
โœ… ๊ณต๋ถ€ ๋ชจ๋“œ / ๊ฒŒ์ž„ ๋ชจ๋“œ ๊ณต๋ถ€ ๋ชจ๋“œ: ๋งž์ถ”๋ฉด ํŒ์—…์œผ๋กœ ๊ฐœ๋… ์˜ˆ์ œ ์„ค๋ช…๊ฒŒ์ž„ ๋ชจ๋“œ: ์ ์ˆ˜, ๋ชฉ์ˆจ, ๋žญํฌ ์‹œ์Šคํ…œ ๋„์ž…
โœ… ํšจ๊ณผ์Œ ์‹œ์Šคํ…œ ์ •๋‹ต ํšจ๊ณผ์Œ, ์˜ค๋‹ต ํšจ๊ณผ์Œ, ๋ฐฐ๊ฒฝ์Œ ์žฌ์ƒ
โœ… ๋‚œ์ด๋„ ๋ฐ ์†๋„ ์กฐ์ ˆ ์‚ฌ์šฉ์ž๊ฐ€ ๋‚œ์ด๋„ ๋ฐ ์ฝ”๋“œ ๋–จ์–ด์ง€๋Š” ์†๋„ ์กฐ์ ˆ ๊ฐ€๋Šฅ
โœ… ์ž๋™ ์ผ์‹œ์ •์ง€ ํƒญ ์ดํƒˆ ์‹œ ์ž๋™ ์ •์ง€, ๋ณต๊ท€ ์‹œ ์ž๋™ ์žฌ๊ฐœ ์ฒ˜๋ฆฌ

๐Ÿ› ๏ธ ์‚ฌ์šฉ ๊ธฐ์ˆ  ์Šคํƒ


๐Ÿ’ก 3๋‹จ๊ณ„: ๋ฌธ์ œ ์ƒํ™ฉ๊ณผ ํ•ด๊ฒฐ ๊ณผ์ •

๋ฌธ์ œ ์ƒํ™ฉ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
๐Ÿ”‡ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ž๋™ ์˜ค๋””์˜ค ์žฌ์ƒ ์ฐจ๋‹จ๋จ ํด๋ฆญ/ํ‚ค๋ณด๋“œ ์ƒํ˜ธ์ž‘์šฉ ์ดํ›„ audio.play() ํ˜ธ์ถœ๋กœ ์žฌ์ƒ ์œ ๋„
โธ๏ธ ์‚ฌ์šฉ์ž๊ฐ€ ํƒญ ์ด๋™ ์‹œ์—๋„ ์ฝ”๋“œ๊ฐ€ ๊ณ„์† ๋–จ์–ด์ง window.blur, window.focus ์ด๋ฒคํŠธ๋กœ ์ฝ”๋“œ ๋‚™ํ•˜ ์ž๋™ ์ผ์‹œ์ •์ง€/์žฌ๊ฐœ ์ฒ˜๋ฆฌ
๐ŸŽฏ ์ •๋‹ต์„ ์ž…๋ ฅํ•ด๋„ ์ž…๋ ฅ ํƒ€์ด๋ฐ๊ณผ ๋‚™ํ•˜ ํƒ€์ด๋ฐ์ด ์–ด๊ธ‹๋‚˜ ์˜ค๋‹ต ์ฒ˜๋ฆฌ๋จ getBoundingClientRect๋กœ ์ •ํ™•ํ•œ ์œ„์น˜ ๋น„๊ต ํ›„ ๋‚™ํ•˜ ์‹œ์  ํŒ๋‹จ
๐ŸŽฎ ์˜ค๋‹ต์ด๊ฑฐ๋‚˜ ์ž๋™ ๋‚™ํ•˜๋œ ์ฝ”๋“œ์—๋„ ํšจ๊ณผ์Œ ์ฒ˜๋ฆฌ ํ•„์š” ์ •๋‹ต/์˜ค๋‹ต ํƒ€์ด๋ฐ ๊ตฌ๋ถ„ํ•˜์—ฌ playHitSound() ๋˜๋Š” playMissSound() ๋ถ„๋ฆฌ ํ˜ธ์ถœ
๐Ÿง  ๊ณต๋ถ€ ๋ชจ๋“œ์—์„œ ์„ค๋ช… ํŒ์—…์ด ๋‚˜์™€๋„ ์ฝ”๋“œ๊ฐ€ ๊ณ„์† ๋–จ์–ด์ง ์ผ์‹œ์ •์ง€ ์ฒ˜๋ฆฌ(.paused), ํŒ์—…์ด ๊บผ์ง„ ํ›„ startDropping()์œผ๋กœ ์žฌ๊ฐœ

๐Ÿ’ก ๊ธฐ๋Šฅ๋ณ„ ์ฝ”๋“œ ๋ถ„์„


โœ… 1. ์ƒ๋ช… ์‹œ์Šคํ…œ: updateLivesDisplay()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ script ๋‚ด๋ถ€, ์ƒ๋‹จ ์•ฝ 500์ค„ ๋ถ€๊ทผ
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
function updateLivesDisplay() {
  lifeBar.innerHTML = '';
  for (let i = 0; i < lives; i++) {
    const heart = document.createElement('div');
    heart.textContent = 'โค๏ธ';
    lifeBar.appendChild(heart);
  }
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 2. ๋žญํฌ ์‹œ์Šคํ…œ: rankLevels

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ script ๋‚ด๋ถ€, ์•ฝ 540์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
const rankLevels = [
  { threshold: 0, speed: 40000, rank: '๐ŸŸซ ๋ธŒ๋ก ์ฆˆ' },
  { threshold: 10, speed: 33000, rank: '๐ŸŸช ์‹ค๋ฒ„' },
  { threshold: 30, speed: 28000, rank: '๐ŸŸจ ๊ณจ๋“œ' },
  { threshold: 60, speed: 20000, rank: '๐ŸŸฉ ํ”Œ๋ ˆํ‹ฐ๋„˜' },
  { threshold: 100, speed: 12000, rank: '๐ŸŸฆ ๋‹ค์ด์•„' },
  { threshold: 150, speed: 6000, rank: '๐Ÿ”ถ ๋งˆ์Šคํ„ฐ' },
  { threshold: 200, speed: 2000, rank: '๐Ÿ”ท ์ฑŒ๋ฆฐ์ €' },
];
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 3. ๋žญํฌ ๊ฐฑ์‹  ํ•จ์ˆ˜: updateRankByScore()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ script ํ•˜๋‹จ๋ถ€ ์•ฝ 1560์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
function updateRankByScore() {
  for (let i = rankLevels.length - 1; i >= 0; i--) {
    if (score >= rankLevels[i].threshold && currentLevel !== i) {
      currentLevel = i;
      dropSpeed = rankLevels[i].speed;
      rankDisplay.textContent = `๋žญํฌ: ${rankLevels[i].rank}`;
      startDropping(); // ์ƒˆ ์†๋„ ์ ์šฉ
      break;
    }
  }
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 4. ๊ฒŒ์ž„ ์˜ค๋ฒ„ ์ฒ˜๋ฆฌ: showGameOverPopup()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ script ํ•˜๋‹จ ์•ฝ 1500์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
function showGameOverPopup() {
  let tier = '';
  if (score >= 200) tier = '๐Ÿ”ท ์ฑŒ๋ฆฐ์ €';
  else if (score >= 150) tier = '๐Ÿ”ถ ๋งˆ์Šคํ„ฐ';
  else if (score >= 100) tier = '๐ŸŸฆ ๋‹ค์ด์•„';
  else if (score >= 60) tier = '๐ŸŸฉ ํ”Œ๋ ˆํ‹ฐ๋„˜';
  else if (score >= 30) tier = '๐ŸŸจ ๊ณจ๋“œ';
  else if (score >= 10) tier = '๐ŸŸช ์‹ค๋ฒ„';
  else tier = '๐ŸŸซ ๋ธŒ๋ก ์ฆˆ';

  gameoverMessage.innerHTML = `
    ๐Ÿ’€ ๊ฒŒ์ž„ ์˜ค๋ฒ„!<br>
    ๋งž์ถ˜ ๊ฐœ์ˆ˜: <strong>${score}๊ฐœ</strong><br>
    ๋‹น์‹ ์˜ ํ‹ฐ์–ด: <strong>${tier}</strong>
  `;
  gameoverPopup.style.display = 'block';
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 5. ์ฝ”๋“œ ๋–จ์–ด๋œจ๋ฆฌ๊ธฐ ์‹œ์ž‘: startDropping()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ script ์ค‘๊ฐ„, ์•ฝ 620์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
function startDropping() {
  if (intervalId) clearInterval(intervalId); // ์ค‘๋ณต ๋ฐฉ์ง€
  intervalId = setInterval(dropCode, dropInterval); // ์ผ์ • ๊ฐ„๊ฒฉ๋งˆ๋‹ค dropCode ์‹คํ–‰

  if (gameMode === 'game') {
    updateLivesDisplay(); // ๊ฒŒ์ž„ ๋ชจ๋“œ์—์„œ๋Š” ํ•˜ํŠธ ํ‘œ์‹œ
  }
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 6. ๋–จ์–ด์ง€๋Š” ์ฝ”๋“œ ์ƒ์„ฑ: dropCode()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ script ์ค‘๊ฐ„, ์•ฝ 630์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ (์š”์•ฝ) ย 
const snippet = codeSnippets[Math.floor(Math.random() * codeSnippets.length)];
const codeEl = document.createElement('div');
codeEl.classList.add('falling-code');
codeEl.textContent = snippet.code;
codeEl.dataset.concept = snippet.concept;
codeEl.style.left = Math.random() * 80 + '%';
codeEl.style.animationDuration = dropSpeed + 'ms';
game.appendChild(codeEl);
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 7. ์ž…๋ ฅ ์ฒ˜๋ฆฌ ๋ฐ ์ •๋‹ต ํŒ๋ณ„: keydown ์ด๋ฒคํŠธ

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ userInput.addEventListener('keydown', ...), ์•ฝ 970์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
if (e.key === 'Enter') {
  const typed = userInput.value.trim();
  for (let el of fallingCodes) {
    if (el.textContent === typed) {
      playHitSound();
      removeFromQueue(el.dataset.concept);
      score++;
      updateRankByScore();
      pauseGameForExam(matchedSnippet);
    }
  }
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 8. ์ •๋‹ต ๊ฐœ๋… ๊ฐ•์กฐ: highlightConcept(concept)

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ ์•ฝ 1070์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
function highlightConcept(concept) {
  const allConcepts = document.querySelectorAll('.concept');
  allConcepts.forEach(el => {
    el.classList.remove('highlight');
    if (el.dataset.concept === concept) {
      el.classList.add('highlight');
    }
  });
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 9. ์ •๋‹ต ์ดํŽ™ํŠธ ํ‘œ์‹œ: showCorrectEffectAtPosition()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ ์•ฝ 1580์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
function showCorrectEffectAtPosition(el) {
  const clone = el.cloneNode(true);
  clone.classList.add('correct-hit');
  clone.style.position = 'fixed';
  clone.style.top = rect.top + 'px';
  clone.style.left = rect.left + 'px';
  ...
  document.body.appendChild(clone);
  setTimeout(() => { clone.remove(); }, 500);
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 10. ๊ฐœ๋… ์„ค๋ช… ํŒ์—… + ์ผ์‹œ์ •์ง€: pauseGameForExam(snippet)

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ ์•ฝ 1120์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ์š”์•ฝ ย 
if (gameMode === 'study') {
  clearInterval(intervalId); // ์ฝ”๋“œ ๋–จ์–ด์ง€๊ธฐ ๋ฉˆ์ถค
  examPopup.innerHTML = `
    <div class="exam-title">๐Ÿ“˜๊ฐœ๋…:${snippet.concept}</div>
    <pre>${snippet.exam}</pre>
    <div class="exam-note">โธ๏ธ Enter ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊ณ„์† ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค!</div>`;
  examPopup.style.display = 'block';
  waitingForResume = true;
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 11. ํšจ๊ณผ์Œ ์žฌ์ƒ ํ•จ์ˆ˜: playHitSound(), playMissSound()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ <script> ์ œ์ผ ์œ„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ย 
function playHitSound() {
  hitSound.currentTime = 0;
  hitSound.play().catch(err => console.warn("์ •๋‹ต ํšจ๊ณผ์Œ ์‹คํŒจ:", err));
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 12. ๋ฐฐ๊ฒฝ์Œ์•… ์ž๋™ ์žฌ์ƒ ์ œ์–ด: tryPlayBGM()

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ ์•ฝ 1620์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ์š”์•ฝ ย 
function tryPlayBGM() {
  if (!bgmStarted) {
    bgm.play().then(() => {
      bgmStarted = true;
    }).catch(err => console.warn("BGM ์‹คํŒจ", err));
  }
}
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 13. ํƒญ ์ดํƒˆ ๊ฐ์ง€ (์ž๋™ ์ผ์‹œ์ •์ง€/์žฌ๊ฐœ)

ํ•ญ๋ชฉ ๋‚ด์šฉ
โœ… ์ฝ”๋“œ ์œ„์น˜ ์•ฝ 1650์ค„
๐Ÿง  ์ฝ”๋“œ ๋‚ด์šฉ ์š”์•ฝ ย 
window.addEventListener('blur', () => {
  clearInterval(intervalId); // ๋–จ์–ด์ง€๊ธฐ ๋ฉˆ์ถค
  document.querySelectorAll('.falling-code').forEach(el => el.classList.add('paused'));
});

window.addEventListener('focus', () => {
  if (!intervalId) startDropping(); // ๋‹ค์‹œ ์‹œ์ž‘
  document.querySelectorAll('.falling-code').forEach(el => el.classList.remove('paused'));
});
๐ŸŽฏ ์„ค๊ณ„ ์˜๋„

โœ… 14. ์ „์ฒด ๊ตฌ์กฐ ์š”์•ฝ

๊ตฌ์„ฑ ์š”์†Œ ์„ค๋ช…
๐Ÿงก ์ƒ๋ช… ์‹œ์Šคํ…œ lives, updateLivesDisplay() โ€” ๋–จ์–ด๋œจ๋ฆฌ๋ฉด ํ•˜ํŠธ ์ฐจ๊ฐ
๐Ÿ† ๋žญํฌ ์‹œ์Šคํ…œ score, rankLevels, updateRankByScore() โ€” ์ ์ˆ˜ ๊ธฐ์ค€์œผ๋กœ ํ‹ฐ์–ด/์†๋„ ์ƒ์Šน
โŒจ๏ธ ์ž…๋ ฅ ๊ฒ€์‚ฌ keydown ์ด๋ฒคํŠธ โ€” ์ •๋‹ต์ด๋ฉด ๊ฐœ๋… ๊ฐ•์กฐ + ์„ค๋ช… ํŒ์—…
๐ŸŽˆ ๊ฐœ๋… ๋ฒ„๋ธ” renderConcepts() โ€” ํ˜„์žฌ ํ™”๋ฉด์— ๋œฌ ์ฝ”๋“œ๋“ค์˜ ๊ฐœ๋… ๋ฒ„๋ธ” ํ‘œ์‹œ
๐ŸŽฎ ๋ชจ๋“œ ๊ตฌ๋ถ„ ๊ณต๋ถ€ ๋ชจ๋“œ vs ๊ฒŒ์ž„ ๋ชจ๋“œ โ†’ ์ผ์‹œ์ •์ง€/ํ•˜ํŠธ ํ‘œ์‹œ ๋ฐฉ์‹ ์ฐจ์ด
๐Ÿ”‡ ์‚ฌ์šด๋“œ ํšจ๊ณผ ์ •๋‹ต/์˜ค๋‹ต/๋ฐฐ๊ฒฝ์Œ ๋ชจ๋‘ try-catch๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
๐Ÿ” ์ž๋™ ์žฌ๊ฐœ ํƒญ ์ „ํ™˜ ๊ฐ์ง€๋กœ ์ฝ”๋“œ ๋–จ์–ด๋œจ๋ฆฌ๊ธฐ ์ผ์‹œ์ •์ง€/์žฌ์‹œ์ž‘ ์ฒ˜๋ฆฌ

๐Ÿ’ก 4๋‹จ๊ณ„: ๊ฒฐ๊ณผ ๋ฐ ์„ฑ๊ณผ (Result)

๐ŸŽฏ ๊ตฌํ˜„ ์™„๋ฃŒ ๋ชฉ๋ก

โœ… ์„ฑ๊ณผ ์š”์•ฝ ๋ฌธ์žฅ

โ€œ์‹ค์ œ ์‚ฌ์šฉ์ž์™€ ํ…Œ์ŠคํŠธํ•˜๋ฉฐ ์‹œ๊ฐ์  ๋ชฐ์ž…๊ฐ, ํƒ€์ดํ•‘ ๋ฐ˜์‘์„ฑ, ๊ฐœ๋… ํ•™์Šต ํ”ผ๋“œ๋ฐฑ์„ ๋ชจ๋‘ ๊ณ ๋ คํ•œ ์‹ค์šฉ์  ์›น ํ•™์Šต ๋„๊ตฌ๋ฅผ ์™„์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.โ€


๐Ÿ’ก 5๋‹จ๊ณ„: ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ ๋ฐ ๊ฐœ์„  ๋ฐฉํ–ฅ (Future Plan)

๊ฐœ์„  ํ•ญ๋ชฉ ์„ค๋ช…
๐Ÿ“ฆ ๋ฌธ์ œ ๋ฐ์ดํ„ฐ ์™ธ๋ถ€ํ™” JSON ๋˜๋Š” Firebase ๋“ฑ ์™ธ๋ถ€ API ์—ฐ๋™ ๊ตฌ์กฐ๋กœ ๋ณ€๊ฒฝ
๐Ÿง  ๊ฐœ๋… ๋ฐ์ดํ„ฐ ํ™•์žฅ JavaScript ์™ธ์—๋„ HTML, CSS, Python ๋“ฑ์œผ๋กœ ํ™•์žฅ ๊ฐ€๋Šฅ
๐Ÿง‘โ€๐ŸŽ“ ๋ฆฌ๋”๋ณด๋“œ ๋„์ž… ์ ์ˆ˜ ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ๋žญํ‚น ์‹œ์Šคํ…œ ๊ตฌ์ถ• (DB ์—ฐ๋™ ํ•„์š”)
๐Ÿ“ฑ ๋ชจ๋ฐ”์ผ ์ตœ์ ํ™” ๋ชจ๋ฐ”์ผ ์„ธ๋กœ ๋ชจ๋“œ ๋Œ€์‘ ๋ ˆ์ด์•„์›ƒ ์„ค๊ณ„ ํ•„์š”
๐Ÿ’ก ์ฝ”๋“œ ํ•˜์ด๋ผ์ดํŒ… highlight.js ๋“ฑ์„ ์ด์šฉํ•ด ์ฝ”๋“œ ๋‹จ์–ด ๊ฐ•์กฐ