2주차 – 자동차 경주

Github – 2주차 자동차 경주 프로젝트

이번 피드백을 통해 내가 몰랐던 부분이 많다는 것을 깨달았다. 피드백 자료를 보면서 부족한 점을 정리하는 데 5일을 썼고, 겨우 2주차 자동차 경주 프로젝트를 시작할 수 있었다. 시험과 졸업 작품 준비도 병행해야 해서 시간이 부족했지만, 최대한 시간을 피드백 내용을 정리하고 공부하는 데 보냈다. 이제 중간고사가 끝났으니 다시 한번 집중하고, 이번 주 졸업 작품까지 마무리하면 좀 더 몰입할 수 있을 것이라고 생각한다.

이번 주 미션 내용은 다음과 같다.

학습 목표

  • 여러 역할을 수행하는 큰 함수를 단일 역할을 수행하는 작은 함수로 분리한다.
  • 테스트 도구를 사용하는 방법을 배우고 프로그램이 제대로 작동하는지 테스트한다.
  • 1주 차 공통 피드백을 최대한 반영한다.

미션 간단 설명

해당 미션은 자동차 경주 게임을 코드로 구현하는 것이다. 게임은 사용자가 입력한 자동차 이름을 기반으로 여러 대의 자동차가 경쟁하는 방식으로 진행된다. 컴퓨터는 1부터 9까지의 서로 다른 숫자로 이루어진 3자리 수를 랜덤하게 생성하며, 사용자는 주어진 횟수 동안 자동차가 전진하거나 멈추는 상황을 맞춰야 한다.

자동차는 주어진 횟수 동안 전진할 수 있는지 여부를 결정하기 위해 0에서 9까지의 랜덤 값을 생성한다. 이 값이 4 이상일 경우 자동차는 전진하게 된다. 사용자는 각 자동차의 이름을 쉼표로 구분하여 입력하며, 자동차의 이름은 최대 5자까지만 허용된다.

게임이 끝난 후에는 누가 우승했는지를 알려주며, 우승자가 여러 명일 경우에는 이름을 쉼표로 구분하여 출력한다. 잘못된 입력이 들어올 경우에는 IllegalArgumentException을 발생시켜 애플리케이션을 종료하게 된다.

입출력 예시

경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
시도할 횟수는 몇 회인가요?
5

실행 결과
pobi : -
woni : 
jun : -

pobi : --
woni : -
jun : --

pobi : ---
woni : --
jun : ---

pobi : ----
woni : ---
jun : ----

pobi : -----
woni : ----
jun : -----

최종 우승자 : pobi, jun

요구사항 분석

과제 요구사항

  1. 기능 목록 작성: README.md에 기능 목록을 구현한 후 구현을 시작한다.
  2. 커밋 단위: 기능 목록을 기준으로 커밋을 추가한다.
  3. 커밋 메시지: AngularJS Git Commit Message Conventions를 참고하여 작성한다.

프로그래밍 요구사항

  • 환경 설정: build.gradle 파일은 변경할 수 없으며, 제공된 라이브러리 외의 외부 라이브러리는 사용하지 않는다.
  • 종료 메서드: 프로그램 종료 시 System.exit()를 호출하지 않는다.
  • 함수 크기: 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어야 한다.
  • 3항 연산자 사용 금지: 3항 연산자는 사용하지 않는다.
  • 들여쓰기 규칙: indent(인덴트, 들여쓰기) depth는 3이 넘지 않도록 구현하고 2까지만 허용한다.
  • 테스트 코드 작성: JUnit 5와 AssertJ를 사용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트한다.

기능 목록

  • 사용자 입력
    • camp.nextstep.edu.missionutils.ConsolereadLine()을 사용하여 사용자 입력을 처리한다.
    • 사용자는 자동차 이름을 쉼표(,)로 구분하여 입력해야 한다.
pobi,woni,jun

사용자는 몇 번의 이동을 할 것인지 입력해야 한다.

5

잘못된 값을 입력할 경우 IllegalArgumentException을 발생시키고 애플리케이션을 종료해야 한다.

  • 자동차 입력
    • 각 자동차에 이름을 부여하며, 전진하는 자동차를 출력할 때 자동차 이름도 함께 표시한다.
    • 자동차 이름은 쉼표(,)로 구분되며, 5자 이하만 가능하다.
  • 자동차 이동
    • 주어진 횟수 동안 n대의 자동차가 전진하거나 멈출 수 있다.
    • 전진 조건은 0부터 9까지의 무작위 값이 4 이상일 경우이다.
    • camp.nextstep.edu.missionutils.RandomspickNumberInRange()를 사용하여 무작위 값을 추출한다.
    • 자동차 이동 차수별 실행 결과를 출력한다.
pobi : --
        woni : ----
        jun : ---
  • 우승자 출력
    • 자동차 경주 게임을 완료한 후, 누가 우승했는지를 알려준다.
    • 우승자는 한 명 이상일 수 있으며, 여러 명일 경우 쉼표(,)로 구분한다.
    • 단독 우승자 안내 문구는 다음과 같다:
최종 우승자 : pobi

공동 우승자 안내 문구는 다음과 같다:

최종 우승자 : pobi, jun

테스트 코드 작성

이번 주 학습 목표는 테스트 도구 사용법을 배우는 것이다. 기능 목록에 따라 구현 시 각각의 테스트 코드 작성하면서 검증했다.

JUnit5 테스트 코드 작성법을 정리한 내용은 아래에서 확인할 수 있다.
[[codingPractices] JUnit5 테스트 코드 작성법](https://velog.io/@grayson1999/codingPractices-JUnit5-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EB%B2%95)

  • 기능 테스트: 기능_테스트() 메서드는 자동차 경주가 올바르게 실행되는지를 검증한다. pobiwoni 두 자동차의 1회 이동 후 결과를 검증한다.
@Test
    void 기능_테스트() {
        assertRandomNumberInRangeTest(() -> {
            run("pobi,woni", "1");
            assertThat(output()).contains("pobi : -", "woni : ", "최종 우승자 : pobi");
        }, MOVING_FORWARD, STOP);
    }
  • 입력값 검증 테스트: 입력값_검증_테스트() 메서드는 사용자 입력이 유효한지 확인한다. null값 또는 빈값, 예상하지 못한 문자 입력에 대해 예외처리 되고 있는지 확인한다.
@Test
    void 입력값_검증_테스트() {
        assertAll(
            () -> assertThatThrownBy(() -> Application.validateInput("woni,pobi", "1a"))
                .isInstanceOf(IllegalArgumentException.class),
            () -> assertThatThrownBy(() -> Application.validateInput("", "1"))
                .isInstanceOf(IllegalArgumentException.class)
        );
    }
  • 자동차 이동 테스트: 자동차_이동_테스트() 메서드는 자동차의 이동 거리를 업데이트하고 결과를 검증한다.
@Test
    void 자동차_이동_테스트() {
        List<String> carNames = Arrays.asList("pobi", "woni", "gray");
        LinkedHashMap<String, Integer> carDistanceResults = new LinkedHashMap<>();

        for (String carName : carNames) {
            carDistanceResults.put(carName, 0);
        }

        Application.updateDistance(carDistanceResults, "pobi", MOVING_FORWARD);
        Application.updateDistance(carDistanceResults, "woni", MOVING_FORWARD);
        Application.updateDistance(carDistanceResults, "gray", STOP);

        assertThat(carDistanceResults.get("pobi")).isEqualTo(1);
        assertThat(carDistanceResults.get("woni")).isEqualTo(1);
        assertThat(carDistanceResults.get("gray")).isEqualTo(0);
    }
  • 우승자 출력 테스트: 여러_우승자_출력_테스트()혼자_우승자_출력_테스트() 메서드는 우승자가 올바르게 출력되는지를 확인한다.
@Test
    void 여러_우승자_출력_테스트() {
        List<String> winners = List.of("pobi", "woni");
        Application.printWinner(winners);
        assertThat(output()).contains("pobi, woni");
    }

회고

  • 목표 달성률: 나는 꾸준한 성장과 특히 좋은 습관을 만드는 것을 목표로 하고 있으며, 이번 프리코스의 목표 달성률은 80%라고 생각한다. 주어진 학습 목표에 맞춰 미션을 수행하면서, 모르는 부분은 블로그에 정리하여 시각화하고 있다. 1주차에는 Git을 주제로, 2주차에는 개발 규칙을 주제로 총 6개의 블로그를 작성했다. 이를 통해 모르는 것을 지나치지 않고 정리하는 습관을 기르고 있다. 프리코스 시작 전에는 어떤 것부터 시작해야 할지 몰라 여러 가지를 깊이 있게 공부하지 못했으나, 이제는 중요한 주제에 대해 체계적으로 접근하고 있다. 이로 인해 전보다 더 나은 이해가 이루어지고 있다고 생각한다.

목표가 80%인 이유는 완벽주의 성격 때문인데, 프로젝트 진행 중 피드백 내용을 리뷰하고 모르는 부분을 정리하는 데 많은 시간을 소모했다. 2주차에 중요한 내용인 테스트 코드와 객체 지향에 대해 깊이 공부할 시간이 부족했음을 깨달았다. 앞으로는 공부할 것이 많을 때 우선순위를 정해 학습하는 방향으로 개선하고자 한다.

프리코스를 통해 얻고자 하는 가장 큰 목표는 좋은 개발자가 되기 위한 기본기를 다지고, 좋은 사고방식과 습관을 내 것으로 만드는 것이다. 기술적 성장은 물론, 매일 조금씩 더 나은 자신을 만들어가는 과정을 통해 의미 있는 성취를 이루고 싶다.

  • 목표 변경 필요성: 이번 프리코스를 진행하면서 구현 기술보다 더 중요한 것이 있다는 것을 깨달았다. 구체적으로, 개발 규칙과 협업도 중요하다는 점을 인식하게 되었다. 따라서 “협업과 개발 규칙을 지키는 개발자가 되는 것”이라는 목표를 추가하고 싶다. 이러한 목표를 통해 좋은 개발자로서의 기본기를 다지고, 실질적인 협업 능력을 키우고자 한다.
  • 눈에 띄는 변화: 프리코스 시작 전에는 어떤 공부가 중요한지 파악하기 어려워 여러 공부를 동시에 했었다. 또한, 기술적인 공부와 이론적인 공부 중 무엇이 먼저인지 결정하기 힘들어 깊이 있는 학습을 하지 못했던 것 같다. 그러나 이번 프리코스를 통해 추천된 중요한 내용을 우선적으로 체계적으로 공부하면서 이해하는 능력이 크게 향상되었다. 또한, 프로젝트를 시작하면서 나의 부족한 부분이 드러났고, 피드백을 받으면서 “왜”라는 질문을 통해 깊이 있는 학습을 하게 되었다. 이러한 점이 가장 큰 변화라고 생각한다.

제출

위로 스크롤