์๋ฐ์คํฌ๋ฆฝํธ ํ์ ๊ฒ์ ํ๋ก์ ํธ
๐ก 1๋จ๊ณ: ํ๋ก์ ํธ ๊ฐ์ (What & Why)
ํญ๋ชฉ |
๋ด์ฉ |
ํ๋ก์ ํธ๋ช
|
JavaScript ๊ฐ๋
ํ์ ๊ฒ์ (Typing Game for JS Concept) |
๋ชฉํ |
JavaScript ํต์ฌ ๊ฐ๋
์ ํ์ ์
๋ ฅ๊ณผ ์๊ฐ ํผ๋๋ฐฑ์ผ๋ก ๋ฐ๋ณต ํ์ตํ ์ ์๋๋ก ๊ตฌ์ฑํ ์ค์ตํ ํ์ต ๊ฒ์ ์น์ฑ |
๊ฐ๋ฐ ๋ฐฐ๊ฒฝ |
๋จ์ ์๊ธฐ๋ณด๋ค ์
๋ ฅ-ํผ๋๋ฐฑ-์ค๋ช
๋ฐ๋ณต ๋ฃจํ๊ฐ ํ์ต์ ๋ ํจ๊ณผ์ ์ด๋ผ ํ๋จํ์ฌ, ์ง์ ๊ฒฝํ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ๋
ํ์ตํ ์ ์๋ ๋๊ตฌ๋ฅผ ์ ์ |
๐ ํต์ฌ
โ์ด ํ๋ก์ ํธ๋ JavaScript ํต์ฌ ๊ฐ๋
์ ๋์ผ๋ก ๋ณด๊ณ ์์ผ๋ก ์
๋ ฅํ๋ฉฐ ๊ธฐ์ตํ ์ ์๊ฒ๋ ๋ง๋ ํ์ต์ฉ ํ์ ๊ฒ์์
๋๋ค. ์๋ฌธ ํ์์ ์๋๋ฅผ ํฅ์์ํค๊ณ , โ๊ณต๋ถ ๋ชจ๋โ์ โ๊ฒ์ ๋ชจ๋โ๋ก ๋๋์ด ๋ค์ํ ํ์ต ์คํ์ผ์ ์ง์ํ๋๋ก ์ค๊ณํ์ต๋๋ค.โ
๐ก 2๋จ๊ณ: ์ฃผ์ ๊ธฐ๋ฅ ๋ฐ ์ฌ์ฉ ๊ธฐ์ (How)
๐ง ์ฃผ์ ๊ธฐ๋ฅ ์์ฝ
๊ธฐ๋ฅ |
์ค๋ช
|
โ
๊ฐ๋
ํ์ดํ ๊ฒ์ |
JS ์ฝ๋๊ฐ ํ๋ฉด ์์์ ๋จ์ด์ง๊ณ , ํด๋น ์ฝ๋๋ฅผ ํ์ดํํ๋ฉด ์ ๋ต ์ฒ๋ฆฌ |
โ
๊ฐ๋
๋ฒ๋ธ ํ์ |
๊ฐ ์ฝ๋์ ๋ํ ๊ฐ๋
ํ์ ์ด ๋ ์ค๋ฅด๋ฉฐ ์๊ฐ์ ๊ฐ๋
์ฐ๊ฒฐ |
โ
๊ณต๋ถ ๋ชจ๋ / ๊ฒ์ ๋ชจ๋ |
๊ณต๋ถ ๋ชจ๋: ๋ง์ถ๋ฉด ํ์
์ผ๋ก ๊ฐ๋
์์ ์ค๋ช
๊ฒ์ ๋ชจ๋: ์ ์, ๋ชฉ์จ, ๋ญํฌ ์์คํ
๋์
|
โ
ํจ๊ณผ์ ์์คํ
|
์ ๋ต ํจ๊ณผ์, ์ค๋ต ํจ๊ณผ์, ๋ฐฐ๊ฒฝ์ ์ฌ์ |
โ
๋์ด๋ ๋ฐ ์๋ ์กฐ์ |
์ฌ์ฉ์๊ฐ ๋์ด๋ ๋ฐ ์ฝ๋ ๋จ์ด์ง๋ ์๋ ์กฐ์ ๊ฐ๋ฅ |
โ
์๋ ์ผ์์ ์ง |
ํญ ์ดํ ์ ์๋ ์ ์ง, ๋ณต๊ท ์ ์๋ ์ฌ๊ฐ ์ฒ๋ฆฌ |
๐ ๏ธ ์ฌ์ฉ ๊ธฐ์ ์คํ
- HTML/CSS: ์๊ฐ์ ๊ตฌ์ฑ ๋ฐ ์คํ์ผ๋ง (๋ฒ ์ด์งํค ๋์์ธ, ์ ๋๋ฉ์ด์
ํฌํจ)
- JavaScript (Vanilla): DOM ์กฐ์, ์ด๋ฒคํธ ์ฒ๋ฆฌ, ์ ๋๋ฉ์ด์
์ ์ด, ํ์ด๋จธ ๋ฑ
- Web Audio API: ์ ๋ต/์ค๋ต ํจ๊ณผ์, ๋ฐฐ๊ฒฝ์ ์ฌ์
- CSS Animation: ์ฝ๋ ๋ํ ์ ๋๋ฉ์ด์
, ๊ฐ๋
๊ฐ์กฐ ์ดํํธ ๊ตฌํ
๐ก 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);
}
}
- ํ์ฌ ์๋ช
์(
lives
)๋งํผ ํํธ๋ฅผ ๊ทธ๋ ค์ค
- ์๋ก๊ณ ์นจ ์ ์๋ ๋ ๋๋ง๋๋๋ก ์ค๊ณ
- ์ฝ๋๊ฐ ๋จ์ด์ ธ์ ์๋ช
์ ์์ผ๋ฉด ์ด ํจ์๋ฅผ ํธ์ถํด ์๊ฐ์ ์ผ๋ก ์๋ช
๋ณํ ํํ
โ
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: '๐ท ์ฑ๋ฆฐ์ ' },
];
- ์ ์(
score
)์ ๋ฐ๋ผ ๋จ๊ณ์ ์ผ๋ก ๋ญํฌ ๋ฐ ์ ๋๋ฉ์ด์
์๋ ๋ณ๊ฒฝ
updateRankByScore()
ํจ์์ ์ฐ๋๋์ด ์๋ ๊ฐฑ์
- ์ ์ ์ ์ฑ์ทจ๊ฐ์ ์๊ฐ์ ์ผ๋ก ํํ (๊ฒ์ด๋ฏธํผ์ผ์ด์
์์ ๊ฐํ)
โ
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;
}
}
}
- ์ ์ ๋ณํ โ ๋ญํฌ ๋ณํ โ ์๋ ๋ณํ๋ผ๋ ์ฐ๊ฒฐ ๊ตฌ์กฐ ๊ตฌ์ฑ
- ํํฅ ๋ญํฌ๋ ํ์ฉํ์ง ์๋๋ก
currentLevel !== i
์กฐ๊ฑด
- ์ฝ๋ ๋์ด๋์ ๋ฐ๋ผ ์ค์๊ฐ ๋์ด๋ ์กฐ์ ์๋ํ
ํญ๋ชฉ |
๋ด์ฉ |
โ
์ฝ๋ ์์น |
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';
}
- ์๋ช
(โค๏ธ)์ด 0์ด ๋๋ฉด ์ด ํจ์ ํธ์ถ
- ์ ์ ๊ธฐ๋ฐ ์ต์ข
๋ญํฌ ๊ณ์ฐ โ ๊ฒ์ ์ค๋ฒ ํ์
ํ์
- ์ต์ข
๋ญํฌ ๋ณด์ ํผ๋๋ฐฑ ์ ๊ณต์ผ๋ก ์ฌ์ฉ์ ๋ชฐ์
๋ ํฅ์
โ
5. ์ฝ๋ ๋จ์ด๋จ๋ฆฌ๊ธฐ ์์: startDropping()
ํญ๋ชฉ |
๋ด์ฉ |
โ
์ฝ๋ ์์น |
script ์ค๊ฐ, ์ฝ 620์ค |
๐ง ์ฝ๋ ๋ด์ฉ |
ย |
function startDropping() {
if (intervalId) clearInterval(intervalId); // ์ค๋ณต ๋ฐฉ์ง
intervalId = setInterval(dropCode, dropInterval); // ์ผ์ ๊ฐ๊ฒฉ๋ง๋ค dropCode ์คํ
if (gameMode === 'game') {
updateLivesDisplay(); // ๊ฒ์ ๋ชจ๋์์๋ ํํธ ํ์
}
}
- ์ผ์ ์ฃผ๊ธฐ๋ง๋ค ์ฝ๋ ๋จ์ด๋จ๋ฆฌ๊ธฐ (
setInterval
)
- ๊ฒ์ ๋ชจ๋์ผ ๊ฒฝ์ฐ์๋ ์๋ช
(ํํธ)๋ ํจ๊ป ํ์
- ๊ธฐ์กด ํ์ด๋จธ ์ ๊ฑฐ โ ์ ํ์ด๋จธ ์์์ ๋ฐ๋ณต ๊ฐ๋ฅํ๊ฒ ๊ตฌ์ฑ
โ
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);
์ฝ๋ + ๊ฐ๋
์ธํธ๋ฅผ DOM ์์๋ก ๋ง๋ค์ด ๋จ์ด๋จ๋ฆผ
- ์ข์ฐ ๋๋ค ์์น ์ง์ ์ผ๋ก ๊ฒ์์ฑ ์ฆ๊ฐ
- ์ ๋๋ฉ์ด์
์๋๋ ํ์ฌ ๋ญํฌ์
dropSpeed
๋ฅผ ๋ฐ์
- ํ์ ์ถ๊ฐํ์ฌ ๊ฐ๋
ํ์ ๊ณผ์ ์ฐ๊ฒฐ ์ ์ง
โ
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);
}
}
}
- ์ฌ์ฉ์๊ฐ Enter ํค๋ฅผ ๋๋ฅด๋ฉด
ํ์ฌ ์
๋ ฅ๊ฐ
์ ์ ๋ต ์ฝ๋๋ค๊ณผ ๋น๊ต
- ์ ํํ ๋ง์ผ๋ฉด:
- ์ ๋ต ์ฌ์ด๋ ์ฌ์
- ํด๋น ๊ฐ๋
ํ์ ์ ๊ฑฐ
- ์ ์ ์ฆ๊ฐ ๋ฐ ๋ญํฌ ์
๋ฐ์ดํธ
- ๊ฐ๋
์ค๋ช
ํ์
ํ์ (๋ชจ๋๋ณ ๋ถ๊ธฐ ์ฒ๋ฆฌ)
โ
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);
}
- ์ฝ๋ ๋ธ๋ก์ ์ ๋ต ์ฒ๋ฆฌํ์ ๋ ์๊ฐ์ ํ ์ ๋๋ฉ์ด์
์ถ๊ฐ
- ๋ณต์ ๋ ์์๋ฅผ
ํ๋ฉด ๊ณ ์ (fixed)
์ํ๋ก ๋ณด์ฌ์ค์ ์์ํ ํผ๋๋ฐฑ ์ ๊ณต
โ
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;
}
- *๊ณต๋ถ ๋ชจ๋(๐)**์์๋ ์ ๋ต์ ๋ง์ท์ ๋:
- ์ฝ๋๋ฅผ ์ ๊น ๋ฉ์ถค
- ์ค๋ช
ํ์
๋์
Enter
๋๋ฅผ ๋๊น์ง ๊ธฐ๋ค๋ฆผ
- *๊ฒ์ ๋ชจ๋(๐ฎ)**์์๋ ์๋์ผ๋ก 5์ด๊ฐ ์ค๋ช
๋ณด์ฌ์ฃผ๊ณ ๋ฐ๋ก ์ฌ๊ฐ
โ
11. ํจ๊ณผ์ ์ฌ์ ํจ์: playHitSound()
, playMissSound()
ํญ๋ชฉ |
๋ด์ฉ |
โ
์ฝ๋ ์์น |
<script> ์ ์ผ ์ |
๐ง ์ฝ๋ ๋ด์ฉ |
ย |
function playHitSound() {
hitSound.currentTime = 0;
hitSound.play().catch(err => console.warn("์ ๋ต ํจ๊ณผ์ ์คํจ:", err));
}
- ์ ๋ต/์ค๋ต์ ๋ฐ๋ผ ํจ๊ณผ์์ด ์ ํํ ํ์ด๋ฐ์ ์ฌ์๋๋๋ก ํจ
currentTime = 0
์ผ๋ก ํญ์ ๋งจ ์๋ถํฐ ์์
- ๋ชจ๋ฐ์ผ/๋ธ๋ผ์ฐ์ ์ ํ ์ ์คํจํด๋ ์๋ฌ ์ ๋๊ฒ
catch
์ฒ๋ฆฌ
โ
12. ๋ฐฐ๊ฒฝ์์
์๋ ์ฌ์ ์ ์ด: tryPlayBGM()
ํญ๋ชฉ |
๋ด์ฉ |
โ
์ฝ๋ ์์น |
์ฝ 1620์ค |
๐ง ์ฝ๋ ๋ด์ฉ ์์ฝ |
ย |
function tryPlayBGM() {
if (!bgmStarted) {
bgm.play().then(() => {
bgmStarted = true;
}).catch(err => console.warn("BGM ์คํจ", err));
}
}
- ๋ธ๋ผ์ฐ์ ์๋ ์ฌ์ ์ฐจ๋จ ํํผ
- ์ฌ์ฉ์ ํด๋ฆญ ๋๋ ํค์
๋ ฅ ์
tryPlayBGM()
ํธ์ถ โ BGM ์ฌ์ ์๋
- ํ ๋ฒ๋ง ์ฌ์๋๋๋ก
bgmStarted
๋ก ์ ์ด
โ
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'));
});
- ์ฌ์ฉ์๊ฐ ๋ค๋ฅธ ํญ์ผ๋ก ์ด๋ํ๊ฑฐ๋, ๋ค์ ๋์์ค๋ฉด ์๋์ผ๋ก ๋ฉ์ท๋ค ์ฌ๊ฐ
- ๋ชจ๋ ๋จ์ด์ง๋ ์ฝ๋๋
paused
ํด๋์ค ์ถ๊ฐ/์ญ์ ๋ก ์ ๋๋ฉ์ด์
์ผ์์ ์ง ์ฒ๋ฆฌ
โ
14. ์ ์ฒด ๊ตฌ์กฐ ์์ฝ
๊ตฌ์ฑ ์์ |
์ค๋ช
|
๐งก ์๋ช
์์คํ
|
lives , updateLivesDisplay() โ ๋จ์ด๋จ๋ฆฌ๋ฉด ํํธ ์ฐจ๊ฐ |
๐ ๋ญํฌ ์์คํ
|
score , rankLevels , updateRankByScore() โ ์ ์ ๊ธฐ์ค์ผ๋ก ํฐ์ด/์๋ ์์น |
โจ๏ธ ์
๋ ฅ ๊ฒ์ฌ |
keydown ์ด๋ฒคํธ โ ์ ๋ต์ด๋ฉด ๊ฐ๋
๊ฐ์กฐ + ์ค๋ช
ํ์
|
๐ ๊ฐ๋
๋ฒ๋ธ |
renderConcepts() โ ํ์ฌ ํ๋ฉด์ ๋ฌ ์ฝ๋๋ค์ ๊ฐ๋
๋ฒ๋ธ ํ์ |
๐ฎ ๋ชจ๋ ๊ตฌ๋ถ |
๊ณต๋ถ ๋ชจ๋ vs ๊ฒ์ ๋ชจ๋ โ ์ผ์์ ์ง/ํํธ ํ์ ๋ฐฉ์ ์ฐจ์ด |
๐ ์ฌ์ด๋ ํจ๊ณผ |
์ ๋ต/์ค๋ต/๋ฐฐ๊ฒฝ์ ๋ชจ๋ try-catch ๋ก ์์ ํ๊ฒ ์ฒ๋ฆฌ |
๐ ์๋ ์ฌ๊ฐ |
ํญ ์ ํ ๊ฐ์ง๋ก ์ฝ๋ ๋จ์ด๋จ๋ฆฌ๊ธฐ ์ผ์์ ์ง/์ฌ์์ ์ฒ๋ฆฌ |
๐ก 4๋จ๊ณ: ๊ฒฐ๊ณผ ๋ฐ ์ฑ๊ณผ (Result)
๐ฏ ๊ตฌํ ์๋ฃ ๋ชฉ๋ก
- ๊ณต๋ถ/๊ฒ์ ๋ชจ๋ ๊ธฐ๋ฅ ์์ ๋ถ๋ฆฌ ๊ตฌํ
- ๋์ด๋ 3๋จ๊ณ (easy, medium, hard), ์๋ 7๋จ๊ณ ์กฐ์
- ์ด 7๋จ๊ณ์ ๋ญํฌ ํฐ์ด (๋ธ๋ก ์ฆ โ ์ฑ๋ฆฐ์ ) ์์คํ
๊ตฌํ
- ํ์
๋ฐฉ์์ ๊ฐ๋
์ค๋ช
, ์ ๋๋ฉ์ด์
๊ฐ์กฐ ํจ๊ณผ๊น์ง ์๋น
- ํจ๊ณผ์, ๋ฐฐ๊ฒฝ์, ํ์ด๋ฐ ์ ์ด, ์ํ ๊ด๋ฆฌ ๋ฑ ์์ ํ ์ธํฐ๋์
UX ๊ตฌํ
โ
์ฑ๊ณผ ์์ฝ ๋ฌธ์ฅ
โ์ค์ ์ฌ์ฉ์์ ํ
์คํธํ๋ฉฐ ์๊ฐ์ ๋ชฐ์
๊ฐ, ํ์ดํ ๋ฐ์์ฑ, ๊ฐ๋
ํ์ต ํผ๋๋ฐฑ์ ๋ชจ๋ ๊ณ ๋ คํ ์ค์ฉ์ ์น ํ์ต ๋๊ตฌ๋ฅผ ์์ฑํ์์ต๋๋ค.โ
๐ก 5๋จ๊ณ: ํ์ฅ ๊ฐ๋ฅ์ฑ ๋ฐ ๊ฐ์ ๋ฐฉํฅ (Future Plan)
๊ฐ์ ํญ๋ชฉ |
์ค๋ช
|
๐ฆ ๋ฌธ์ ๋ฐ์ดํฐ ์ธ๋ถํ |
JSON ๋๋ Firebase ๋ฑ ์ธ๋ถ API ์ฐ๋ ๊ตฌ์กฐ๋ก ๋ณ๊ฒฝ |
๐ง ๊ฐ๋
๋ฐ์ดํฐ ํ์ฅ |
JavaScript ์ธ์๋ HTML, CSS, Python ๋ฑ์ผ๋ก ํ์ฅ ๊ฐ๋ฅ |
๐งโ๐ ๋ฆฌ๋๋ณด๋ ๋์
|
์ ์ ๊ธฐ๋ฐ ์ค์๊ฐ ๋ญํน ์์คํ
๊ตฌ์ถ (DB ์ฐ๋ ํ์) |
๐ฑ ๋ชจ๋ฐ์ผ ์ต์ ํ |
๋ชจ๋ฐ์ผ ์ธ๋ก ๋ชจ๋ ๋์ ๋ ์ด์์ ์ค๊ณ ํ์ |
๐ก ์ฝ๋ ํ์ด๋ผ์ดํ
|
highlight.js ๋ฑ์ ์ด์ฉํด ์ฝ๋ ๋จ์ด ๊ฐ์กฐ |