https://github.com/woowacourse/javascript-baseball-precourse

 

GitHub - woowacourse/javascript-baseball-precourse: 숫자 야구게임 미션을 진행하는 저장소

숫자 야구게임 미션을 진행하는 저장소. Contribute to woowacourse/javascript-baseball-precourse development by creating an account on GitHub.

github.com

 

이때까지 JS를 이용하여 직접 기능을 구현하는 일이 없었다.

 

그러다 보니, 내 지식으로 체득하는 것이 아닌, 단순 암기에 그쳤다.

 

Python을 배울 때도 직접 알고리즘을 문제를 구현할 때 프로그래밍 실력이 크게 늘었다.

 

JS를 배울 때 다른 언어들보다 습득하는 시간이 빠르다는 이유로 관과 했던 불찰도 있다.

 

이번 우아한테크 코스의 문제를 직접 풀어보며 프런트엔드의 구현력을 키우는 시간을 가졌다.

 

배웠던 내용으로 처음 적용하는 것 이기도 하고, 요구사항들을 처음 만족해 보았던 시간이어서 어색하거나 미비한 것들이 있을 수 있다. 배우며 성장한다는 마음으로 하였다.


개발 환경 : VSCode, node.js

테스트 :  cypress

 

우아한 테크 코스는 기능 요구 사항 부터 시작하여 프로그래밍 요구 사항 등 요구 사항에 맞게 프로그래밍하는 것이 중요하다.

 

깃 레파지토리를 clone 하였으며, node.js 및 npm install로 모듈 설치 과정을 거쳐 프로젝트를 시작.

 

 

프로그래밍 요구 사항에서 index.js 에서 구현할 function 또는 class 형태를 활용하여 구현하는 것에 힌트를 얻어, 단순 프로그래밍 보다 클래스를 활용하여 구현하는 것이 더 효율적이라고 판단했다.

 

이전 나는 JS로 class를 구현해 본 적이 없어 처음에는 다소 헤매었지만, 메서드를 게임의 기능별로 구현하고,

 

실제 게임을 진행하는 메서드를 play로 지정하여 구현하였다.

BaseballGame 클래스에서 구현할 기능의 목록

1. 구현한 기능을 진행할 play 메서드

2. 유저가 입력한 숫자 3개와 랜덤함수를 비교하여 ball과 strike를 계산해주는 rule 메서드

3. rule에서 계산한 ball과 strike를 바탕으로 출력할 문자열을 만들어줄 resultString

4. 유저가 잘못된 값을 전달 할 경우 예외처리

 

index.js 메서드 목록

1. 3개의 중복되지 않는 랜덤값을 리턴하는 createComputerNumber
2. 3스트라이크 시 게임 종료 및 종료 div 출력
3. 재시작 버튼시 랜덤수 초기화 및 게임 다시 진행

 

 

하지만 1번 기능을 구현할 때 약간 해매었다.

 

프로그래밍 요구 사항 중 1번의 기능은 우테코에서 제공하는 api 또는 모듈을 활용해야 했다.

 

이 부분에서 많이 헤매었는데,

html에서 노드의 모듈을 사용하려면, 웹팩 모듈을 설치한 후 웹팩화 해야 했었다.

 

결국 우테코에서 제공하는 CDN을 이용하여 index.js 에서 해당 기능을 사용하는 것으로 수정하였다.

 

추후 CDN 및 모듈을 사용하는 방법을 정리해야겠다고 생각했다.

 

결론적으로 BaseballGame 클래스를 구현하였다.

 

index.js 에서 해당 클래스를 사용하기 위해 export 시켜주었다.

 

그 후 index.js 에서 해당 클래스를 동작하도록 import 시킨 후 game 객체를 생성하였다.

그 후 선택자 및 이벤트 리스너를 활용하여 유저가 3개의 숫자를 submit 해주었을 때 play 메서드가 동작하도록 구현하였다.  

 

또한 여기서 언급하기 어려울 정도로 사소한 것들에서 많이 배울 수 있었다. 

JS 특성상 오류가 발생하여도 콘솔창에서만 잠깐 알려주거나 에러코드가 안 나오는 것 때문에 적응의 시간이 필요하였다.

 

 

전체 코드

index.js

"use strict";
import BaseballGame from "./BaseballGame.js";

let game = new BaseballGame();
let user = "";
let computerNumber = "";

const $result = document.querySelector("#result");
const $userInput = document.querySelector("#user-input");
const $form = document.querySelector("#form");
const $restart = document.querySelector("#game-restart-button");

const $goal = document.querySelector("#goal");

const createComputerNumber = () => {
  computerNumber = MissionUtils.Random.pickUniqueNumbersInRange(1, 9, 3);
  computerNumber = computerNumber.join("");
  return computerNumber;
};


function error(userInput) {
  // 사용자 입력을 문자열로 처리
  if (typeof userInput !== "string" || userInput.length !== 3) {
    return false;
  }

  // 각 문자를 숫자로 변환하고 유효성 검사
  let userInputNumbers = Array.from(userInput).map(Number);

  // 모든 요소가 유효한 숫자인지 확인
  if (!userInputNumbers.every((num) => !isNaN(num))) {
    return false;
  }

  // 중복된 숫자가 있는지 확인
  let uniqueNumbers = new Set(userInputNumbers);
  if (uniqueNumbers.size !== userInputNumbers.length) {
    return false;
  }

  // 모든 요소가 1과 9 사이의 정수인지 확인
  if (!userInputNumbers.every((num) => num >= 1 && num <= 9)) {
    return false;
  }

  return true;
}

let computer = createComputerNumber();

console.log("random : " + computer);

$form.onsubmit = function () {
  user = $userInput.value;
  console.log(typeof user);

  if (!error(user)) {
    alert("정확한 값 입력하세요!");
    return;
  }

  let gameResult = game.play(computer, user);
  if (gameResult == "3스트라이크") {
    $result.style.display = "none";
    $goal.style.visibility = "visible";
    return false;
  }
  cancle();
  $result.textContent = gameResult;
  return false;
};

const restart = () => {
  console.log("restart");
  computerNumber = createComputerNumber();
  console.log(computerNumber);
  $goal.style.visibility = "hidden";
  $result.style.display = "block";
  cancle();
};

const cancle = () => {
  $userInput.value = "";
  $userInput.focus();
};

$restart.addEventListener("click", restart);

BaseballGame.js

export default class BaseballGame {
  constructor() {
    console.log("baseball 생성자 실행");
  }

  play(computerInputNumbers, userInputNumbers) {
    let strike = 0,
      ball = 0;
    [strike, ball] = this.rule(computerInputNumbers, userInputNumbers);
    const result = this.resultString(strike, ball);
    console.log(result);
    return result;
  }

  resultString(strike, ball) {
    let result = "";

    if (ball > 0) {
      result += ball + "볼";
    }
    if (strike > 0) {
      if (result) {
        result += " ";
      }
      result += strike + "스트라이크";
    }
    if (strike == 0 && ball == 0) {
      result = "낫싱";
    }
    return result;
  }

  rule(computerInputNumbers, userInputNumbers) {
    let strike = 0;
    let ball = 0;

    for (let i = 0; i < 3; i++) {
      if (computerInputNumbers[i] === userInputNumbers[i]) {
        strike += 1;
        continue;
      } else if (
        computerInputNumbers.includes(userInputNumbers[i]) &&
        computerInputNumbers[i] !== userInputNumbers[i]
      ) {
        ball += 1;
      }
    }
    return [strike, ball];
  }
}

index.html

<!DOCTYPE html>
<html lang="ko">

<head>
  <meta charset="UTF-8" />
  <title>숫자 야구 게임</title>

</head>

<body>
  <div id="app">
    <h1>⚾ 숫자 야구 게임</h1>
    <p>
      <strong>1~9까지의 수</strong>를 중복없이
      <strong>3개</strong> 입력해주세요. <br />
      올바른 예) 139 <br />
      틀린 예) 122
    </p>
    <form id="form">
      <input type="text" id="user-input" />
      <button id="submit">확인</button>
    </form>

    <h3>📄 결과</h3>
    <div id="result">1볼 1스트라이크</div>

    <div id="goal" style="visibility: hidden;">
    <h2>정답을 맞추었습니다!</h2>
    <div>게임을 새로 시작 하시겠습니까?</div>
    <button id="game-restart-button">게임 재시작</button>
  </div>
  <script type="module" src="index.js"></script>
  <script type="module" src="BaseballGame.js"></script>

</body>
</html>

index.html

 

 

플레이 테스트

 

숫자 야구 테스트


예외 처리 목록

 

1. 플레이어가 잘못된 입력값 입력 시

 

1-1 중복된 숫자가 포함

1-2 숫자 0이 포함

1-3 세자릿수가 아닌 수

1-4 숫자가 아닌 문자 입력

1-5 공백 입력

function error(userInput) {
  // 사용자 입력을 문자열로 처리
  if (typeof userInput !== "string" || userInput.length !== 3) {
    return false;
  }

  // 각 문자를 숫자로 변환하고 유효성 검사
  let userInputNumbers = Array.from(userInput).map(Number);

  // 모든 요소가 유효한 숫자인지 확인
  if (!userInputNumbers.every((num) => !isNaN(num))) {
    return false;
  }

  // 중복된 숫자가 있는지 확인
  let uniqueNumbers = new Set(userInputNumbers);
  if (uniqueNumbers.size !== userInputNumbers.length) {
    return false;
  }

  // 모든 요소가 1과 9 사이의 정수인지 확인
  if (!userInputNumbers.every((num) => num >= 1 && num <= 9)) {
    return false;
  }

  return true;
}

 

$form.onsubmit = function () {
  user = $userInput.value;
  console.log(typeof user);

  if (!error(user)) {
    alert("정확한 값 입력하세요!");
    return;
  }

 

 

3개의 인덱스에 접근하며 확인하려는 방법을 이용하려 모든 입력값 (사용자, 컴퓨터)를 문자열로 처리했었는데,

 

그로 인해 예외처리하는 것이 쉽지 않았다.

 


배운 점

 

1. 클래스의 구현 및 사용 방법

2. 자바스크립트 선택자와 이벤트 리스너

3. 자바스크립트로 CSS 조작

4. import와 export

5. 예외처리

6. CDN 사용법

 

 

 

아쉬운 점

 

1. node.js 모듈 사용 방법 숙지

2. crpress 사용방법

3. 좀 더 효율적인 코드 작성 필요성

4. Git commit의 부재 - 매우 중요한 것인데 서둘러 만들다 보니 없이 진행하였다.

 

아직 원인을 찾지 못하였는데, 테스트 환경이 잘 동작하지 않았다. 테스트 환경 및 구축 또한 시간을 들여 공부해야겠다.