83% 달성했다.
목표를 '한 미션 안에서 최소 3개 이상의 오류를 마인드셋 과정을 통해 해결하는 것'으로 잡았는데, 저번 미션에서는 3개를 해결했고 이번 미션에서는 2개를 해결했기 때문이다.
하지만 83%와 상관없이 이번 미션에서는 목표의 목적을 이뤘다.
이유는 저번 미션에서 발생한 오류가 이번 미션에서도 발생했는데, 저번에는 해결하는 데 30분이 걸린 것을 이번에는 1분 만에 해결했기 때문이다. 구글 스프레드 시트에 작성한 기록을 보고 바로 해결했다.
오류를 해결한 과정은 다음과 같다.
'[ERROR]'로 시작하는 에러 메시지를 던질 때, Jest의 테스트 케이스에서 'rejects.toThrow'가 해당 에러를 정상적으로 감지하고 테스트가 통과해야 한다.
코드에서 'throw new Error("[ERROR]");'가 실행되면, 이와 관련된 테스트 케이스인 'rejects.toThrow("[ERROR]");'가 통과해야 한다.
변경하지 않는다.
이번 미션에서 최소 목표인 3개를 지키지 못했고 (생각보다 오류가 발생하지 않았다), 다음 미션은 더 복잡한 미션으로 예상돼서 3개를 채울 것 같다.
그리고 아직 오류가 발생했을 때, '올바른 동작을 한 줄로 정의하고, 원인을 찾는 연습'이 부족하다고 느껴서 더 많은 오류를 만나봐야 할 것 같다.
1주차 보다 프로그래밍을 멈추고 생각하는 시간이 자연스러워졌다. 이제는 미션이 어려워도 조급한 마음을 갖지 않고 최소한 당장 해야 할 일에 대해서는 머릿속으로 흐름이 그려질 때까지 멈춘다. 숙련도가 올라갈 때까지는 의식적으로 멈추는 습관을 유지하려고 한다.
1주차에서는 확장성에 대한 고민이 부족했다. 요구사항에서 커스텀 연산자가 1개가 아니라 2개로 바뀌면 코드를 많이 바꿔야 했다. 그래서 '요구사항에 변화가 생길 때 어떻게 대응할 수 있을까? 어떻게 하면 수정할 때 최소한의 비용이 들지?'에 대해 고민하게 되었다. 그리고 2주차의 학습 목표 2개도 결국에는 '변화에 대응하기 쉬운 코드를 위한 목표들'이라는 것을 깨달았다.
그리고 2주차를 진행하며 질문이 생기고 대답하며 고민했다.
의존성이 높은 함수는 테스트를 하기가 어려웠다. 다른 함수나 외부 모듈에 의존할 경우, 목킹이 필요해서 테스트 코드가 복잡해졌다. 그래서 최대한 한 함수가 다른 함수에 의존하지 않게 작성했더니, 테스트를 위해서뿐만 아니라 함수 자체가 좀 더 유지보수와 재사용성이 향상됐다.
그리고 TDD 방식을 고집할 필요는 없었다. TDD는 테스트를 먼저 만드는 것인데, 아직 테스트 코드 작성에 대한 숙련도가 떨어져서 테스트 코드를 기능보다 먼저 작성하는 게 조금 막연했다. 그래서 구현에 대한 확신이 있으면 몇 번은 구현 먼저 했다. 그리고 원하는 대로 동작하는지 확인하는 용도로 테스트 코드를 이용했다. 앞으로는 테스트 코드를 더 많이 작성해 보며 TDD 방식에 익숙해지려고 한다.
1주차 피드백과 프리코스 디스코드 통해서 명확한 의도의 이름을 짓는 단계를 만들었다.
1단계: 축약하지 않는 이름을 작성한다.
2단계: 내부 구현이 드러나는 부분을 감춘다.
3단계: 이름만 보고도 결과가 예상되는 이름을 짓는다.
1주차 피드백에 쓰여 있는 대로 처음에는 이름을 지을 때 축약하지 않았다. 예를 들면, '자동차가 전진하면 자동차의 위치가 1 증가하는 함수'의 이름을 지을 때 'IncreasePositionByOne'으로 지었다. 그런데 '확장성'을 생각하면 'One' 부분이 필요 없다고 느껴졌다. 그래서 'IncreasePosition'으로 바꾸었다. 그리고 내부 구현도 드러낼 필요가 없다고 생각이 들어서, position 값을 증가시키는 내부 구현을 감추며 'MoveForward'로 변경하였다. 이름만 봐도 어떤 동작을 할 것인지 예상이 가는 이름이 되었다.
추가로 이름뿐만 아니라, 커밋 메시지를 작성할 때도 '명확한 의도'를 드러냈다.
이를 위해서 하나의 커밋이 하나의 일만 해야 했고, 기능목록에 있는 대로 커밋하는 게 도움이 됐다.
1주차에는 '최소한의 버전'의 기능 목록만 작성하고 점진적으로 기능 목록에 추가하는 것이 목표였다. 이번 주차에서는 거기에 더해서 각 기능 자체도 가볍게 작성하는 것을 목표로 두었다. 우선순위가 가깝지 않은데 구체적인 요구사항은 틀이 돼서 변경할 때 유연성이 떨어졌기 때문이다. 그래서 우선순위에 가까워질수록 명확하게 바꾸었고 기능 목록을 좀 더 유연하게 쓸 수 있게 되었다.
1주차에는 App.js 한 파일에 모든 코드를 작성했는데, 이번에는 한 파일에 작성하려니까 가독성이 떨어졌다.
그래서 GPT의 조언으로 관심사별로 파일구조를 나눠봤다. 이렇게 하니까 비지니스 로직과 다른 로직을 명확히 분리할 수 있어서 가독성이 좋았다.
/src
├── App.js (주요 흐름 제어)
├── models: 데이터와 상태를 관리하는 객체 정의
├ └── Car.js (자동차 객체 정의)
├── services: 비지니스 로직을 처리하고 애플리케이션의 흐름을 제어
├ └── RacingGameService.js (게임 로직 처리 - 자동차 이동, 우승자 계산)
├── utils: 재사용 가능한 작은 기능들을 모아 놓은 도구 모듈
├ ├── InputUtils.js (입력 처리 및 검증)
├ └── OutputUtils.js (출력 처리)
└── constants.js (상수 정의 - 매직 넘버, 에러 메시지 등)
프리코스 디스코드에는 MVC 패턴이 가장 많이 보였다. 하지만 이번 미션에 적합하지 않다고 생각했다. 왜냐하면 화면을 동적으로 업데이트 하거나 상태 변화를 반영할 UI가 없어서 View를 따로 만들 필요 없다고 느꼈고, 사용자 입력은 시작할 때 한 번만 받기 때문에 Controller 또한 나눌 필요가 없다고 느꼈다.
따라서 MVC로 나누면 오히려 불필요하게 복잡해질 것 같았고, 대신 유지보수가 쉬운 구조를 선택했다.
다음 주에 미션에 따라서 MVC를 적용해 볼 수도 있을 것 같다.