이글은 딱딱체
로 쓰여져있습니다. 참고해주시면 감사하겠습니다!
2주차에 들어가기 앞서서
먼저 새로운 과제를 진행하기전에 어떤점을 보완을 해야할까 생각을 해봤다.
왜 이러한 과제를 냈는지 파악해보자.
지난 1주차 과제에서는 소감문을 작성할때에 왜 코치분들이 이 과제를 내셨을까라는 생각을 했었다.
문제를 풀기전에 이런 고민을 해봤어야했는데 순서가 바뀐 공부를 했던 것 같다. 또한 질문에 대한 의미를 제대로 파악하지 못하고 과제에 임하는 것은 학습효과가 떨어져 나에게 손해가 될 것이기 때문에 이번 2주차에는 이런 과제를 왜 냈는지에 생각을 해보고 들어가려고 한다.
가독성에 너무 집중했다.
변수명, 함수명 그리고 읽기 편한 코드를 만들겠다는 생각에 빠져버려 정작 중요한 기능확인을 제대로 하지 못한 것 같다. 슬랙 채널을 보니 다른분들은 자신의 코드가 제대로 동작하는지 확인하기위해 여러 테스트 케이스를 만들어 확인을 하시는데 나는 그런 노력은 하지 않았다. 마감이 지난다음에야 슬랙에 올라온 테스트 케이스를 돌려보았고, 천만다행이게도 다 통과는 하더라.
우선 코드가 제대로 동작을 하는게 우선이라고 생각한다. 가독성은 그 다음 생각하자. 마침 이번 과제에서는 Jest 로 구현한 기능이 제대로 동작하는지 확인하고 넘어가야겠다.
이번 과제의 요구사항
1. 숫자 야구 구현하기
단순하게 숫자야구를 구현하는 것이다. 순서도를 그려가면서 어떤 절차로 구현을 해야할지 파악을 해볼 것이다.
2. throw를 통한 예외처리
실제 어플리케이션에는 수많은 예외상황이 발생할 것이다. 이번과제는 작은 게임이지만 사용자가 얼마든지 콘솔로 잘못된 입력을 할 수 있기 때문에 그것에 대한 예외처리를 요구하는 것 같다. 발생할 수 있는 예외상황을 스스로 생각해보고, 처리해보길 원한다고 판단했다.
3. lint 와 prettier 를 사용한 코드 컨벤션
공통 피드백에서 lint 와 prettier 의 사용을 권장하기도 했기때문에 이번 과제에서는 적용을 해보려고 한다.
lint 동작을 위해 eslint 라는 linter 를 사용할 생각이다. 이를 사용하여 런타임상황에서 발생할 에러를 미리 잡아내어 생산성을 높이고 싶다. 또한 eslint에도 formatting 기능이 있긴 하지만 prettier가 formatting에 특화가 되어있고, 커스텀을 하기에 더 편해 두가지 조합을 선택하기로 하였다.
4. 함수 또는(메소드) 가 한가지의 기능만 하게하라.
위 요구사항에는 다음 두가지의 중요사항을 마음에 품고 임할 생각이다.
- 함수명만 봐도 어떤 기능을 하는지 알 수 있게하기.
- 작은단위로 쪼개어 재사용 시도해보기.
2번 중요사항을 지키면 코드 재사용이 증가하여 유지보수가 용이해진다
라고하는데 유지보수까지는 힘들 것 같고 재사용을 얼마만큼 할 수 있을지에 집중하고 싶다. 기존에 코딩할때는 네이밍을 잘 짓지도 않았고 함수를 따로 분리하지 않았다. 이번기회에 재사용성에 대해 고민을 해보고 또 적용을 해볼 수 있었으면 좋겠다. 사람들이 좋다고 좋다고 하는데 아직 경험을 해보지 않아서 와닿지 않는다. 부딪혀보면서 느껴보자.
5. Jest를 사용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인하라.
제일 기대가 되는 요구사항이다. 실은 TDD 에 대해 관심이 있어서 Cypress를 잠깐 배워보기도 했는데 Jest는 사용해본적이 없었다. Jest를 사용하면 그간 내가 테스터가 되어 일일히 테스팅을 해줬던 지루한 과정이 없어져서 생산성이 증가될 것이 기대되고, 한번 테스트를 하면서 코드를 짜는 것을 경험해보고 싶었기 떄문에 굉장히 설렌다.
구현하면서 배웠던 점들
순서도
작은 게임이지만 순서도를 그려가며 어떻게 게임이 진행될까 시뮬레이션을 해보았다.
이것 외에도 볼
과 스트라이크
구하는 로직은 종이에 어떻게 구현해야할지 적어가면서 생각했던 것 같다. 머리로만 생각하는 것보다 시각적으로 보면서 생각을 이어나가는게 나에게는 좀 더 효과적인 것 같다. 잘 까먹는 탓에 생각을 놓치지 않을 수 있어서 좋다.
throw를 통한 예외처리
오랜만에 throw
를 사용해본 것 같다. throw 를 통해서 예외처리를 하는데는 별다른 어려움은 없었다. 다만 throw 를 사용할때 try-catch
와 는 세트라고 생각해서 당연하게 try catch 를 통해 throw 에러를 잡아주었는데, 이렇게 되어버리면 Jest에 throw 발생이 전달되지 않아 테스트를 통과하지 못하는 상황이 발생했다. 이 점이 왜 그런지 찾는데 시간이 걸렸던 것 같고 그 외에는 에로사항이 없었다.
lint 와 prettier 를 사용한 코드 컨벤션
eslint 와 prettier 를 사용하려고 했지만, 프로그래밍 요구사항에 보면 package.json 은 건들지 말라고 해서 적용할 수 없었다. 아쉬웠다..
실은 다 적용해놓고 위 요구사항을 뒤늦게 확인하여 후다닥 제거했다.
함수 또는(메소드) 가 한가지의 기능만 하게하라.
한가지 기능만 하게끔 하려고 노력해봤다. 메소드를 재사용하여 반복된 코드를 줄였다. 예를 들면 숫자야구게임의 한번의 게임 턴과 컴퓨터가 랜덤으로 수를 생성하는 메소드를 재사용한게 생각이 난다.
또한 별개의 주제지만 depth 의 제한이 2이하였기 때문에 이때문에 다음 코드를 두고 고민을 좀 했다.
아래와 같은 함수를
const generateRandomNumberArray = () => {
const randomNumberArray = [];
while (randomNumberArray.length < GAME_MESSAGE.GAME_NUMBER_LENGTH) {
const digit = Random.pickNumberInRange(GAME_MESSAGE.MIN_NUMBER, GAME_MESSAGE.MAX_NUMBER);
if (!randomNumberArray.includes(digit)) {
randomNumberArray.push(digit);
}
}
return randomNumberArray;
};
다음과 같이 말이다.
하지만 바꾸지 않았다. if 문 안에 한줄의 코드만 있었기 때문에 중괄호를 제거할 수 있었고. 오히려 저렇게 빼는데 더 복잡하다고 판단했다.
const checkDuplicateNumbers = (digit, randomNumberArray) => {
const copyArray = [...randomNumberArray];
if (!copyArray.includes(digit)) {
copyArray.push(digit);
}
return copyArray;
};
const generateRandomNumberArray = () => {
let randomNumberArray = [];
while (randomNumberArray.length < GAME_MESSAGE.GAME_NUMBER_LENGTH) {
const digit = Random.pickNumberInRange(GAME_MESSAGE.MIN_NUMBER, GAME_MESSAGE.MAX_NUMBER);
randomNumberArray = checkDuplicateNumbers(digit, randomNumberArray);
}
return randomNumberArray;
};
또 고민되는게 파라미터로 넘어오는 변수를 그대로 수정하는 것은 동시성이 생길시에 바뀐 변수와 관련된 함수에 영향을 끼칠 수 있어 지양하려고 한다. 이러한 문제점을 해결하기위해 방법을 생각했지만 copyArray 와 같이 깊은 복사를 하여 복사한값을 수정하는 방법밖에 생각이 나지않았다. 다른 방법은 없나 생각을 해봐야겠다.
Jest
이번 과제를 통해 Jest를 반 강제적으로 배우게 되었다. 다소 수동적인 태도이지만, 이 기회를 통해 능동적인 고민을 하려고한다.
왜 Jest를 사용하게 되었을까.
내 생각에는 이런 테스팅 도구가 나오기 전에는 일일히 console 에 변수나 함수 리턴값을 찍어가면서 디버깅을 했을 것이다. 아마 이러한 과정이 지겨워진 사람들이 앞서말한 지루하고 시간이 오래걸리는 문제점을 해결하기 위해 개발하지 않았을까 싶다. 또한 완벽하지는 않겠지만 미리 설계를 다 해놓고, 그 다음 테스트를 진행하면서 pass 하는지만 체크하면 되기때문에 구현에만 집중할 수 있게 될 것 같다. 그리고 팀 협업을 할때에도 서로가 인정하는 테스트 코드를 통과하면 내가 구현한 기능이 잘 동작했다는 것이고 명백한 근거가 생기기 때문에 증명하는데 시간을 소요할 필요가 없어질 것이다.
정리하자면 Jest를 통해 얻을 수 있는 이점은 다음과 같을 것이다.
- 디버깅에 많은 시간을 할애 할 필요없이 코드가 올바르게 동작하는지 확인할 수 있다.
- 버그를 사전에 차단할 수 있다.
- 협업 동료에게 코드의 안정성을 쉽게 증명할 수 있다.
배우면서 고민했던 부분
공식문서를 보기도 했고, 정리가 되어있는 블로그의 글을 참고하기도 했다.
또 월요일부터 풀리퀘가 열려있길래 올려놓으신 분이 테스트 코드는 어떻게 짜셨나 보면서 배웠다.
그리고 아쉽게도 TDD 는 적용하지 못했다. 이번 과제는 구현을 먼저하고 기능이 제대로 동작하는지 확인용도로 테스트를 진행하였다.
적용하지 못한 이유는 Jest 로 테스트 코드를 짜는것이 처음이라 테스트 코드를 짜고 구현을 하는게 어려웠기 때문이다. 그래서 내 상황에 맞게 위 방법을 사용하였고 나름 나쁘지 않았던 것 같다. 오히려 알고리즘 문제를 풀고 히든 테스트케이스를 찾아서 내 로직의 문제점을 찾는 것 처럼 내가 구현한 코드에 문제점을 생각할 수 있어 재밌었던 것 같다.
검증하는 과정에서 에러를 하나 만났는데 기록해두려고 한다. 에러 상황은 테스트 코드가 RUNS 인 상태에서 결과가 나오지 않았다. 마치 무한루프를 도는 것 같았다.
위와 같은 에러를 만나게 된 이유는 다음과 같았다.
numberEnteredByUser에['123', '1'] 라는 테스트 케이스를 추가하였고 이는 예외 상황이기 때문에 테스트 fail 이 되는지 체크하려했다. 하지만 이 코드가 계속 RUNS 실행만 되고 30초정도 대기한 후 에러가 났다
test('재시작 또는 종료의 예외처리', () => {
const pickedNumberArrayByComputer = [1, 2, 3];
const numberEnteredByUser = [
['123', '0'],
['123', '\n'],
['123', '1'],
];
numberEnteredByUser.forEach((enteredNumber) => {
mockRandoms(pickedNumberArrayByComputer);
mockQuestions(enteredNumber);
expect(() => {
const app = new App();
app.play();
}).toThrow();
});
});
위와 같은 문제가 생긴 이유는 다음과 같다.
- 3스트라이크 이후 게임을 재시작할 것인지 종료할 것인지 묻는다.
- 이때 1을 입력하면 사용자의 다음 숫자입력을 기다린다.
다음 숫자입력을 기다리는데 이것이 만족되지 않아 테스트가 완료되지 않는 에러였다. 테스트 코드에 문제가 있나 엄청 검색을 많이 했는데 알고보니 입출력 받느라 생긴 에러였다. 이를 통해 테스트할때에도 구현된 코드의 동작여부에 따라 테스트가 완료되지 않고 계속 대기하는 에러가 발생할 수 있음을 알게되었다.
사용하면서 좋았던 점
테스트 코드를 사용하면서 좋았던 점은 매직넘버나 문자열을 상수화하는 리팩토링을 하였는데, 이때 내가 인간디버거가 되어 잘 바뀌었는가 확인할 필요없이 구현해놓은 테스트 코드 돌려주기만 하면 놓친부분에 대해 빠르게 파악할 수 있어서 좋았다.
짜는게 귀찮지만 아마 서비스가 커지면 필수로 적용을 해야되지 않을까 싶다.
이번주 느낀점 요약
- 질문이나 문제에는 상대방의 의도가 있다. 이것을 파악하는것이 핵심이다.
- 나는 이번주의 핵심은 테스트 코드로 자신의 로직을 점검하는것, 예외처리 , 구현능력 이라고 판단하였다.
- 테스트 코드 처음 사용해봤는데 느낌이 나쁘지 않다.
'공부기록 > 웹 개발' 카테고리의 다른 글
[우아한 테크코스 프리코스] 4주차 (0) | 2022.11.30 |
---|---|
[우아한 테크코스 프리코스] 3주차 (0) | 2022.11.15 |
html dataset 을 사용한 css switch case (1) | 2022.10.11 |
[타입스크립트] Utility Types (0) | 2022.10.11 |
[타입스크립트] keyof , in keyof (0) | 2022.10.06 |