[JavaScript] 행맨 게임
플레이 화면
API를 써보고 싶어서 행맨 게임을 만들어 봤습니다.
확실히 비동기 처리를 잘해야 문제없이 돌아가더라고요...
단순히 데이터를 가져와 쓰는것이 아닌 가져오는 데 걸리는 시간과 그 이후의 과정들을 잘 연결하는 게 문제였습니다.
그 외에도 많은 고비가 있었다...
1. 단어를 가져오는데 시작 버튼을 클릭을 연속으로하면 두 개의 단어가 붙어 구성되는 문제가 있었다.
시작 버튼을 클릭하면 데이터를 가져올때까지 시작 버튼을 비활성화했다.
2. 문자열로 제공받은 단어를 이용하면 문자 하나하나를 다루는데 불편함이 있어 배열에 담는 과정을 포함함.
3. 데이터를 가져오는데 시간이 걸려 그 전에 단어를 가져온 것처럼 실행이 될 때 문제가 있어 비동기 처리함.
위의 두 과정은 아래에서 다룸.
주요 기능 및 구현
중복 입력 금지
소문자 한개만 입력 가능
이벤트 활성화 및 비활성화 조건
시작 버튼을 눌러야만 키 이벤트가 활성화 된다.
단어를 맞추거나 목숨이 없을경우 키 이벤트가 비활성화 된다.
HTML 구조
<h1>행맨</h1>
<h2 id="hidden"> </h2>
단어 입력 : <input type="text" autofocus>
<div id="status">
남은 목숨: 10 <br>
현재까지 입력한 문자:
</div>
<button>시작 및 다시하기</button>
id="hidden": 숨겨진 단어와 맞춘 부분을 출력하게 구성.
input: 단어의 입력을 받음.
id="status": 현재 상태를 보임.
랜덤 단어 API
async function getPuzzle() {
return axios.get('https://puzzle.mead.io/puzzle?wordCount=1')
.then((result) => {
return result.data.puzzle.toLowerCase()
})
}
랜덤 단어를 가져와 반환을하는데 첫 문자가 대문자로 데이터를 제공하는터라, 소문자로 변환하여 단어를 반환.
실행의 비동기 처리
async function play() {
// 초기값 세팅
word=[]
life=10
arr=[]
doInsert=[]
//input 버튼에 inputEnter 이벤트리스너 등록.
const input = document.querySelector('input')
input.addEventListener('keyup', inputEnter)
//단어를 받아 word에 하나하나 push하여 배열로 만듬.
await getPuzzle()
.then((puzzle) => {
for (const c of puzzle)
word.push(c)
})
//단어의 길이만큼 arr에 "*"를 push.
for(let i=0; i<word.length; i++){
arr.push('*')
}
// 화면에 보여지는 데이터 업데이트
upDate()
}
play() 함수를 통해 진행을 위한 세팅을 함.
- input 태그에 키 이벤트 등록.
- status 초기화.
- 받은 단어 word와 현재까지 사용자가 입력한 문자, 사용자가 맞춘 현재 단어 상태 arr을 "*"로 가공.
- 화면에 보여지는 데이터를 현재 데이터에 맞추기 위한 upDate 함수 실행
문자열로 받아오면 원하는 인덱스의 문자를 다루는데 불편하기에 배열로 다룸.
inputEner 이벤트 리스너
function inputEnter(event) {
const input = event.currentTarget
if(event.key==='Enter'){
if(!isAble(input.value)){
alert('올바른 문자를 입력하세요.')
input.value=''
return
} else if(alreadyChar(input.value)){
alert('이미 입력한 문자입니다.')
return
}
doInsert.push(input.value)
let flag = false
for(const i in word){
if(word[i]===input.value){
flag=true
arr[i]=word[i]
}
}
if(flag===false){
life--
}
input.value=''
upDate()
}
}
사용자가 input 태그에서 엔터키를 눌렸을때만 동작한다.
- isAble(word) : 주어진 단어가 영어 소문자 한 글자인지 검사.
- alreayChar(word) : 입력한 문자가 앞전에 이미 입력한 적이 있는지 검사.
- doInsert 배열을 통해 사용자가 입력한 문자들을 기록.
- doInsert : 사용자가 입력한 문자들을 기록하는 배열.
- flag : 사용자가 입력한적이 있는 문자인지 아닌지 판별.
- upDate : 현재 상태를 화면에 업데이트.
정규표현식
function isAble(value) {
const regex = new RegExp('^[a-z]$')
if(regex.test(value)){
return true
}
return false
}
행맨 게임을 만든다고 소문자만 검사하는 걸 어떻게 구성할지 생각했었는데
자바스크립트로 입력의 문자를 검사하는데 정규표현식을 사용할 수 있다.
화면에 표시되는 html 업데이트 / 이벤트 제거
function upDate() {
const status = document.querySelector('#status')
const hidden = document.querySelector('#hidden')
const input = document.querySelector('input')
hidden.textContent = arr.join(' ')
status.innerHTML = `남은 목숨: ${life}<br>현재까지 입력한 문자: ${doInsert.join(', ')}`
if(life===0){
alert(`정답은 ${word.join('')}였습니다.`)
input.removeEventListener('keyup', inputEnter)
} else if(arr.indexOf('*')===-1){
alert('성공!')
input.removeEventListener('keyup', inputEnter)
}
}
innerHTML로 문자열을 HTML로 인식하여 수정했다.
life가 0이거나 모든 단어를 맞춰 arr(사용자가 맞춘 상황)에 "*"가 없다면 키 이벤트를 제거함.
연속 클릭 방지
document.addEventListener('DOMContentLoaded', (event) => {
const button = document.querySelector('button')
button.addEventListener('click', () => {
button.disabled=true
play()
.then(() => {
button.disabled=false
})
})
})
play() 함수의 실행은 모든 html이 로딩된 이후에 실행이 가능해야 한다.
그렇지 않으면 선택자를 실행하는데 문제가 생긴다. 그래서 DOMContentLoaded를 구성하였다.
모든 것이 로딩된 후, 시작 버튼에 이벤트를 등록해 주었다.
클릭을 하면 play() 함수를 실행이 완료된 후, 다시 시작 버튼을 활성화하게 구성했다.
연속 클릭은 단어를 가져오는데 두 개를 붙여서 가져오는 문제가 있었기 때문이다..