본문 바로가기

풀스택 개발/간단 개발 실습

[JS] 부딪히며 만들어보는 타자 연습 (Eng Ver.)

수업+구글링+유튜브로 따라해보고 css로 레이아웃을 좀 다듬었다.

 

(단어당 3초 이내로 입력하게 설정, 3초가 지날 경우 즉시 종료)

 

코드 요약 설명

const GAME_TIME = 3;
let score = 0;
let time = GAME_TIME;
let isPlaying = false;
let timeInterval;
let words = [];
let checkInterval;
const wordInput = document.querySelector('.word-input');
const wordDisplay = document.querySelector('.word-display');
const scoreDisplay = document.querySelector('.score');
const timeDisplay = document.querySelector('.time');
const button = document.querySelector('.button');

 

GAME_TIME은 게임의 총 시간을 설정하는 상수, score는 현재 점수를 저장하는 변수이다.

time은 남은 시간을 저장하며 초기값은 GAME_TIME으로 설정된다.

isPlaying은 게임의 진행 상태를 나타내는 변수이고, timeIntervalcheckInterval은 각각 타이머와 상태 체크를 위한 setInterval 함수의 반환값을 저장한다. words는 단어 목록을 저장하는 배열이고 wordInput, wordDisplay, scoreDisplay, timeDisplay, button은 HTML 요소를 참조하는 상수이다.

 

init();
function init() {
    buttonChange('게임로딩중...')
    getWords();
    wordInput.addEventListener('input', checkMatch);
}
function run() {
    if (isPlaying) {
        return;
    }
    isPlaying = true;
    time = GAME_TIME;
    wordInput.focus();
    scoreDisplay.innerText = score;
    timeDisplay.innerText = time;
    timeInterval = setInterval(countDown, 1000);
    checkInterval = setInterval(checkStatus, 50);
    buttonChange('게임중');
}

init() 함수는 게임을 초기화하며 버튼 텍스트를 '게임로딩중...'으로 설정하고, 단어를 불러오는 getWords() 함수를 호출, 입력 필드에 checkMatch 함수의 입력 이벤트 리스너를 추가한다. run() 함수는 게임을 시작, 게임이 진행 중일 경우 종료하고, 새 게임을 시작하기 위해 isPlaying을 true로 설정한 후 시간을 GAME_TIME으로 초기화한다. 또한, 입력 필드를 focus하고, 점수, 남은 시간을 업데이트하면서, 타이머와 상태 체크를 위한 setInterval을 설정 후에, 버튼 텍스트를 '게임중'으로 변경한다

 

function checkStatus() {
    if (!isPlaying && time === 0) {
        buttonChange("게임시작");
        clearInterval(checkInterval);
    }
}

// 단어 불러오기
function getWords() {
    axios.get('https://random-word-api.herokuapp.com/word?number=100')
        .then(function (response) {

            response.data.forEach((word) => {
                if (word.length < 10) {
                    words.push(word);

                }
            })

            buttonChange('게임시작')
        })
        .catch(function (error) {
            // handle error
            console.log(error);
        })

}

checkStatus() 함수는 게임 진행 중이지 않고 시간이 0이 되면 버튼 텍스트를 '게임시작'으로 변경한 후, 상태 체크를 위한 setInterval을 중지한다. getWords() 함수는 API를 통해 단어 목록을 불러와서, 10자 미만의 단어만을 words 배열에 추가, 단어를 정상적으로 불러오면 버튼 텍스트를 '게임시작'으로 변경하고, 오류 발생 시 콘솔에 에러를 출력한다

보기에 나오는 영어 100개의 단어는 *axios Api를 사용*  https://github.com/axios/axios

 

GitHub - axios/axios: Promise based HTTP client for the browser and node.js

Promise based HTTP client for the browser and node.js - axios/axios

github.com

 

// 단어 일치 체크
function checkMatch() {
    if (wordInput.value.toLowerCase() === wordDisplay.innerText.toLowerCase()) {
        wordInput.value = '';
        if (!isPlaying) {
            return;
        }
        score++;
        scoreDisplay.innerText = score;
        time = GAME_TIME;
        const randomIndex = Math.floor(Math.random() * words.length); // 수정
        wordDisplay.innerText = words[randomIndex];
    }
}

function countDown() {
    if (time > 0) {
        time--;
    } else {
        isPlaying = false;
        clearInterval(timeInterval); // 수정: 타이머가 멈추도록 추가
    }
    timeDisplay.innerText = time;
}

function buttonChange(text) {
    button.innerText = text;
    text === '게임시작' ? button.classList.remove('loading') : button.classList.add('loading');
}

checkMatch() 함수는 입력 필드의 값이 표시된 단어와 일치할 경우, 입력 필드를 비우고 게임이 진행 중일 때만 점수를 증가시키고, 시간을 GAME_TIME으로 재설정한 후 화면에 랜덤 단어를 표시한다. countDown() 함수는 시간이 남아있으면 1초씩 감소시키고, 시간이 0이 되면 게임을 종료하며 타이머를 멈춘다. buttonChange() 함수는 버튼의 텍스트를 변경하고, 텍스트가 '게임시작'일 경우 'loading' 클래스를 제거하고 그렇지 않으면 클래스를 추가하게 된다


참고용 코드↓


 

 

▼ index.html 코드▼

더보기

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>타자 게임</title>
    <link rel="stylesheet" href="css/style.css">
    <script src="https://cdn.jsdelivr.net/npm/axios@1.6.7/dist/axios.min.js"></script>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>타자 게임</h1>
        </div>
        <div class="game-box">
            <div class="word-display">
                Good Game
            </div>
            <div class="word-input-box">
                <input type="text" class="word-input" placeholder="여기에 입력하세요..." />
            </div>
            <div class="game-info">
                <div>
                    남은시간: <span class="time">0</span>초
                </div>
                <div>
                    획득점수: <span class="score">0</span>점
                </div>
            </div>
            <button class="button loading" onclick="run()">게임을 불러오는 중..</button>
        </div>
    </div>

    <script src="js/main.js"></script>
</body>
</html>

 

▼ styles.css코드 ▼

더보기

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    background: linear-gradient(135deg, #f5f7fa, #c3cfe2);
}

.container {
    text-align: center;
    width: 100%;
    max-width: 500px;
}

.header {
    background: #4a90e2;
    color: #ffffff;
    padding: 1rem;
    border-radius: 12px 12px 0 0;
    margin-bottom: 1.5rem;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.header h1 {
    margin: 0;
    font-size: 2.2rem;
}

.game-box {
    background: #ffffff;
    padding: 2rem;
    border-radius: 12px;
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
}

.word-display {
    font-size: 2.5rem;
    font-weight: bold;
    color: #4a90e2;
    padding: 1rem;
    border: 3px solid #4a90e2;
    border-radius: 8px;
    background: #f5f5f5;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    margin-bottom: 1.5rem;
    transition: background-color 0.3s, border-color 0.3s;
}

.word-display:hover {
    background-color: #e2e2e2;
    border-color: #007aff;
}

.word-input-box {
    margin-bottom: 1.5rem;
}

.word-input {
    padding: 0.75rem;
    width: 100%;
    max-width: 400px;
    font-size: 1rem;
    border: 2px solid #4a90e2;
    border-radius: 8px;
    outline: none;
    transition: border-color 0.3s;
}

.word-input:focus {
    border-color: #007aff;
}

.game-info {
    margin-bottom: 1.5rem;
    font-size: 1.2rem;
    color: #333;
    display: flex;
    justify-content: space-around;
    width: 100%;
    max-width: 300px;
}

.game-info div {
    font-weight: bold;
}

.button {
    width: 100%;
    max-width: 220px;
    height: 50px;
    background: #4a90e2;
    color: white;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-size: 1.1rem;
    transition: background-color 0.3s, transform 0.2s;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.button:hover {
    background-color: #357abd;
}

.button:active {
    transform: scale(0.98);
}

.loading {
    background-color: #ccc;
    cursor: not-allowed;
}

 

▼main.js코드▼

더보기

const GAME_TIME = 3;
let score = 0;
let time = GAME_TIME;
let isPlaying = false;
let timeInterval;
let words = [];
let checkInterval;
const wordInput = document.querySelector('.word-input');
const wordDisplay = document.querySelector('.word-display');
const scoreDisplay = document.querySelector('.score');
const timeDisplay = document.querySelector('.time');
const button = document.querySelector('.button');

init();
function init() {
    buttonChange('게임로딩중...')
    getWords();
    wordInput.addEventListener('input', checkMatch);
}


// 게임 실행
function run() {
    if (isPlaying) {
        return;
    }
    isPlaying = true;
    time = GAME_TIME;
    wordInput.focus();
    scoreDisplay.innerText = score;
    timeDisplay.innerText = time;
    timeInterval = setInterval(countDown, 1000);
    checkInterval = setInterval(checkStatus, 50);
    buttonChange('게임중');
}



function checkStatus() {
    if (!isPlaying && time === 0) {
        buttonChange("게임시작");
        clearInterval(checkInterval);
    }
}

// 단어 불러오기
function getWords() {
    axios.get('https://random-word-api.herokuapp.com/word?number=100')
        .then(function (response) {

            response.data.forEach((word) => {
                if (word.length < 10) {
                    words.push(word);

                }
            })

            buttonChange('게임시작')
        })
        .catch(function (error) {
            // handle error
            console.log(error);
        })

}

// 단어 일치 체크
function checkMatch() {
    if (wordInput.value.toLowerCase() === wordDisplay.innerText.toLowerCase()) {
        wordInput.value = '';
        if (!isPlaying) {
            return;
        }
        score++;
        scoreDisplay.innerText = score;
        time = GAME_TIME;
        const randomIndex = Math.floor(Math.random() * words.length); // 수정
        wordDisplay.innerText = words[randomIndex];
    }
}

function countDown() {
    if (time > 0) {
        time--;
    } else {
        isPlaying = false;
        clearInterval(timeInterval); // 수정: 타이머가 멈추도록 추가
    }
    timeDisplay.innerText = time;
}

function buttonChange(text) {
    button.innerText = text;
    text === '게임시작' ? button.classList.remove('loading') : button.classList.add('loading');
}

'개발 > 간단 개발 실습' 카테고리의 다른 글

유튜브 웹 페이지 실습  (2) 2024.09.09
[React] 영화 서비스  (0) 2024.08.20
[React] 로그인 페이지 구현  (0) 2024.08.13
[JS] 단어 추측  (0) 2024.08.12
[JS] 가위바위보 (vs PC)  (0) 2024.08.09