1주 차 미션에서 작성했던 준비 운동을 마무리하고 본격적인 미션이 시작되는 것 같다.
이번 2주 차 미션에서는 1주 차에서 학습한 것에 더해 함수를 분리하고, 각 함수별로 테스트를 작성하는 것에 익숙해지는 것을 목표로 하고 있다.
그리고 작성한 함수에 대한 테스트 코드를 작성하여 내가 생각한 기능대로 제대로 동작을 하는지 테스트도 진행을 해아 한다. 자발적으로 작성해 본 적이 없는 내 입장에서는 가치 있는 미션인 것 같다.
2주차 과제를 진행하면서 내가 과제를 진행했던 과정을 정리하겠다.
문제 풀기에 앞서 다음과 같은 진행 방식을 유의하면서 기능을 구현해야 한다.
기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항
세 가지로 구성되어 있다.1부터 9까지 서로 다른 수로 이루어진 3자리의 수를 맞추는 게임 기능을 구현해야 한다.
1스트라이크
1볼 1스트라이크
낫싱
IllegalArgumentException
을 발생시킨 후 애플리케이션은 종료되어야 한다.숫자 야구 게임을 시작합니다.
숫자를 입력해주세요 : 123
1볼 1스트라이크
숫자를 입력해주세요 : 145
1볼
숫자를 입력해주세요 : 671
2볼
숫자를 입력해주세요 : 216
1스트라이크
숫자를 입력해주세요 : 713
3스트라이크
3개의 숫자를 모두 맞히셨습니다! 게임 종료
게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.
1
숫자를 입력해주세요 : 123
1볼
...
Application
의 main()
이다.build.gradle
파일을 변경할 수 없고, 외부 라이브러리를 사용하지 않는다.System.exit()
를 호출하지 않는다.ApplicationTest
의 모든 테스트가 성공해야 한다. 테스트가 실패할 경우 0점 처리한다.camp.nextstep.edu.missionutils
에서 제공하는 Randoms
및 Console
API를 사용하여 구현해야 한다.
camp.nextstep.edu.missionutils.Randoms
의 pickNumberInRange()
를 활용한다.camp.nextstep.edu.missionutils.Console
의 readLine()
을 활용한다.기능을 구현하기 전 docs/README.md
에 구현할 기능 목록을 정리해 추가한다.
Git의 커밋 단위는 앞 단계에서 docs/README.md
에 정리한 기능 목록 단위로 추가한다.
프로그램 요구 사항에서 Java 코드 컨벤션의 링크를 들어가보았다. git commit을 할 때 어떤 형태로 작성을 해야 하는지 그에 대한 가이드가 들어있었다.
그 외에도 git comit에 대한 자세한 내용을 알고 싶어서 구글링을 통해 git commit convention에 대해 다양한 가이드를 참조했고, 내가 공부했던 내용들을 바탕으로 벨로그에 정리를 하였다.
문제에서 이번 주차의 미션은 함수를 분리하고 이에 따른 테스트 코드를 작성하는 것을 목표로 한다. 그래서 나는 각 기능을 하나의 함수로 취급해서 각 가능들을 고찰하였다. 즉, 하나의 Application 파일 안에서 코드 작성을 진행했다. 내 코드의 내용이 정답이라는 보장은 없기에 어떤 함수를 만들었고, 무슨 기능인지 간단하게 설명만 하려고 한다.
최종적으로 내가 생각했던 기능들은 다음과 같다.
1️⃣ 게임 시작 공지 기능
2️⃣ 1부터 9까지 3개의 서로 다른 자연수의 컴퓨터 숫자 뽑기 기능
3️⃣ 사용자로부터 3개의 숫자 입력 받기 기능
4️⃣ 사용자로부터 입력된 3개의 숫자의 유효성 검증 기능
5️⃣ 컴퓨터 숫자와 사용자 숫자로부터 '볼'의 개수 뽑기 기능
6️⃣ 컴퓨터 숫자와 사용자 숫자로부터 '스트라이크'의 개수 뽑기 기능
7️⃣ '스트라이크' 및 '볼'의 개수 정보 출력 기능
8️⃣ 3스트라이크가 나올 때까지 3번과 7번을 반복 기능
9️⃣ 3스트라이크가 달성 후 게임 재시작 여부 기능
목록 당 기능을 구현 후 commit convention에 따라서 commit을 진행했다.
1️⃣ 숫자 야구 게임을 시작합니다.
문구 출력을 위한 기능이다. 이 기능에 대한 메서드를 따로 구현을 했고, 메서드 이름을 noticeStartGame
으로 설정했다.
2️⃣ createComputerNumbers
메서드를 만들었고, Randoms의 pickNumberInRange
메서드를 사용하였다.
3️⃣ inputUserNumbers
메서드를 만들었다. 이 메서드에는 숫자를 입력해주세요 :
도 출력이 된다. 처음에는 숫자 입력 출력 문구도 따로 기능으로 뺄 생각을 했지만, 입력 기능에 관련이 있으므로 빼지 않기로 결정했다.
4️⃣ isValidUserNumbers
메서드를 만들었다. 이 메서드는 다음과 같은 내용을 검증한다.
5️⃣ getBallNumber
메서드를 만들었고, 3자리의 유효한 사용자 숫자와 컴퓨터 숫자를 비교하여 볼의 개수를 출력한다.
6️⃣ getStrikeNumber
메서드를 만들었고, 3자리의 유효한 사용자 숫자와 컴퓨터 숫자를 비교하여 스트라이크의 개수를 출력한다.
7️⃣ printBallStrikeResult
를 만들었다. 이 메서드는 들어온 볼의 개수와 스트라이크의 개수 정보를 바탕으로 알맞은 결과를 출력한다. 또한 boolean형의 메서드이므로 3스트라이크가 아니면 true를 반환한다.
8️⃣ 7️⃣번에서 3스트라이크가 나오지 않을 경우 true를 반환하는 것을 이용하여 main
함수에서 반복문을 통해서 야구 게임 숫자 반복 입력을 설정했다.
9️⃣ wantRestart
를 구현했고, 이 메서드는 boolean형이다. 즉, 사용자가 게임 재시작을 원하면 true를 반환, 게임 종료를 원하면 false를 반환하도록 설정해 main
에서 반복문을 통해서 게임을 재시작 하도록 설정하였다.
Java 코드 컨벤션에 따라서 전반적으로 가독성을 높이기 위한 코드 리펙토링을 진행했다.
모든 기능 및 코드 리펙토링을 마치고 전반적으로 각 기능들이 이상이 없는지 그에 따른 테스트 코드를 작성하였다.
테스트 코드에 대한 작성 경험은 별로 많지 않아서 테스트 코드를 쓸 때 주로 어떤 기능을 쓰는지, 또 어떤 방식으로 테스트를 진행하는지 공부하였다.
테스트를 진행하는 방식에는 BDD(Behavior-driven development), TDD(Test-driven development)이 두 가지가 존재한다는 것을 알았다.
TDD는 테스트를 먼저 작성하고 그 테스트를 통과시키는 코드를 작성하는 흐름을 기본으로 하지만, BDD는 시나이오를 기반으로 테스트를 작성한다. 그리고 given
, when
, then
등의 구조로 테스트를 작성한다.
나는 BDD방식을 통해, 즉 given
, when
, then
의 구조로 테스트 코드를 작성하려고 노력했다. 그래야 일반 사람들이 언뜻 봐서도 이해가 될 것 같았기에 가독성 면에서도 좋을 것 같다는 생각이 들었다.
테스트 코드를 작성을 해보면서 힘들었던 점을 꼽아서 얘기하자면 출력 관련 테스트에 대한 코드 작성과 private으로 설정된 메서드를 테스트 코드 파일로 불러오는 일이었다.
나는 이것과 관련하여 구글링을 하는데 많은 시간을 투자했었다.
하지만 결과적으로 테스트 코드를 완성했고, 꽤 유용한 기능들에 대한 노하우를 얻었다는 느낌을 받았다.
미션 저장소의 README.md는 소스코드에 앞서 해당 프로젝트가 어떠한 프로젝트인지 마크다운으로 작성하여 소개하는 문서이다. 해당 프로젝트가 어떠한 프로젝트이며, 어떤 기능을 담고 있는지 기술하기 위해서 마크다운문법을 검색해서 학습해보고 적용해 본다.
기능 목록을 클래스 설계와 구현, 함수(메서드) 설계와 구현과 같이 너무 상세하게 작성하지 않는다. 클래스 이름, 함수(메서드) 시그니처와 반환값은 언제든지 변경될 수 있기 때문이다. 너무 세세한 부분까지 정리하기보다 구현해야 할 기능 목록을 정리하는 데 집중한다. 정상적인 경우도 중요하지만, 예외적인 상황도 기능 목록에 정리한다. 특히 예외 상황은 시작 단계에서 모두 찾기 힘들기 때문에 기능을 구현하면서 계속해서 추가해 나간다.
README.md 파일에 작성하는 기능 목록은 기능 구현을 하면서 변경될 수 있다. 시작할 때 모든 기능 목록을 완벽하게 정리해야 한다는 부담을 가지기보다 기능을 구현하면서 문서를 계속 업데이트한다. 죽은 문서가 아니라 살아있는 문서를 만들기 위해 노력한다.
문자열, 숫자 등의 값을 하드 코딩하지 마라. 상수(static final)를 만들고 이름을 부여해 이 변수의 역할이 무엇인지 의도를 드러내라. 구글에서 "java 상수"와 같은 키워드로 검색해 상수 구현 방법을 학습하고 적용해 본다.
클래스는 상수, 멤버 변수, 생성자, 메서드 순으로 작성한다.
class A {
상수(static final) 또는 클래스 변수
인스턴스 변수
생성자
메서드
}
변수 이름에 자료형, 자료 구조 등을 사용하지 마라.
String carNameList = Console.readLine();
String[] arrayString = carNameList.split(",");
함수 길이가 길어진다면 한 함수에서 여러 일을 하려고 하는 경우일 가능성이 높다. 아래와 같이 한 함수에서 안내 문구 출력, 사용자 입력, 유효값 검증 등 여러 일을 하고 있다면 이를 적절하게 분리한다.
만약 여러 함수에서 중복되어 사용되는 코드가 있다면 함수 분리를 고민해 본다. 또한, 함수의 길이를 15라인을 넘어가지 않도록 구현하며 함수를 분리하는 의식적인 연습을 할 수 있다.
단지 기능을 점검하기 위한 목적으로 테스트를 작성하는 것은 아니다. 테스트를 작성하는 과정을 통해서 나의 코드에 대해 빠르게 피드백을 받을 수 있을 뿐만 아니라 학습 도구로도 활용할 수 있다. 이런 경험을 통해 테스트에 대해 어떤 유용함을 느꼈는지 알아본다.
테스트의 중요한 목적 중 하나는 내가 작성하는 코드에 대해 빠르게 피드백을 받는 것이다. 시작부터 큰 단위의 테스트를 만들게 된다면 작성한 코드에 대한 피드백을 받기까지 많은 시간이 걸린다. 그래서 문제를 작게 나누고, 그 중 핵심 기능에 가까운 부분부터 작게 테스트를 만들어 나간다.
잘보고갑니다!