우테코 프리코스가 시작되었습니다! 딱 1년만이네요! 돌이켜보면 그동안 많은 일이 있었습니다.(내레이터 톤으로) GPT가 상용화됐고 GPT-4도 나왔고, 고용시장은 위태위태하고, 피어는 점점 많아지고... 저는 부트캠프를 들으면서 리액트니 라이브러리니... 상태 관리 어쩌구... 네트워크 통신... 이것저것 머릿속에다 쑤셔박았구요, 팀 프로젝트도 해보고 포트폴리오도 만들고 이것저것 했네요.
그리고 1년! 이 지나서 프리코스가 다시 시작됩니다! 두근두근!
작년에는 온보딩에서 간단한 알고리즘 문제를 7개 정도 풀었고, 2주차에서 야구 게임 미션이 나왔는데요! 이번엔 1주차에 야구 게임이 찾아왔네요! 그런데 완전히 그대로는 아니고, 달라진 게 몇 가지 있었습니다!
{
"name": "javascript-baseball",
"version": "1.0.0",
"description": "우아한테크코스 프리코스 숫자 야구 게임 미션",
"type": "module",
"dependencies": {
"@woowacourse/mission-utils": "2.1.1"
},
"devDependencies": {...
.
.
.
올해 미션에서의 패키지.json 파일을 보면, 작년 미션과는 다르게 "type" : "module"이 추가되어 있습니다! 이제 CommonJS의 require / module.exports 대신, import / export로 다른 파일의 모듈을 불러올 수 있습니다.
미션할 때 뻔질나게 사용했던 MissionUtils 라이브러리가 2.1.1 버전으로 업데이트 되었어요! 주요한 변경점은 Console 클래스의 새 기능인 readLineAsync입니다. 기존의 readLine은 콜백 기반으로 작동하게끔 되어있어서, 다른 비동기 처리법 대신 콜백 방식만 사용했었는데요! (덕분에 아름다운 콜백 헬을 볼 수 있었음) 이번 미션에서는 readLineAsync를 제공하고, 미션 설명에서도 '사용자 입력을 받을 때 MissionUtils의 Console.readLineAsync를 사용하라' 라고 명시가 되어 있습니다! 비동기 처리가 편해져서 더 쉽게 사용할 수 있을 것 같아요! (아닐수도 있어요) 빨리 사용법을 익혀봐야겠네요!
더 이상 예전의 제가 아닙니다!!! 작년에는 첫 주차에 알고리즘 문제를 풀면서 하루에도 십수번 머리가 쪼개질 것 같았고, 야구게임에 와서는 '이걸 대체 어쩌라는거야'하고 막막함부터 느꼈지만! 2023년 NEW 그른손은 다릅니다. 작년에는 몰랐던 지식과 패턴을 활용해서, 작년에 제출했던 과제를 리파이닝한다는 마음으로 한번 사부작거려보려고 합니다. 결심을 다지는 의미에서 기능 목록부터 작성했습니다!
## 🤔요구사항 리스트
- [ ] 프로그램 시작 시 메세지를 표시한다.
- [ ] 1 ~ 9까지의 무작위 숫자로 이루어진 정답을 설정한다.
- [ ] 사용자로부터 입력을 받고, 결과를 반환한다.
- [ ] 같은 수가 같은 자리에 있으면 스트라이크, 다른 자리에 있으면 볼, 전혀 없으면 낫싱으로 계산한다.
- [ ] 메세지를 반환하고, 다시 숫자를 입력받는다.
- [ ] 결과값이 3 스트라이크일 경우, 게임 종료 메세지를 표시하고 새로 시작 / 종료 명령 입력을 표시한다.
- [ ] 게임 시작 선택 시 새 게임이 시작된다.
- [ ] 게임 종료 선택 시 프로세스가 종료된다.
- [ ] 사용자의 입력이 잘못되었을 경우, throw 문을 사용해 예외를 발생시키고 애플리케이션을 종료한다.
- [ ] 정답 입력 시 사용자의 입력은 '서로 다른 3자리의 숫자'여야 한다. 이에 대한 예외 처리를 작성한다.
- [ ] 게임 명령어 입력 시 사용자의 입력은 '1과 2 중 하나' 여야 한다. 이에 대한 예외 처리를 작성한다.
- [ ] 프로그램에서 사용할 상수 변수를 별도의 파일에 작성한다.
- [ ] 프로그램을 MVC 패턴으로 분리한다. - [ ] Model은 야구 게임 데이터와 게임 로직을 포함한다. - [ ] View는 사용자의 입력, 출력 등 UI 관련 로직을 포함한다. - [ ] Controller는 View와 Model 사이를 연결한다. (View에서 받은 입력값을 Model로 넣고, 반환값을 View로 전달...)
그리고 지난 번에 유틸을 MissionUtils로 불러와서 사용할 때도 항상 MissionUtils.Console.readLine 이런 식으로 써서 길고 불편했던 게 생각 나서, missionUtils라는 별도의 파일에다가 쓸 것만 따로 불러왔어요!
import { Console, Random } from "@woowacourse/mission-utils";
export const { print, readLineAsync } = Console;
export const { pickNumberInRange } = Random;
이제 사용할 때는 그냥 print('배고프다')처럼 쓰면 되는 거죠!
그리고 게임 로직에 관련된 함수들을 저장할 곳도 만들었습니다.
//gameUtils.js
import { pickNumberInRange } from "./missionUtils.js";
export const gameUtils = {
generateAnswer(min, max, length) {
const answer = new Set();
while (answer.size < length) {
const randomNumber = pickNumberInRange(min, max);
answer.add(randomNumber);
}
return [...answer];
},
};
이렇게! 그리고 미션 설명을 다시 읽으면서 필요한 상수 변수들을 생각해보고, constants.js라는 파일을 만들어서 여기다가 몰아넣고 관리하기로 했습니다!
export const GAME_CONSTANTS = {
MIN_NUMBER: 1,
MAX_NUMBER: 9,
ANSWER_LENGTH: 3,
STRIKE_OUT_COUNT: 3,
};
export const GAME_RESULTS = {
BALL: "볼",
STRIKE: "스트라이크",
NO_MATCH: "낫싱",
};
export const USER_COMMANDS = {
RESTART: "1",
QUIT: "2",
};
export const GAME_MESSAGES = {
START: "숫자 야구 게임을 시작합니다.",
FINISH: `${GAME_CONSTANTS.NUMBER_LENGTH}개의 숫자를 모두 맞히셨습니다! 게임 종료`,
INPUT_NUMBERS: "숫자를 입력해주세요 : ",
INPUT_COMMAND: `게임을 새로 시작하려면 ${USER_COMMANDS.RESTART}, 종료하려면 ${USER_COMMANDS.QUIT}를 입력하세요.\n`,
};
export const ERROR_MESSAGES = {
INVALID_NUMBERS: `[ERROR] 입력 숫자는 ${GAME_CONSTANTS.MIN_NUMBER}~${GAME_CONSTANTS.MAX_NUMBER} 사이의 중복되지 않는 ${GAME_CONSTANTS.NUMBER_LENGTH}개의 숫자여야 합니다.`,
INVALID_COMMAND: `[ERROR] 명령어는 ${USER_COMMANDS.RESTART}이나 ${USER_COMMANDS.QUIT}만 입력할 수 있습니다.`,
};
하드코딩 하지 마라!는 원칙에 따라, 게임 커맨드나 게임 로직에 관련된 데이터도 하나하나 상수 변수로 정리했습니다! 방 정리를 좀 이렇게 해야 할텐데!
이제 다음은, 요구사항 리스트에 작성했던 대로 MVC 패턴에 맞춰서 야구게임을 만들어볼 겁니다. 작년 미션에서는 모든 코드를 App.js에 몰아넣어놔서, 진짜 테스트 통과하는데 필요한 것만 넣었는데도 코드 길이가 130줄을 넘어갔었는데요, 다른 미션에서 했던 대로 MVC 패턴을 활용해서 깔끔하게 분리해볼겁니다!