우아한테크코스 5기 - 프리코스 4주차 미션[bridge]

core·2022년 12월 3일
0

우아한테크코스

목록 보기
6/6

서론

3주 차 미션에서는 클래스 분리, 단위 테스트를 시작해 보는 것으로 과제를 진행했지마, 이번 4주 차 미션에서 작성하는 실습 과정의 목표는 다음과 같다.

  1. 클래스(객체)를 분리하는 연습
  2. 리펙터링

3주 차에서는 클래스를 분리하는 연습에 있어서 많은 어려움을 겪었지만, 이번 4주 차에서는 클래스를 분리하는 연습을 하는 것에 있어서 큰 어려움을 느끼면서 진행할 것 같은 느낌은 들지 않았다. 좀 더 자신감 있게 과제를 진행할 수 있을 것 같다.

이번에는 하나의 기능을 만들어 놓고 테스트를 할 수 있으면 테스트를 해서 그 기능이 정상적으로 동작하는지 확인해가면서 과제를 진행하였다.

코드의 내용을 다 설명을 하게 되면 글이 너무 길어질 것 같아서 어떤 과정으로 과제를 해결했는지 설명하겠다.


기능 구현 유의사항

문제 풀기에 앞서 다음과 같은 진행 방식을 유의하면서 기능을 구현해야 한다.

  • 미션은 기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항 세 가지로 구성되어 있다.
  • 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다.
  • 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.

🚀 기능 요구 사항

위아래 둘 중 하나의 칸만 건널 수 있는 다리를 끝까지 건너가는 게임이다.

  • 위아래 두 칸으로 이루어진 다리를 건너야 한다.
    • 다리는 왼쪽에서 오른쪽으로 건너야 한다.
    • 위아래 둘 중 하나의 칸만 건널 수 있다.
  • 다리의 길이를 숫자로 입력받고 생성한다.
    • 다리를 생성할 때 위 칸과 아래 칸 중 건널 수 있는 칸은 0과 1 중 무작위 값을 이용해서 정한다.
    • 위 칸을 건널 수 있는 경우 U, 아래 칸을 건널 수 있는 경우 D값으로 나타낸다.
    • 무작위 값이 0인 경우 아래 칸, 1인 경우 위 칸이 건널 수 있는 칸이 된다.
  • 다리가 생성되면 플레이어가 이동할 칸을 선택한다.
    • 이동할 때 위 칸은 대문자 U, 아래 칸은 대문자 D를 입력한다.
    • 이동한 칸을 건널 수 있다면 O로 표시한다. 건널 수 없다면 X로 표시한다.
  • 다리를 끝까지 건너면 게임이 종료된다.
  • 다리를 건너다 실패하면 게임을 재시작하거나 종료할 수 있다.
    • 재시작해도 처음에 만든 다리로 재사용한다.
    • 게임 결과의 총 시도한 횟수는 첫 시도를 포함해 게임을 종료할 때까지 시도한 횟수를 나타낸다.
  • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.
    • Exception이 아닌 IllegalArgumentException, IllegalStateException 등과 같은 명확한 유형을 처리한다.

입출력 요구 사항

입력

  • 자동으로 생성할 다리 길이를 입력 받는다. 3 이상 20 이하의 숫자를 입력할 수 있으며 올바른 값이 아니면 예외 처리한다.
  • 라운드마다 플레이어가 이동할 칸을 입력 받는다. U(위 칸)와 D(아래 칸) 중 하나의 문자를 입력할 수 있으며 올바른 값이 아니면 예외 처리한다.
  • 게임 재시작/종료 여부를 입력 받는다. R(재시작)과 Q(종료) 중 하나의 문자를 입력할 수 있으며 올바른 값이 아니면 예외 처리한다.

출력

  • 게임 시작 문구
다리 건너기 게임을 시작합니다.
  • 게임 종료 문구
최종 게임 결과
[ O |   |   ]
[   | O | O ]

게임 성공 여부: 성공
총 시도한 횟수: 2
  • 사용자가 이동할 때마다 다리 건너기 결과의 출력 형식은 실행 결과 예시를 참고한다.
    • 이동할 수 있는 칸을 선택한 경우 O 표시
    • 이동할 수 없는 칸을 선택한 경우 X 표시
    • 선택하지 않은 칸은 공백 한 칸으로 표시
    • 다리의 시작은 [, 다리의 끝은 ]으로 표시
    • 다리 칸의 구분은 |(앞뒤 공백 포함) 문자열로 구분
    • 현재까지 건넌 다리를 모두 출력
  • 예외 상황 시 에러 문구를 출력해야 한다. 단, 에러 문구는 "[ERROR]"로 시작해야 한다.
[ERROR] 다리 길이는 3부터 20 사이의 숫자여야 합니다.

실행 결과 예시

다리 건너기 게임을 시작합니다.

다리의 길이를 입력해주세요.
3

이동할 칸을 선택해주세요. (위: U, 아래: D)
U
[ O ]
[   ]

이동할 칸을 선택해주세요. (위: U, 아래: D)
U
[ O | X ]
[   |   ]

게임을 다시 시도할지 여부를 입력해주세요. (재시도: R, 종료: Q)
R
이동할 칸을 선택해주세요. (위: U, 아래: D)
U
[ O ]
[   ]

이동할 칸을 선택해주세요. (위: U, 아래: D)
D
[ O |   ]
[   | O ]

이동할 칸을 선택해주세요. (위: U, 아래: D)
D
[ O |   |   ]
[   | O | O ]

최종 게임 결과
[ O |   |   ]
[   | O | O ]

게임 성공 여부: 성공
총 시도한 횟수: 2

🎯 프로그램 요구 사항

  • JDK 11 버전에서 실행 가능해야 한다. JDK 11에서 정상적으로 동작하지 않을 경우 0점 처리한다.
  • 프로그램 실행의 시작점은 Application의 main()이다.
  • build.gradle 파일을 변경할 수 없고, 외부 라이브러리를 사용하지 않는다.
  • Java 코드 컨벤션 가이드를 준수하며 프로그래밍한다.
  • 프로그램 종료 시 System.exit()를 호출하지 않는다.
  • 프로그램 구현이 완료되면 ApplicationTest의 모든 테스트가 성공해야 한다. 테스트가 실패할 경우 0점 처리한다.
  • 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 이름을 수정하거나 이동하지 않는다.
  • indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
    • 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
  • 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
  • 3항 연산자를 쓰지 않는다.
  • 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
  • JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.
  • else 예약어를 쓰지 않는다.
    • 힌트: if 조건절에서 값을 return 하는 방식으로 구현하면 else를 사용하지 않아도 된다.
    • else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
  • 도메인 로직에 단위 테스트를 구현해야 한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다.
    • 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다.

✏️ 과제 진행 요구 사항

  • 기능을 구현하기 전 docs/README.md에 구현할 기능 목록을 정리해 추가한다.

  • Git의 커밋 단위는 앞 단계에서 docs/README.md에 정리한 기능 목록 단위로 추가한다.


진행 과정

이번에도 작성한 코드에 대한 설명을 다 진행하기에는 글이 너무 길어질 것 같아서 과제를 진행하면서 있었던 일들 위주로 소개를 하려고 한다.

주요 기능에 대한 목록 고찰

문제 요구 사항대로 최대한 한 가지의 기능을 담당할 수 있도록 목록을 고찰하려고 노력하였다.
예를 들어서 게임 시작 문구 출력 기능이 있으면 그것에 대한 메서드를 만들고 출력 관련 기능을 담당하는 클래스에 담는 방식으로 생각하면서 기능 목록을 구상하였다.

지금까지 계속 기능 목록을 작성해봤으므로 기능 목록을 정리하는데는 크게 어려움이 없었다.

기능 구현 후 테스트 코드 작성 병행

지난 3주 차에서는 모든 기능들을 구현 후 그에 따른 테스트 코드를 한번에 작성하는 방식으로 진행했었다면 이번 4주 차 과제에서는 기능을 생성하고 그 기능이 제대로 동작하는지 테스트 코드를 작성하는 방식으로 과제를 진행했다.
테스트 코드를 통해서 기능 오류가 발견되면 기능을 수정하고 커밋 컨벤션에 따라서 커밋을 하였다.

기능 목록 수정이 있을 때마다 작성했던 기능 목록을 고치기

기능 목록은 처음부터 항상 완벽하진 않기에 코드 작성시 중간에 항상 추가적으로 필요한 기능이 있는 경우도 있었고, 기능의 역할 수정이 필요한 경우도 있었다.
예를 들어서 단순히 다리를 생성하는 기능을 기능 목록에 처음에 작성했었다면, 다리를 생성하기 위해서 세부적으로 어떤 기능이 필요할지 추가적으로 기능이 필요했다. 그에 따른 추가 가능 사항들을 다시 정리하고 기능 목록을 항상 라이브성을 유지하기 위해서 실시간으로 업데이트 하는 방식으로 과제를 진행했다.

파일 구조를 변경하는 등 리펙토링 작업 진행

주어진 클래스 파일을 다른 패키지로 이동하는 작업 및 코드 변수명 변경 등의 리펙토링 작업과 클래스 내에서 작성했던 메서드를 다른 클래스로 옮기는 리펙토리 작업을 하며 과제를 진행했다.

과제 진행 중 어려웠던 사항

주어진 클래스 및 메서드를 활용하기 위한 고찰의 어려움

3주 차 과제는 단순히 자기 자신만의 힘으로 클래스와 메서드를 알아서 구현해서 문제를 해결하면 됐으나, 4주 차는 몇 개의 클래스와 메서드가 주어진다.

각 클래스에 대한 요구 사항(기능 구현 유의 사항 설명에는 기재하지 않았다. 생성자, 필드 등의 요구 사항)과 그 클래스가 가지고 있는 메서드를 활용해서 어떻게 내가 구상한 기능들을 구현을 해야할지 고민하는데 많은 시간이 걸렸다.

다리 건너기 게임의 결과 출력에 대한 구현

다리를 생성하고 다리 건너기를 진행하기 위한 기능 구현은 어렵지 않았다.
다만 다리 건너기 게임 진행 중 현재 내가 건너고 있는 다리의 상황을 출력으로 나타낼 때 어떤 방식을 사용해야 할지 고민을 했지만 쉽게 방법이 떠오르지 않았다.

어려웠던 과정들을 해결하기 위한 노력

처음에는 아무것도 작성할 수가 없을 만큼 막막하고 멍때리는 경우가 많았다. 한편으로는 내가 생각했던 방식으로 작성을 했는데 작성하는 과정 중에서 또다른 오류가 발생하거나 막히면 곤란할 것 같다는 그 불안감이 나의 코드 작성에 영향을 미치면서 무기력한 상황에 처해지기도 하였다.
그렇지만 무기력하게 아무것도 하지 않으면 처음부터 과제를 안하는 상황과 다름없기에 일단은 이것저것 부딪혀보고 왔던 길을 되돌아가면서 피드백을 차차 쌓아가며 코드 작성을 하는 것이 낫겠다는 생각이 들었다.
그래서 일단은 계속 작성을 해보고 이 코드는 어떤 방식으로 구현 방식을 하는 것이 좋을지 보완하는 방식으로 코드를 작성하였다.
그런 방식으로 코드를 완성도 있게 작성할 수 있는 단계까지 도달하여 좌절을 딛을 수 있다는 성취감을 가질 수 있었고, 4주 차 과제도 내 자신만의 힘으로 해결할 수 있었다.

profile
코어의 서버 탐험기

0개의 댓글