이번에는 숫자 야구 게임(우테코 2주차 깃허브 링크)이었다. 주 기능은 컴퓨터에서 3자리 숫자를 랜덤으로 생성한 후, 사용자의 입력값을 받아와 사용자에게 스트라이크/볼/낫싱으로 힌트를 주는 것이다. 추가된 요구 사항은 크게 정리하자면
가 되겠다. 가이드라인을 확인한 후
순으로 진행하였다.
이번에 진행한 내 깃허브 레포지토리는 여기이다.
이 글의 마지막에는 피드백과 소감을 적어 보았다.
처음에 정리한 이후 수정을 좀 거쳤지만 최종적인 기능 목록은 다음과 같다.
1. 게임 시작 문구 출력
2. 숫자 랜덤 생성
- 3자리 자연수
- 1부터 9까지의 숫자로 구성
- 중복 불가
3. 사용자 입력 유효성 검사
- e) 3자리 자연수가 아닌 경우
- e) 0이하의 정수 불가
- e) 중복된 숫자 불가
4. 사용자 입력 받아오기
5. 스트라이크/볼 카운트 세기
5. 정답 검사 결과 출력
- 볼, 스트라이크 둘다 존재 : 볼 -> 스트라이크 순서, 숫자 + 볼/스트라이크
- 하나도 없는 경우 - 낫싱
- 정답시 종료 문구 출력
- 오답시 3. 사용자 입력 검사 반복
6. 종료 검사
- 1 입력시 1. 게임 시작 문구 출력으로 돌아감
- 2 입력시 종료
- e) 나머지 숫자 입력시 오류 출력
이번주에는 jest의 사용 방법을 익히고 테스트코드를 작성하는 데서 시간을 많이 썼다. 왜냐하면 나는 남들이 짜준 테스트코드만 사용해봤지 한 번도 테스트코드를 짜본 적이 없었기 때문이다. 처음에는 jest를 깔라는 이야기인줄 알고 깔았었는데 npm install을 하니 jest가 자동으로 설치되었다. 저번 주에 npm을 이미 깔았어서 안 깔아도 되는줄 알았는데 npm이라는 건 node 전체의 패키지가 아니라 프로젝트마다 달라지는 듯 하다. 제일 당황스러웠던 것은 이 jest did not exit one second after the test runs has completed라는 오류였다. 이 오류가 발생하면 터미널에서 다음 값을 입력할 수 없어서 강제종료를 시켜 주어야 했다.
터미널에서는 jest를 시작할 때 --detectOpenHandles라는 옵션을 이용하라고 설명하긴 하지만 근본적인 원인을 해결하고 싶어서 검색을 한참 했다. 알고보니 어떤 비동기적 처리가 jest를 실행하고 나서도 종료되지 않아서 일어나는 오류라고 한다.
추정으로는, 아마도 숫자 야구 게임에서 사용자의 숫자값을 입력 받는데, 그 값을 1초안에 입력하지 않아서 이러한 문구를 띄워주는 듯 하다. (깃허브 디스커션에 자세한 설명이 적혀 있던데, 동기/비동기, async/await 개념에 대해 공부를 하고 와야 이해가 될 것 같다).
그래서 이 오류를 그냥 무시하고 진행하였고, 완성한 뒤에도 이 오류는 여전히 발생했지만 테스트 케이스 자체는 통과할 수 있었다.
테스트 케이스 작성도 애를 많이 먹은 부분이다. jest를 사용해본 적이 아예 없으니 jest의 명령어에 대해 잘 몰랐기 때문이다.
간단하게 말하면 함수를 만든 다음 그 함수가 통과되면 테스트케이스가 통과되는 것이다. 제일 간단한 형식으로 쓰면 이런 식으로 쓸 수 있다. test뒤에 괄호를 열어준 다음, 첫 번째 인수로 테스트할 내용, 두번째 인수로 통과할 함수를 넣어준다. 그리고 expect안에 테스트할 값을 넣어 주고 뒷 부분은 그 테스트값이 어떨지 적절한 메소드를 넣어 주면 된다. 이 경우에는 .toContain이라는 메소드를 사용하면 테스트할 값이 괄호 안의 값을 포함하는지 알려 주는 것이다.
test('split 메서드로 구분자가 포함되지 않은 경우 값을 그대로 반환', () => {
const input = '1';
const result = input.split(',');
expect(result).toContain('1');
});
그리고 알아야 할 중요한 개념은 jest.fn()이라는 개념이다.
const mockRandoms = (numbers) => {
MissionUtils.Random.pickNumberInRange = jest.fn();
numbers.reduce((acc, number) => {
return acc.mockReturnValueOnce(number);
}, MissionUtils.Random.pickNumberInRange);
};
const randoms = [1, 3, 5];
mockRandoms(randoms);
여기서 이 부분은
MissionUtils.Random.pickNumberInRange = jest.fn();
라이브러리의 랜덤값을 추출해주는 메소드를 가짜 함수로 대체하겠다는 것이다. 가짜 함수로 만들어주었으니 대체할 값을 직접 만들어서 넣어 주어야 한다. 이 경우에는 하단에서 mockRandoms(randoms)라고 호출해 줌으로써 numbers라는 부분에 [1,3,5]를 넣어준다.
이번 것은 얼추 이해는 했는데 아무래도 활용하려면 아직 시간이 좀 필요할 듯 하다. Jest 공식 API 설명 사이트에서 필요한 기능을 찾아서 활용해 봐야겠다.
각 함수에 대한 테스트는 해봤는데 함수를 연결했을 때 제대로 작동하는지는 테스트 케이스를 짤 엄두가 안 나서 그냥 테스트 케이스를 짜지 않았다. 하지만 나중에 그 부분에서 에러가 나서 어떻게든 이해해서 테스트 케이스를 짤 걸 하는 생각이 들었다.
기능 구현에서 어려웠던 점은 함수를 분리시키는 일이었다. 함수 분리 자체는 어렵지 않았지만 분리된 함수에서 얻은 값을 어떻게 다시 끌어오냐가 고민이 많이 되었다. 지역변수로 선언했기 때문에 그 함수를 나오면 값 자체가 유실되었기 때문이다.
이번에는 한 함수를 호출하고 그 함수 내에서 다른 함수를 호출하는 식으로 진행을 했다. 다른 방법을 찾을 시간이 모자랐기 때문이다. 다음에 진행할 때는 함수에서 값을 받아와서 그 값을 다른 함수 내에서 끌어올 수 있는 방법을 고민해 볼 생각이다. 아마 class 개념이 덜 잡혀서 그런 것 같다.
저번주 피드백을 반영해서, 이번에는 prettier과 eslint를 사용했다. 보통 처음에 프로젝트를 시작하는데 설치하라고 권장되는데, 나는 마지막에 사용했다. 왜냐하면 eslint를 사용하면 package.json파일이 변경되는데 이 파일을 변경하지 말라는 요구가 있었기 때문이다. 그래서 나중에 모두 작성한 뒤 es lint를 돌리고 package.json파일을 올리지 않는 방법으로 진행했다.
코딩스타일 가이드는 airbnb 코딩 스타일을 따르는 게 권장되어 airbnb 스타일로 prettier을 설정해 주었다.
arrowParens: 'always'
bracketSpacing: true
jsxBracketSameLine: false
jsxSingleQuote: false
printWidth: 80
proseWrap: 'always'
quoteProps: 'as-needed'
semi: true
singleQuote: true
tabWidth: 2
trailingComma: 'es5'
useTabs: false
한 번 해봐서 그런지 pull request는 쉽게 보냈다.
README.md를 상세히 작성한다
기능 목록을 재검토한다
기능 목록을 업데이트한다
값을 하드 코딩하지 않는다
구현 순서도 코딩 컨벤션이다
한 함수가 한 가지 기능만 담당하게 한다
함수가 한 가지 기능을 하는지 확인하는 기준을 세운다
JavaScript에서 객체를 만드는 다양한 방법을 이해하고 사용한다
테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다
처음부터 큰 단위의 테스트를 만들지 않는다
남들과 비교하기보다는 자신이 모르는 것에 집중하기
커밋 메시지를 쓸 때 괄호 안에 범위(scope)를 넣어 주기
처음 기능 목록을 작성할 때 이 기능을 어떤 함수에 넣어 줄지 적어주기
기능 목록 구현 앞부분에 체크박스를 만들어 주기.
함수를 기능에 따라 그룹화해서 다른 파일로 분리시켜주기
기능을 함수로 캡슐화 시켜준 다음, 마지막에 조합할 것
기능 구현과 테스트 실행도 다른 커밋으로 분리
jest를 활용하여 테스트 케이스를 짜는 연습이 많이 필요하다고 생각이 들었다. 테스트 케이스 짜는게 서툴러서 적당히 짰더니, 나중에 생각지도 못한 문제가 발생했다. 테스트를 제대로 짰으면 이런 문제가 생기지 않았을 것 같다는 생각이 들었다. 함수 자체 테스트 뿐만 아니라 기능이 연결되는지에 대한 테스트도 필요할 듯 하다.
jest의 오류에 대해서 이해하지 못한 건 아쉬움이 크다. 현재로써 이해하기엔 너무 추가 학습이 많이 필요할 것 같아서 건너 뛰었지만 고생을 많이 한 부분이라서 언젠가 이해하고 싶다.
공부할 건 많고 시간은 부족해서 일단 중요해보이는 것 위주로 학습하고 크게 중요하지 않은 건 일단 패스했는데, 모르는 건 회고 글에 잔뜩 적어놨으니 시간이 될 때나 그 기능이 중요해질 때 차근차근 머리에 넣어 봐야겠다.
이번에는 첨부해준 영상이 많은 도움이 되었다. 물론 자바 강의라서 메소드가 다 다르긴 하지만 일단 객체지향 언어라서 유사한 점이 많아서 배울 점이 많았다.