post-custom-banner

이어서..

오늘은

1. 테스트 코드를 메인 코드로 옮기기
2. 테스트 통과

를 목표로 가졌다.

둘 다 완료했다. 시간이 많이 걸린 부분이 controller 의 구조에 관한 고민, 그리고 테스트에서의 에러였다.


메인 코드로 옮기기

ResultOfGame 로직 변경

테스트 클래스를 메인 코드로 그대로 옮겼는데, 에러가 발생하지 않아서 단위 테스트를 잘 진행했구나- 라는 생각이 들어 뿌듯했다.

코드를 옮기다가 새로운 고민이 생겼는데, ResultOfGame (게임 결과 결정) 클래스에서 결과가 담긴 문자열을 OutputView 에 전달하는 방식이 맞는지였다. 이것이 SRP 원칙에 위반되지 않나 고민을 했다.
그리고 더불어 controller 를 작성하다 보니 '재시작 부분을 어떻게 구현할 것인가' 라는 고민이 들었다. 사실 재시작을 테스트 하려면 통합 테스트를 진행했어야 하기 때문에 단위 테스트를 위한 테스트 코드에서는 이 재시작 기능을 구현할 수가 없었다.

controller 부분에서 if 문을 넣어도 되지만, 이렇게 controller 에서 재시작을 판단하는 로직을 넣어버리면 'model과 view의 상호작용을 조정하는 중개자’ 라는 책임 이상의 역할을 한다고 생각했다. 그래서 ResultOfGame 클래스 안에 내부 클래스를 생성해서 OutputView 에 전달할 문자열과 게임 재시작 여부를 RealResult 객체로 전달하게 했다. message도, restart도 게임 결과라고 생각해서 SRP 원칙을 위반하지 않는다고 판단했다. 사실 나중에 리팩토링 할려고 생각해서 클래스, 메서드 명이 내 마음대로다. 일단 오늘 목표는 테스트 통과였기에... 내일부터 맘 편하게 리팩토링을 하고 싶었다.

Controller

BaseballGameController 클래스의 코드다. 최종 완성은 아니다. 일단은 이러이러한 과정으로 작동해야하지 않을까 - 하고 러프하게 작성한 거라 이것 역시 메서드명이 마음대로다.

중첩 while 문은 정말 사용하지 않을려고 엄청나게 고민했는데, 결국 사용하지 않으면 재시작 부분을 구현할 수 없다고 생각됐다. 이 부분은 나중에 게임의 로직을 담당하는 클래스를 작성해야 겠다고 생각했다.

Error1 - NoSuchElementException


테스트 코드를 실행하니 NoSuchElementException 에러가 발생했다. 직접 main() 에서 실행했을 땐 문제가 없었는데 테스트 코드에서 실패했다.

일단 원인을 파악했다. Scanner 를 사용하는 InputView 에서 문제가 있는 것 같았다. Scanner 에서 더 이상 읽을 수 있는 데이터가 없는 것으로 생각됐다. 그 원인은 바로....

Error1 해결


이 부분 때문이었다. getUserInput() 메서드를 호출할 때마다 새로운 Scanner 객체를 생성해서 입력값을 더 받아야 한다고 판단했기 때문이다. 스캐너 객체 생성을 클래스 필드로 꺼내줌으로써 해결됐다.

Error2 - 잘못된 결과 출력

또 다른 문제가 발생했다. 1볼, 1볼 부분이 1볼 1스트라이크, 3스트라이크가 나와 정답을 맞추고 게임을 끝내야 하는데, 다른 결과를 출력해 테스트를 실패했다. 아마 카운터 쪽이나 결과 출력 쪽에 문제가 있지 싶었다.

Error2 해결

원인은 if문의 사용이었다. if문을 전부 거쳐가고 있어서 localMessage가 덮어씌워지고 있었다. 우테코 이전 기수분들의 회고나 공부 기록을 보면 'else 사용을 지양' 해야 한다는걸 봤는데, 이 때까지 학습 하면서 다른 모든 것들은 그 이유에 대해 공부를 했는데 (예 : 왜 getter, setter 사용을 지양해야 하는지) 딱 이 'else 사용을 지양' 해야 하는지만 이유를 공부하지 않고 맹목적으로 믿었다. 사실 'else 사용을 지양' 하는건 return 값을 else 문을 사용하지 않고 if 조건절에서 값을 return 하는 방식을 사용하란 것이었다. 딱 이 부분에서 문제가 발생한 것을 보고 스스로 검증하고 이유를 파고들어 학습하는 것이 왜 중요한지 마음에 새겨졌다.

Error3 - 같은 에러?

위의 에러를 분명히 해결하고 테스트를 했는데, 똑같은 결과가 나왔다.

또 디버깅을 해보니, 이번엔 다른 곳의 문제였다. 컴퓨터가 랜덤 생성한 숫자가 게임 재시작 시 그대로 사용되고 있었다.
디버깅한 것을 보면 computerNumber 가 처음 생성한 "135" 에서 변하지 않는 것을 볼 수 있다.

원인은 controller 의 run() 메서드에서 랜덤 생성한 숫자를 필드에 저장하는 getRandomNumber() 메서드가 단 한번만 실행되기 때문이었다.

이렇게 게임을 재시작 할 때마다 랜덤 숫자를 새로 생성하게 해 주었다.


테스트 성공!

결국 성공했다.

사실 말이 에러 찾고 원인 찾고 해결한거지 디버깅만 몇 시간을 하고 검색하고.. 시간을 참 많이 사용했다. 이 날 편두통이 심했는데 어떻게든 테스트를 성공하고 싶어서 참고 진행했다. 결국 오후 11시 58분에 테스트를 통과했다...

(테스트 성공하고 혼자 박수쳤다.)


마무리

드디어 테스트 코드를 통과했다! 사실 제출 시에 테스트도 통과해야 하지만, 그래도 이제는 마음에 여유가 좀 생겼다. 주일은 교회도 가고 해서 시간이 좀 부족할까 싶었는데, 설계를 제외하고 3일만에 완성했다. 그래도 너무 재밌었다! 어릴 때부터 막연하게 품어온 개발자라는 꿈이 내게 맞는 것 같아서 기분이 좋았다. 그리고 이번 주 부터는 다른 일정이 없어 하루에 11시간 까지는 시간을 쏟을 수 있게 되었다.

내일부터는 이 더러운 controller 를 대대적으로 리팩토링 하고, 전체적으로 코드를 더 좋은 코드, 더 좋은 객체 지향 설계로 바꾸는 과정에 들어갈 예정이다. 중점을 기능 구현에서 학습과 피드백으로 더 두려고 한다.

새로 알게된 점

1. 개발자가 판단해야 한다.
이게 무슨 말이냐면은, ResultOfGame 로직을 변경하면서 SRP 원칙 위반 여부을 고민할 때 느낀 점이다. 어떻게 보면 위반하는 것 같은데, 어떻게 보면 또 '1스트라이크' 도 게임의 결과이고, 'false(정답 아님)' 도 게임의 결과이지 않은가? 이렇게 애매한 것들은 개발자가 자의적으로 판단할 필요가 있다는 사실을 깨달았다.

2. 객체의 재생성 여부
코드를 작성하고 로직을 짤 때 이 객체를 재사용 할지, 재사용 하지 않을지를 잘 생각해야 한다고 느꼈다. 무턱대고 새로운 객체를 생성해도 안되고, 무조건 객체를 재사용 해서도 안된다. 오늘 에러를 해결하면서 '이 객체를 가지고 내가 어떻게 써먹을려 그러나?' 를 객체를 만들기 전에 항상 고민하는 습관을 가져야겠다고 마음먹었다.

3. 무조건 수용하지 말자
mvc 패턴이든, else 지양이든, 싱글톤이든 사용하는 이유와 장점, 단점을 파악하고 써야 한다고 느꼈다. 평소에는 항상 이유와 장단점을 먼저 학습하고 파고들다가, 오늘 안에 테스트 통과를 한다는 목표에 미쳐서(원래는 안 그러는데, 뭔가 갑자기 오기가 생겼다. 가능 할 것 같아서... 그리고 구현하느라 끝까지 학습하지 못한 것들을 다시 씹고 뜯고 해보고 싶어서...) 이번에 딱 한번 이유를 모르고 else 지양을 했더니, if 문을 다 실행하는 어처구니 없는 결과가 발생했다. 알고 쓰기!! 다짐한다.

좋았던 점

1. 무엇보다 테스트 통과한 것. 일단 (리팩터링을 해야하지만)구현에 성공했다는 기쁨, 좀 더 여유롭게 학습할 시간을 가질 수 있겠다는 기쁨, 의외로 생각한 것보다 빨리 구현했다는 기쁨.

2. 개발자라는 직업이 나에게 맞다는 확신을 얻은 것
테스트 디버깅을 하다가 머리가 깨질 듯이 아프고 속이 좋지 않았는데(아마 체증이었던 것 같다.) 약 생각은 커녕 계속 앉아서 '어디가 문제냐... 찾고만다...' 라는 생각으로 디버깅을 했다. 이게 확신에 대한 증거가 아닐까 생각한다. 그리고 무엇보다 '때로는 머리 아프고 때로는 해결이 안돼서 답답하고 또 눈도 침침해지고 하지만' 재밌다! 어려운 전략 게임을 하는 기분이다.

아쉬웠던 점

  1. 새로 알게된 점의 2, 3번
  2. 너무 구현에 신경을 썼던 것이 아쉬운 하루였다. 근데 1주차라서 '내가 이 정도 구현하는데 시간이 얼마나 걸리는지' 에 대한 판단이 필요했기에, 어쩔 수 없었다고 생각한다. 이를 바탕으로 다음 과제때는 구현과 학습의 시간 분배를 잘 할수 있지 않을까?

도움이 된 자료들

| BOJHelp - NoSuchElement
| OPENJDK 64-BIT SERVER VM WARNING:...
| else 예약어 대신 if 조건절 내 return 방식을 활용해보자!
| 이 기능 있는줄 나만 몰랐나? - 1편- Intellij에서 TODO 기능을 사용해보자.

profile
자바 백엔드 개발자
post-custom-banner

0개의 댓글