[우아한테크코스 3기] 2주차 프리코스: 자동차 경주 게임 후기

Junseo Kim·2020년 12월 13일
2

1주차 프리코스 피드백

1주차 프리코스를 진행 후 2주차 미션과 함께 공통적인 피드백을 보내주셨다. 그중 스스로 부족하다고 생각했던 부분을 적어보려한다.

개발 도구의 code format 활용

개발 도구에 code format을 어느정도 맞춰주는 기능이 존재했다.
나는 Mac과 IntelliJ를 사용하기 때문에 단축키는 cmd + opt + L이다.

불필요한 공백 라인 만들지 않기

공백 라인을 사용할 때는 문맥이 달라지는 부분에 의도를 가지고 띄워야한다.

기능 목록

기능 구현 목록은 구현을 하면서 얼마든지 변경될 수 있기 때문에 시작할 때 너무 완벽하게 작성하려 하기보다는 구현하면서 동시에 계속 업데이트 해주는 것이 좋다!

너무 상세하게 작성할 필요는 없다.

예외사항도 기능 목록에 적어줘야한다. 새로운 예외 사항이 생각나면 계속 업데이트해준다!

나머지 피드백

이 부분은 나름 구현할 때 신경 쓴 부분들이지만 혹시 다른 분들에게 도움이 될까봐 적어놓습니다.

  • 클래스, 메소드, 변수 이름을 짓는데 시간을 많이 투자해야하며 축약하지 않는 것이 좋다.
  • for, while, if 와 괄호 사이의 space도 코드 컨벤션이다!
  • 클래스는 1) 상수 or 클래스 변수 2) 인스턴스 변수 3) 생성자 4) 메소드 순으로 구현하는 것이 좋다.
  • 반복 금지!
  • 들여쓰기 시 space와 tab 같이 쓰지 않기
  • 의미없는 주석 x
  • 하드 코딩 x
  • git commit 메세지 의미있게 작성하기

2주차 프리코스: 자동차 경주 게임

2주차 프리코스는 자동차 경주 게임 구현이었다. 1주차 요구사항에 몇 가지 요구사항이 더 추가되었고 1주차 피드백도 신경쓰면서 구현했다. 또 1주차 프리코스의 목적이 함수 분리였다면 2주차는 클래스 분리 + 함수 분리가 목표였다.

문제 설명과 제출한 코드는 => 여기로 가시면 볼 수 있습니다!

메소드 길이 제한

요구사항 중 메소드 길이가 15라인이 넘지 않도록 하라는 요구사항이 추가되었다. 즉 함수가 한 가지 일만 잘 하도록 구현하라는 뜻이었다.

이를 만족하는지 보기 위해서는 메소드 이름을 확인해보는 방법이 있다. 메소드 내부의 로직 중 메소드 이름과 맞지 않는 로직이 존재한다면 해당 로직을 또 다른 메소드로 분리할 수 있다.

메소드가 한 가지 일만 하게 된다면 유지보수도 쉬워진다~~
(참고: https://woowacourse.github.io/javable/2020-05-10/single-job-method)

else 예약어 쓰지 않기

지금까지 else를 쓰지 않아본 적이 없다. 그만큼 많이 사용했었다. else 예약어를 왜 사용하지 않는 게 좋은지가 궁금해서 찾아봤다.

else를 사용하는 것에 비해 간결하고 명확해진다 고 한다. (이런 글을 보고나니 else가 존재하는 코드가 갑자기 보기 안좋아졌다 ㅎ..)

이를 해결 하기 위해 early return을 사용할 수 있다.
early return이란 if 조건문에서 값을 바로 return 해주는 방법이다.
(참고: https://woowacourse.github.io/javable/2020-07-29/dont-use-else)

일급 컬렉션 사용하기

이번 미션을 진행해보면서 일급 컬렉션이라는 개념을 알게되었고 '경주에 참여하는 자동차 리스트'에 이를 적용해 볼 수 있을거라는 생각이 들어서 한 번 적용해봤다.(잘 적용했는지는 잘 모르겠다ㅎㅎ...)

일급 컬렉션은 컬렉션 변수를 제외한 다른 멤버 변수가 없는 클래스를 말한다.

List<Car> carList = new ArrayList<>();
carList.add(car1);
carList.add(car2);
carList.add(car3);
...

위의 리스트를 아래와 같이 클래스를 만들어서 사용했다.

public class ParticipatingCars {
    private List<Car> cars;
    
    public ParticipatingCars(List<Car> cars) {
        this.cars = cars;
    }
}

이렇게 일급컬렉션을 적용하면 사용자가 필요한 조건을 만족시키는 자료구조로 사용할수도 있고, 불변성을 유지할수도 있고, 상태와 로직을 한 번에 처리할 수도 있으며, 클래스 이름으로 명확한 표현이 가능하다.
(참고: https://jojoldu.tistory.com/412)

getter 사용하지 않기

객체는 메세지를 받아 로직을 수행하고 필요하다면 객체 내부의 값을 변경시키기도하게 동작해야한다. 즉 객체 자체가 스스로 작업을 해야한다.

하지만 getter를 무분별하게 사용하게되면 객체가 스스로 작업을 하지 않고 외부에서 작업이 일어나게 된다. 이를 막기 위해 getter를 무분별하게 사용하지 말라는 뜻이다.

이번 미션에서 경주가 끝난 후 승자를 가리는 부분에서 getter를 사용하지 않고 코딩해보았다.

    public List<String> findWinner(ParticipatingCars allRacingCars, int leadPosition) {
        List<String> winners = new ArrayList<>();

        for (Car car : allRacingCars.getCars()) {
            if (car.getPosition() == leadPosition) {
                winners.add(car.getName());
            }
        }

        return winners;
    }

위와 같이 getter를 이용해 외부에서 로직을 처리하지 않고, 아래처럼 객체 자체에 메세지를 보내 객체가 로직을 처리하도록 구현했다.

    public List<String> findWinner(ParticipatingCars allRacingCars, int leadPosition) {
        List<String> winners = new ArrayList<>();

        for (Car car : allRacingCars.getCars()) {
            if (car.isWinner(leadPosition)) {
                winners.add(car.getName());
            }
        }

        return winners;
    }

(참고: https://woowacourse.github.io/javable/2020-04-28/ask-instead-of-getter)

패키지 구조

이번 과제를 진행하면서 패키지를 한 번 잘 나눠보자는 생각이 들었고 패키지를 나누는 2가지 방법을 알게되었다.

  1. package by layer
    layer를 기준으로 패키지를 나누는 방식이다.
racingcar
    controller
    model
    view
  1. package by feature
    feature를 기준으로 패키지를 나누는 방식이다.
racingcar
    car
    race
    round

두 가지 방법 중 package by feature가 높은 모듈성, 하이 레벨 추상화 같은 이유로 더 좋은 방법이라는 글을 보았고 package by feature을 적용해보았다.

경주를 진행하는 자동차를 관리하는 car, 경주를 관리하는 race, 각 경주 라운드를 관리하는 round가 필요하다고 생각하여 위와 같이 패키지를 분리해봤다.
(참고: http://www.javapractices.com/topic/TopicAction.do;jsessionid=0BF4844350780B6F55476E1137FF4893?Id=205)

클래스 분리

클래스 분리를 잘 해보기 위해 공부를 하다가 '객체지향 생활 체조'와 'SOLID 원칙'을 알게되었다. 그 중 클래스와 관련된 규칙은 아래와 같다.

  • 모든 entity를 작게 유지한다.(50줄 이상 x)
  • 2개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  • 모든 클래스는 각각 하나의 책임만 가져야 한다.

여기서 모든 클래스는 각각 하나의 책임만 가져야 한다.는 규칙이 무슨 말인지는 알겠는데 적용해보려니까 헷갈렸다.

큰 메소드 별로 클래스를 모두 분리해야하나? 라는 생각까지 들었고 제대로 이해하지 못한 상태로 클래스 분리를 도전했다.

경주를 관리하는 race 패키지에서 전체적인 흐름이 진행된다고 생각하여 controller를 하위에 두어 관리했고 실질적인 로직 구현은 경주 준비, 경주 진행, 결과 확인의 단계로 나눠 볼 수 있다고 생각하여 3개의 service 클래스를 만들어주었다. 그 후 필요할 때, car 클래스와, round 클래스를 가져와서 race에서 사용하는 식으로 구현했다.

결국 이번 미션에서는 클래스 분리를 제대로 적용하지 못했던것 같다ㅠㅠ

총평

학교 마지막 과제가 끝나 1주차 프리코스에 비해 많은 시간을 투자할 수 있었다. 역시 시간을 투자한만큼 더 많이 얻어가는 것 같다. 나름 고민을 많이 하고 패키지 구조 설정과 클래스 분리를 시도해봤는데 솔직히 제대로 적용했는지는 잘 모르겠다. 2주차 미션 진행 후 피드백이 도움이 될 것이라 생각하고 3주차 과제때 좀 더 나은 패키지 구조와 클래스 분리를 하고 싶다. 이제 2번째 과제지만 점점 코드가 체계가 잡혀가는 것 같고 스스로 성장하고 있는 것이 느껴져서 재미있다. 과제를 진행할수록 점점 우테코에 합격해서 더 많이 성장하고 싶다는 생각이 강해진다.

0개의 댓글