외부활동/우아한테크코스 [프리코스]

[우아한테크코스 7기] 프리코스 2주차 회고

softmoca__ 2024. 10. 29. 14:14
목차

https://github.com/woowacourse-precourse/java-racingcar-7/pull/141

 

[자동차 경주] 최영철 미션 제출합니다. by softmoca · Pull Request #141 · woowacourse-precourse/java-racingcar-7

제 코드를 보려와주셔서 감사합니다. ☺️ 제 코드를 읽으시면서 가독성이 떨어지시거나 코드의 의도를 파악하기 힘드셨던 부분은 강하게 피드백 해주시면 감사하겠습니다 ! 코드 리뷰 하시면

github.com

 

시작하며 

 

 

요즘 지하철을 타거나, 밥먹으면서도, 학교 수업을 들는 중에도 프리코스 미션에 대해 생각을 할 정도로 프리코스에 푹 빠저 몰입을 하고 있다. 1주간의 적응 후 정말 온전히 미션을 통해 성장하는 요즘이 너무 좋다 !

 

1주차 미션이 끝나고 스터디원들과 코드리뷰를 하는 과정 중 나의 코드에 대해 명확하게 근거를 통해 설명을 하지 못하는 나를 보며 아직 메타인지을 잘 활용하지 못하고 있따고 느꼈다.

또한 나보다 많은 경험을 가진 스터디원의 코드를 보며 논의하며  너무 나의 코드가 허접하고 부족하게 느껴져 작아지는것을 느끼며 작아지며 마음이 촉박해지기도 하였다.

하지만 2주간의 시간을 통해 많은 성장을 하고 있는것을 내가 제일 잘 느끼고 있어 조바심을 가지지말고 다시한번 어제의 나, 저번주차의 나와 비교해 성장한 점에 집중을 하기위해 노력했다.

 

또한 2주차의 자동차경주 미션을 진행하며 기능 구현에 급급해 새로운 기술스택을 많이 적용해보는것에 급급해하지 않고, 미션에서 마주한 문제들을 나 자신만의 근거를 통해 개선하는것에 더욱 초점을 맞추었다.

그러한 2주간의 시간을 통해 현재 가장 크게 느끼는것은 객체지향적인 사고를 할수 있게 된것이다.

그저 특정 입력에 특정 결과를 출력하는 로직을 여러개 이어붙이는것이 아닌 객체를 인식하고 행동과 메세지 책임에 대해 고민하는 나를 통해 새로운 시야를 얻어 나름 너무 뿌듯하였다.

 

 

고민한 점

1️⃣  매직넘버 상수의 관리

2주차 자동차 경주 게임 미션을 진행하며 특정한 정수값을 사용해야하는 경우가 많았다.

자동차 이름최대 제한인 5

랜덤값의 최소값 0 

랜덤값의 최대값 0

이동할수 있는 기준값 4

 

그게 두가지 방향에 대해 고민을 하였다.

1. 상수를 필요로 하는 도메인에서 매직넘버를 관리

2. global 패키지에서 매직넘버를 관리

 

1번 방법의 경우 해당 도메인 내에서 코드를 읽다가 파일(클래스)의 이동없이 바로 스크롤을 통해 해당 상수가 어떤 역할을 하며 어떤 값을 가질수 있는지 확인할 수 있으며, 요구사항이 수정될 경우 해당 클래스만 수정하면 되어 유지보수성이좋다.

 

2번 방법의 경우는 상수 자체를 관리하는 패키지는 유일하게 하나만 존재하니 상수와 관련된 수정 사항이 있을 경우 해당 상수 관리 패키지만 수정을 할수 있으며, 해당 프로젝트에서 어떠한 상수들이 있는지 한번에 확인할수 있다.

 

각각의 방법 마다 장단점이 분명이 있음을 인지하였고, 나만의 근거를 통해 결국 2번을 통해 구현하였다.

2번을 택한 근거로서, 2번으로 구현을 하더라도 다른 도메인에서 상수를 사용하 '명확한 상수명'을 사용한다면 굳이 파일(클래스)간 이동을하지 않을수 있다고 생각했다. 

또한 에러메세지와 view 입출력 메시지 또한 global 패키지에서 enum으로 관리를 했기에 통일감을 줄수 있다고 생각했다.

 

1번과 2번을 모두 사용하는 것에 대해서도 고려를 해보았지만 프로젝트 전체의 '통일성'이 마음에 걸려 우선은 2번으로 구현을 하였고 리뷰어분들의 의견을 듣기 위해 PR 커멘트에 기록을 하였다.

 

2️⃣ 너무 무거운 Cars의 책임

미션을 제출하기 앞서 계층별로 어떠한 흐름을 통해 프로그램이 동작하는지 시각해 보았다.

 

피그마를 통해 제작하며 Cars에 너무 많은 역할을 부여했다는 생각이 들었고, 어떻게 개선을 해볼까 고민하며 새로운 역할을 가질 객체를 구상해 보았다.

WinnerResuet 클래스를 도입하여 우승자를 찾기 위한 역할을 위임해보려고 하였지만 오히려 여러 클래스를 이동하며 프로그램의 전체 흐름을 파악하는데 가독성이 저해된다고 생각하였다.

그래서 새로운 객체 도입은 하지 않고 ParsStringUtils와 RaceRandomUtils와 같은 유틸 패키지로 무거운 역할을 약간 덜어주는 것으로 마무리 하였다.

지금 당장은 유틸 클래스들과 Car와 Cars로 미션의 기능요구 사항을 충족하기에는 과하지도 부족하지도 않다고 생각했다.

하지만 이후 여러 요구사항이 추가되는 경우를 고려해 더 세분화된 객체를 도입하는것 또한 필요하다고 느껴 다른 스터디원들은  프로그램 전체의 가독성과 유지보수성 중 어떠한 점을 더 챙겼으며 어떤 근거가 있었는지 의견을 공유 받았다.

 

배운것 및 잘한점

1️⃣  1순위는 주어진 요구사항 만족 ! 남은 시간에  클린코드와 예외 처리!

1주차 공통 피드백 중 '문자열 계산기'에 대한 피드백 영상에서 코치님이 하신 말씀이다.

클린코드와 예외는 미션 처음 부터 완벽히 구상하는것이 아닌, 주어진 요구사항을 만족한 뒤 미션을 진행하며 하나씩 떠오르는 클린코드 사항 및 예외처리를 기록하고, 주어진 요구사항을 모두 만족 시킨 이후에 해당 작업을 하라고 하셨다.

 

1주차에 예외처리에 크게 데인 나에게 너무나 소중한 피드백 내용이었다.

2주차 미션은 해당 피드백에 따라 주어진 요구사항을 모두 만족한 뒤 예외사항과 클린 코드 기법을 적용하며 불필요한 몰입을 최소한으로 하며 많은 성장을 할수 있었다.

 

2️⃣ 축약하지 않고, 이름을 통해 의도 드러내어 가독성 올리기

1주차 코드 리뷰의 피드백 사항으로 네이밍에 관해 피드백을 받았었다.

위 코드에서는 calculate라는 클래스 내부에 다시 한번 calculate라는 이름을 중복적으로 기재한 점이었다.

또한 1주차 공통 피드백 사항의 '이름을 통해 의도를 들어낸다, 축약하지 않는다'라는 피드백을 적극적으로 받아들여 메서드의 네이밍에 많은 신경을 썼다.

 

위와 같이 코드리뷰를 통해 네이밍에 대해 신경을써서 개선한 점에 대해 리뷰를 받아 너무 뿌듯했다 ㅎㅎ

 

 

 

3️⃣ 메서드를 추출하여 indent 줄이며 가독성 올리기

1주차 미션에서 코드 전반적으로 else와 중복되는 로직이 많아 가독성이 낮아 얼리리턴과 private 메서드 추출에 대한 피드백을 받아 적용해 보았다.

코드 전반적으로 중복되는 로직이 없는지 확인하는데 시간을 많이 투자했으며 그 결과 아래와 같이 외부 객체에서 사용하는 하나의 pulic 메서드에서 내부의 얼리리턴으로 구현한 private 메서드를 사용하는 로직들을 구현하였다.

많은 시간을 투자한 결과 확실히 가독성이 좋아졌으며 이후 로직에 문제가 생겼을 경우 디버깅 또한 매우 효과적이어서 뿌듯하였다.

public List<Car> findWinnerCars() {
        int topPosition = findTopPosition();
        List<Car> winners = new ArrayList<>();

        for (Car car : carList) {
            addCarIfTopPosition(winners, car, topPosition);
        }
        return winners;
    }

    private void addCarIfTopPosition(List<Car> winners, Car car, int topPosition) {
        if (car.getPosition() == topPosition) {
            winners.add(car);
        }
    }

    private int findTopPosition() {
        int maxPosition = 0;
        for (Car car : carList) {
            maxPosition = updateMaxPosition(maxPosition, car);
        }
        return maxPosition;
    }

    private int updateMaxPosition(int currentMax, Car car) {
        if (car.getPosition() > currentMax) {
            return car.getPosition();
        }
        return currentMax;
    }

 

 

4️⃣ 매직넘버 상수 관리를 관리하며 가독성 올리기

처음에는 매직 넘버들을 상수로 관리하는 이유에 대해 큰 공감을 하지 못하였다.

하지만 좋은코드나쁜코드 스터디를 통해 매직넘버에 대해 공부를 하며 클린코드 기법은 '현재의 나 자신' 뿐만 아니라 '미래의 나', '팀원'을 위해 꼭 필요하다는 것을 깨달게 되었다.

프로그래밍을 하는 나는 가이드를 보며 구현을해서 4라는 값이 무슨 의미를 가지는지 알고 있지만 처음 코드를 보는 사람의 입장에서는 그저 정수 4 그이상, 그이하도 아니며 해당 4의 의미를 이해하기위해 코드 전반적인 문맥을 살펴야함을 알게되었다.

 

그 결과 아래같은 매직넘버를 상수로 관리하며 어떤 동작을 하는지 코드만 보더라도 이해를 할수 있는 로직을 구현할수 있었다

.

 if (name.length() > MAX_CAR_NAME_LENGTH_NUMBER.getValue())

 if (RaceRandomUtils.generateRandomNumber() >= MIN_FORWARD_NUMBER.getValue())

 

 

5️⃣ Controller의 적절한 역할 부여

왼쪽 코드는 1주차에 작성했던 Controller이다.

2주차에 비해 훨씬 적은 기능을 가졌지만 상당히 무거우며, 흐름을 파악하는데 꽤나 시간이 걸리는 코드이다.

2주차의 경우 Model에 책임을 더 많이 위임하며 훨씬 Controller가 가벼워졌으며 추상화 레벨을 동일하게 맞춰 가독성을 높혀 프로그램 전체를 훨씬 파악하기 쉬워졌다.

가독성을 올리기 위해 한 노력들이 코드 리뷰를 통해 다시 한번 드러나니 너무 뿌듯하였다 

 

 

보완해야할 점 아쉬운점  

1️⃣  출력시 String 메서드 활용

위 피드백과 같이 출력에 대해 String의 메서드를 사용하면 더 가독성을 올리수 있다는 사실에 대해 알게 되었다.

그결과 아래와 같은 메서드들을 String이 가지고 있는것을 알게 되었고 이후 3주차에는 해당 메서드들을 활용해 보려한다.

// 방법 1: String.format 
System.out.println(String.format("%s : %s", 
    car.getName(), 
    "-".repeat(car.getPosition()))
);
// 방법 2: formatted()
System.out.println("%s : %s"
    .formatted(car.getName(), "-".repeat(car.getPosition()))
);

 

 

2️⃣ 일급 객체 , 원시값 포장

 

PR 커멘트에 Cars에 역할이 무거운 점에 대한 의견으로 일급 객체와 원시값 포장이라는 방법을 도입해 보면 도움이 될꺼 같다는 피드백을 받았다. 처음 알게된 개념으로 짧게 살펴본 결과 2주차 미션을 하며 고민한 무거운 역할을 덜어줄 좋은 무기라고 생각되어 해당 개념을 공부한 후 3주차에 꼭 적용해보고자한다.

 

3️⃣ 정적 팩토리 메서드

가독성과 안정성, 성능 세마리 토끼를 모두 잡을수 있는 정적팩토리메서드 패턴의 도입에 대한 피드백을 받았다.

2주차에 신경을 가장 많이쓴 가독성을 챙길 수 있으며 또한 생성자 또한 private으로 관리할수 있어 안정성이 올라가며 매번 인스턴스를 생성하지 않아도 되는 엄청난 장점을 가진 개념이라 생각된다 !
3주차에는 객체를 생성할 때 정적팩토리 메서드를 활용해 보아야겠다 !