우아한테크코스

우아한테크코스 - 프리코스 4주차 회고

aoaa 2022. 11. 23. 17:00

프리코스 대망의 4주차 미션은 다리 건너기 게임입니다. 

https://github.com/Voyager003/java-bridge/tree/Voyager003

 

GitHub - Voyager003/java-bridge

Contribute to Voyager003/java-bridge development by creating an account on GitHub.

github.com


1. 시작하며

 3주차 미션인 로또를 끝내고, 다른 지원자들이 올린 코드를 살펴봤는데 적잖이 충격을 받았습니다. 제가 작성한 코드는 진짜 동작만을 하도록 설계했다고 느껴졌다면, 올려진 코드들을 보면 객체를 객체답게 다루는 설계임을 확실히 느낄 수 있었습니다. 여기에 자극받아 4주차 미션에서는 어떤 코드가 좋은 코드인지 깊이 고민하고 코드를 짰던 것 같습니다. 


2. 프로그램 설계

 

 패키지는 크게 5가지로 상수와 메시지, 괄호를 담은 constant, 게임 전반을 관리하는 controller, 메인 로직을 담당하는 domain, 예외를 검증하는 exception, 입력을 받고 출력하는 view로 나눴습니다.


3. controller

3.1 BridgeController

 게임의 전반적인 흐름을 관리하는 컨트롤러입니다. 처음 설계 시 BridgeGame 클래스를 컨트롤러로 사용하려고 작성 중이었는데, BridgeGame 클래스에는 Inputview와 Outputview가 포함되면 안된다는 조건을 뒤늦게 확인하고 BridgeController를 다시 만들었습니다. play 메서드를 통해 다리 건너기 게임을 시작하도록 하는데, 생성한 다리는 게임 끝까지 사용하기 때문에, 게임 종료 조건인 gameOver를 boolean으로 선언하고 이동하고자 하는 다리, 결과 출력, 재시작 여부를 묻도록 설계했습니다. 

 


4. domain

4.1 BridgeMaker, BridgeNumberGenerator, BridgeRandomNumberGenerator

 BridgeMaker 클래스의 makeBridge 메서드는 이름 그대로 다리를 만드는 역할을 합니다. BridgeNumberGenerator라는 생성자로 받는데, BridgeNuberGenerator라는 함수형 인터페이스를 상속받은 BridgeRandomNumberGenerator에서 난수(0, 1)을 생성하여 1이라면 UP, 0이라면 DOWN 칸을 건널 수 있는 다리를 생성을 합니다.

 

 마지막 제출 때 골치 아프게 했던 클래스 중 하나인데, 패키지를 domain으로 옮겨둬서 코드 실행에는 문제는 없었지만 우테코 홈페이지 제출 시 오류가 생겼었습니다. 클래스가 처음 위치했던 자리에 원복하였더니 해결되는 문제였습니다. 뭐가 잘못된건지 요구사항을 확인하다가 발견한 것이어서 다시 한번 요구 사항 잘 읽어야겠다라는 생각이 절로 들게 만드는 상황이었습니다.

4.2 Bridge

 BridgeMaker 클래스에서 반환한 리스트를 받아 생성된 다리를 나타내는 클래스입니다. 이 후 후술할 BridgeGame 클래스에서 다리의 이동 여부를 판단하는 isMove()와 다리의 길이를 반환하는 getBridgeSize() 메서드를 생성했습니다. 이 때 생성한 리스트는 피드백을 보고 final 값으로 선언하여 불변 객체로 만들었습니다.

4.3 Player

Player 클래스는 어디로 이동할지(U, D) moving을 인자로 받아 movePlayer 메서드를 통해 List에 값을 저장합니다. 후술할 BridgeGame 클래스의 게임 초기화 시 사용할 getPlayerMoving, clearMoving 메서드를 생성했습니다. bridge와 마찬가지로 playerMoving도 같은 이유로 final을 선언했습니다. 

4.4 BridgeGame

 BridgeGame 클래스는 플레이어를 움직일 때 사용하는 move()와 게임 재시작 시 상태를 초기화하는 메서드 retry()가 있습니다. 생성자를 먼저 선언해주었고, 시도횟수를 나타내는 attempt와 게임의 상태(성공과 실패)를 나타내는 변수 gameStatus, 다리의 상태를 담은 bridgeStatus를 생성했습니다. 

 Controller에서 inputview로 생성한 movingInput을 패러미터로 받아 player에 담았습니다. 이 때 bridge 클래스의 isMove로 다리를 건널 수 있는지 판단하고, 건널 수 없다면 bridgeStatus에 X를 넣고 return으로 메서드를 종료하고 그게 아니라면 건널 수 있는 상태가 이기 때문에 O를 넣어줬습니다.

 또한 gameOver라는 메서드를 boolean으로 선언하여 게임을 진행할 수 있는지 판별하는 메서드를 생성했습니다. 

 retry() 메서드는 Controller에서 Inputview의 gameCommand를 패러미터로 받아 재시작 여부를 판단하는 메서드입니다.

이 때, R이 입력된다면 리스트를 모두 비우고, 게임의 상태를 실패로 만든 뒤, 시도 횟수를 1 증가시켰습니다.


5. View

5.1 InputView + Exception

 InputView 클래스입니다. 메서드는 크게 3가지로 다리의 길이(3~20)를 입력받는 readBridgeSize(), 플레이어의 이동(U, D)을 받는 readMoving(), 재시작 여부(R, Q)를 받는 readGameCommand()로 나뉩니다. try - catch 문으로 예외 처리를 하였는데, exception 패키지의 Exception 클래스를 짚고 넘어가겠습니다.

 



 먼저 공통 검증으로 아무런 입력도 하지 않았을 때, 예외가 발생하도록 했습니다.

 readBridgeSize()에 들어오는 입력을 예외 처리하기 위해 알파벳이 입력되거나, 3부터 20사이의 수가 아니라면 예외가 발생하도록 했습니다. 

readMoving과 readGameCommand 같은 경우, UD와 RQ 이외의 문자가 들어오면 예외를 발생하고 메시지를 날렸습니다. 정규표현식을 사용하여 좀 더 가독성을 높였습니다.

 

5.2 OutputView

  대망의 OutputView 클래스입니다. 이번 미션에서 가장 머리 싸맸던 코드입니다. 처음에는 해시맵에 Key: Updown, Value: OX를 담아 StringBuilder를 사용해서 괄호를 append할 까 생각했었는데 다리를 건넌 상태를 유지해야 하기 때문에 포기하고 List에 담은 element를 비교하는 방식으로 바꿨습니다. 리스트의 크기만큼 반복하면서 조건에 맞다면 printf를 이용해 O, X 사인을 삽입하도록 했습니다.

 

 printResult 메서드는 게임 성공, 실패 메시지를 출력하는 메시지로 BridgeGame의 getGamestatus를 받아 게임 성공, 실패 여부를 판별한 뒤, 완성한 다리와 게임 성공 여부, 게임 시도 메시지를 출력하고 종료하게 됩니다. 

 


6. 프리코스를 마치며

 길다면 길고, 짧다면 짧다고 할 수 있는 4주간의 프리코스를 마치고 너무나도 큰 성장을 했다고 생각합니다. 왜냐면 미션이 하나씩 끝날 때 마다 다른 지원자들의 코드를 보고 부족함을 많이 느껴, 자극을 받고 더 좋은 코드를 짜기 위한 고민을 많이 했던 것 같습니다. 

 한 주의 미션이 끝날 때마다 주어지는 피드백, 코드 구현 시 주어지는 제약 조건 등을 지키면서 코드를 짜다보니 미션을 하나 끝낼 때마다 성장하는 것을 느낄 수 있었습니다.

 또한 평소에 지나쳤던 코딩 컨벤션, git 컨벤션을 지키면서 코드를 짜는 좋은 습관을 가질 수 있게 되었고, 좋은 개발자가 되기위한 학습 방향을 잡아준 좋은 경험이 되었던 것 같습니다. 

 

 진행하면서 저에게 아쉬웠던 점은 테스트 코드 작성과 람다식 작성에 대해 익숙해지지 않았다는 것입니다. 간단한 예외 처리 테스트 같은 경우는 간단했지만, 대부분의 반환 타입이 void이기 때문에 test를 어떻게 진행해야 할지 갈피를 잡지 못했습니다. 람다식 같은 경우도 다른 지원자들의 코드를 보고 도입해보고 싶었지만 이해가 부족하여 미션 코드에는 적용하지 못했습니다.

 그래서 얼마남지않은 11월과 12월에는 이전 기수에서 했던 미션으로 Mock과 Assertj를 이용한 테스트코드 작성 연습과 람다식에 익숙해지기위한 연습을 해야겠다고 목표삼았습니다. 

 

 비교할 대상은 바로 어제의 나. 더 좋은 개발자가 되기 위한 길을 향해 달려가겠습니다!