자바 콘솔을 사용하는 행맨 게임을 제작해보았다.
단계별로 요구사항을 맞춰가면서 코드를 작성하였는데, 요구사항 정의가 중요하다는 것을 느끼게 된 프로젝트이다.
조원들과 서로 코드 리뷰를 진행해가며 리팩토링하는 과정이 재미있었고 도움이 많이 되었다.
행맨 게임을 할 수 있는 프로그램을 만듭니다. 컴퓨터는 미리 정의된 목록에서 단어를 무작위로 선택하고 사용자는 단어의 문자를 추측해야 합니다.
컴퓨터는 사용자의 추측에 대한 피드백을 제공하며 라운드가 진행됩니다. 사용자는 정답 단어를 추측하거나 시도 횟수가 부족할 때까지 추측을 반복하게 됩니다.
ou : 게임 횟수와 목숨을 입력하세요.
in : 2, 4
ou : 1번째 게임이 시작됩니다. 정답 단어는 5글자 입니다.
ou : 1 라운드 : _____, 목숨 4
in : p
ou : 2 라운드 : _pp__, 목숨 4
in : i
ou : 3 라운드 : _pp__, 목숨 3
in : l
ou : 4 라운드 : _ppl_, 목숨 3
in : e
ou : 5 라운드 : _pple, 목숨 3
in : a
ou : 6 라운드 : apple, 목숨 3
ou : 축하합니다. 정답입니다.
ou : 다음 게임을 시작합니다.
ou : 2번째 게임이 시작됩니다. 정답 단어는 7글자 입니다.
ou : 축하합니다. 정답입니다.
ou :
=== Game Result ===
게임 id : 1, 추측 : 성공, 남은 목숨 : 3, 정답 : apple
라운드 id : 1, 남은 목숨 : 4, _____, 사용자 입력 : p
라운드 id : 2, 남은 목숨 : 4, _pp__, 사용자 입력 : i
라운드 id : 3, 남은 목숨 : 3, _pp__, 사용자 입력 : l
라운드 id : 4, 남은 목숨 : 3, _ppl_, 사용자 입력 : e
라운드 id : 5, 남은 목숨 : 3, _pple, 사용자 입력 : a
===================
ou : 다음 게임을 시작합니다.
ou : 2번째 게임이 시작됩니다. 정답 단어는 7글자 입니다.
ou : 메뉴를 선택합니다. (1 : 게임하기, 2 : 게임 결과 보기, 3 : 라운드 결과 보기)
in : 2
ou : 게임 id를 입력해주세요.
in : 1
ou :
=== Game Result ===
게임 id : 1, 추측 : 성공, 남은 목숨 : 3, 정답 : apple
라운드 id : 1, 남은 목숨 : 4, _____, 사용자 입력 : p
라운드 id : 2, 남은 목숨 : 4, _pp__, 사용자 입력 : i
라운드 id : 3, 남은 목숨 : 3, _pp__, 사용자 입력 : l
라운드 id : 4, 남은 목숨 : 3, _ppl_, 사용자 입력 : e
라운드 id : 5, 남은 목숨 : 3, _pple, 사용자 입력 : a
===================
ou : 메뉴를 선택합니다. (1 : 게임하기, 2 : 게임 결과 보기, 3 : 라운드 결과 보기)
in : 3
ou : 라운드 id를 입력해주세요.
in : 4
=== Round Result ===
라운드 id : 4, 남은 목숨 : 3, _ppl_, 사용자 입력 : e
===================
ou : 메뉴를 선택합니다. (1 : 게임하기, 2 : 게임 결과 보기, 3 : 라운드 결과 보기)
...
처음 스캐너를 열어주고 프로그램 종료시까지 계속 사용하였는데, 닫아주어야 한다고 코멘트 받아서 try-catch-resource 구문으로 수정하였다.
// 리뷰 전
public class HangManApplication {
private static final Scanner scanner = new Scanner(System.in);
// 리뷰 후
public class HangManApplication {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
...
}
입력 예외를 처리하는 과정에서 while문을 사용하였는데, 다른 분들이 보기에 코드가 가독성 있지 않고 복잡해보였다. while문이 아니라 try-catch로 재귀함수 돌리는 방법도 있다고 코멘트 받아서 수정하였다.
// 리뷰 전
while (!isValidInput) {
try {
System.out.println("게임 횟수와 목숨을 입력하세요.");
scanner.nextLine();
String str = scanner.nextLine();
String[] input = str.split(",");
if (input.length < 2) {
throw new IllegalArgumentException("숫자, 숫자 형태로 입력해주세요.");
}
System.out.println("\n다음 게임을 시작합니다.");
}
System.out.println(gameId + "번째 게임이 시작됩니다. 정답 단어는 " + answer.length() + "글자 입니다.");
playRound(gameId, userLife, answer, blind, scanner);
gameId++;
}
}
// 리뷰 후
public static void startGame() {
try {
System.out.println("게임 횟수와 목숨을 입력하세요.");
String str = reader.readLine();
...
runGames(user, startGameID);
} catch (NumberFormatException e) {
System.out.println("숫자, 숫자 형태로 입력해주세요.");
startGame();
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
startGame();
} catch (IOException e) {
System.out.println("입력 오류가 발생했습니다.");
}
}
user class가 가장 애매했는데(사실상 따로 클래스를 나눈것도 의미는 없는 기분, 확장성도 고려되지 않음) user 객체가 점점 많아진다면? 이라는 코멘트를 받아 아예 userName변수까지 추가해서 추후 user객체들을 List로 관리하도록 수정하였다.
// 리뷰 전
public class User {
private Integer life;
private Integer gameNum;
...
}
// 리뷰 후
public class UserList {
private Map<String, User> users;
...
}
public class HangManApplication {
private static final UserList userList = new UserList();
public static void main(String[] args) {
System.out.println("사용자 이름을 입력하세요.");
String userName = br.readLine();
User user = new User(userName, life, gameNum);
userList.addUser(user);
...
}
만약 게임이 한번도 진행되지 않은 경우에도 게임 id와 라운드 id를 입력해야 게임 진행 여부를 확인할 수 있었는데, 이 부분을 코멘트 받아 미리 걸러줄 수 있도록 추가하였다.
// 리뷰 후
if (games.isEmpty()){
System.out.println("진행된 게임이 없습니다.");
return;
}
코드 리뷰를 해주면서 리팩토링 하는 과정이 즐거웠고, 내가 항상 놓치는 부분에서도 피드백 받을 수 있어서 도움이 많이 되었다. 특히 try-resource 구문은 다른 프로젝트에서도 애용하게 되었다. 추후 행맨 프로젝트를 스프링부트를 사용해 웹어플리케이션으로 전환해보고 싶은데, 아직 스프링에서 허우적대는 수준이라 언제 완성될지는 모르겠다 🥺
깃에서 자세히 보기