벌써 우테코 시작하고 2주가 지났다. 나는 1,2주 회고를 같이 작성하려 한다. 다른 특별한 이유가 있진 않고… 1주차 소감문을 너무 짧게 쓴게 걸렸다. 소감문을 쓰고나서 이렇게 회고글을 작성하고, 해당 글을 링크해도 된다고 알게 됐다. 그래서 아쉬움에 1~2주 회고록을 작성한다. 내용이 중요하겠지만 이렇게라도 아쉬움을 채우고 싶다.


우테코 적응 하기

1. 꼼꼼함

우아한 테크 코스 프리코스 1주차 미션이 끝났다. 우테코 1주차 미션은 미션과제 요구사항을 최대한 구현하기와 우테코에 적응하기였다. 요구사항은 과제에 대한 기능요구사항 말고도 프로그래밍 요구사항, .. 요구사항이 더 있었다. 심지어 메일에도 미션에 대한 내용이 있다. 꼼꼼히 보지 않으면 놓치는게 많아질 것이다. 이 부분은 설명회와 사전 OT에도 언금되었다. 메일과 요구사항을 많이 보고 미션을 진행해달라고 했다. 꼼꼼하지 않으면 테스트를 통과하지 못하는 등 예기치 못한 상황에 빠지게 된다. 분명히 ReadMe에 나와있는 내용인데 디스코드나 카카오 단톡방에 질문으로 올라온다. 개발자의 기본이라고 생각하고, 나 또한 그렇게 안돼서 노력 많이 한다. 어떤 프로젝트더라도 PR을 하기 전에 내 코드를 한 번 더 확인하는 습관을 들이자.

2. 몰입

우테코 평가 요소 중 하나인 몰입이다. 많은 사람들이 우테코 프리코스에 몰입하는 모습에 놀랐다. 디스코드로 자신의 글을 공유하고, 코드를 리뷰해달라고 요청하고, 스터디를 구한다. 또한 의논하고, 토론한다. 자신의 생각을 거리낌 없이 주장하고 피드백을 자연스럽게 받는 모습들이 보기 좋았다. 주니어 개발자들에게는 더할 나위 없이 좋은 곳임에 틀림 없다. 졸업 예정자인 대학생 또는 주니어 개발자인 주변 지인들에게 꼭 추천하고 싶다. 신청해도 시간을 쓰지 않는 사람들도 있다. 프리코스를 하게 되면 우테코 프리코스에 어느 정도 집중을 추천한다.

우테코에 몰입해야하는 이유

여러 잠점이 있겠지만 내가 생각하는 좋았던 장점이다.

  1. 공짜 피드백
    어딜 가서 이런 좋은 피드백을 공짜로 받을 수 없다. 쉽지 않다. 좋은 회사에 들어가더라도 좋은 팀과 좋은 사수를 만나기는 쉽지 않고, 다른 방법으로도 돈을 주고 피드백을 받아야 한다. 물론 피드백 주는 사람의 커리어를 무시할 수 없지만, 나와 같은 2년차 개발자들까지는 충분히 성장할 수 있는 공간이라고 생각한다. 나보다 못하는 사람들도 많고 나보다 잘하는 사람들도 많다.
  2. 객관적인 실력 (내 위치??) 사실 객관적인 실력보다는 내가 부족한 부분을 알 수 있게 된다. PR로 많은 사람들의 코드를 볼 수 있다. 잘하는 사람도 못하는 사람도 성의하는 사람도 다양하다. 꼼꼼하게 보다보면 좋은 코드를 보면서 배운다. 그리고 디스코드에 글과 미션들은 나에게 공부할 것들을 던져준다. “이게 너에게 부족해. 그러니 학습해”라고 말해주는 것 같다.
  3. 학습 방법 공부하고 공부한 내용들을 적용할 수 있는 방법을 알려준다. 사실 이게 잘 안되는 사람들이 많다. 공부는 하지만 공부하고 나면 끝이다. 실제로 내가 코드로 구현해봐야 안다고 9만원 강의에서도 말해주는데, 잘 안된다. 이 방법을 4주동안 한 주씩 반복하며 필요성을 느끼게 해준다.

3. 학습

어떻게 학습해야할까? 2주동안 곰곰히 생각했다. 시간을 잘 쪼개서 학습해야 한다. 하지만 공부할 것들이 너무 많다. 그래서 많이 생각한 끝에 그냥 미션에 집중하기로 했다. 2주차 미션인 테스트 코드만 해도 아직 공부한 내용이 아니기 때문에 다른 것들은 신경쓸 여유가 없다.

  1. 설계와 코드 구현을 일요일전까지 끝내기
    • 2주차에서는 먼저 테스트코드를 학습하니 뒤늦게 설계와 구현에 시간을 쓸 여유가 없어졌다. 그래서 공부한 내용들을 많이 써먹지 못하게 됐다. 그래서 일단 다른 생각안하고 구현을 완료하고, 공부와 리팩토링을 같이 하면서 학습하는게 맞는 것 같다.
  2. 수요일 오전까지 공부와 리팩토링 반복하기
    • 주어진 미션을 생각하면서 필요한 공부를 하고, 코드에 적용시킨다. 이렇게 해야 공부한 내용을 오래 잡아둘 수 있을 것 같다.
  3. 소감문과 코드 리뷰 정리
    • 소감문(회고록)을 쓰면서 구현하며 어렵거나 부족했던 점을 생각하고, 코드 리뷰를 하면서 다시 새긴다. 그리고 리뷰했던 피드백들을 따로 정리한다.

이제 위에 계획으로 나머지 시간을 보낼 생각이다.


1주차 미션 코드 리뷰 - 야구 게임

1주차에 미션을 하면서 객체지향적으로 코드를 구현하고, 테스트 코드를 작성하자는 목표를 가졌었다. Object라는 책을 읽고, 객체의 책임과 역할 그리고 객체들의 협력을 생각하며 설계하고 구현하였지만 잘 안됐다. 사실 책도 한 번 읽고, 책 내용 정리도 안했다. 남은 기억이 없었다😅. 테스트 코드도 시간이 부족한 상황에서 작성하려니 잘 안됐다. 여러모로 아쉬운 1주였다.

1. 노꼼꼼

public boolean isStrike(int size){
     return strike == size ? true : false;
}

public static boolean validInput(String input, String pattern) {
     if (!input.matches(pattern)) {
     return false;
     }
     return true;
}

final private String statement;

꼼꼼하지 못한 코드를 면밀히 보여주는 코드이다. 부끄럽지만 되새기자.

2. View..? Validation..?

public enum ViewType {
    START("숫자 야구 게임을 시작합니다.\n"),
    ROUND("숫자를 입력해주세요 : ", "^[\\d]{3}$"),
    END("3개의 숫자를 모두 맞히셨습니다! 게임 종료\n"),
    RESTART("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.\n", "^[1-2]{1}$");

    private final String statement;
    private String pattern;

    ViewType(String statement) {
        this.statement = statement;
    }

    ViewType(String statement, String pattern) {
        this.statement = statement;
        this.pattern = pattern;
    }

    public String getStatement() {
        return statement;
    }

    public String getPattern() {
        return pattern;
    }
}

View를 어떻게 분리할지 많은 고민을 했다. 인터페이스를 만들어서 다른 여러 개의 구현체를 만들까 생각도 하고, 리플랙션도 생각해보고, 어떻게 하면 중복을 줄일 수 있을가 생각에 치중한 것 같다. 그러다보니 약간 산으로 간 것 같기도 하다. Validation은 View에서 검증하도록 하였는데 사실 백엔드에서는 Model에 들어가기는 하지만 유효성 검증은 프론트엔드에서도 중요시 하기 때문에 입출력에 두었다. 별차이는 없겠지만 바로 피드백할 수 있는 View에 있는게 좋겠다 생각했다. 정답은 없지만 찜찜함이 남은 구현이었다.

3. 리팩토링

public void start() {
     view.println(ViewType.START.getStatement());
     
     while (true) {
        round();
        view.println(ViewType.RESTART.getStatement());
        String isRestart = view.getInput(ViewType.RESTART.getPattern());
        if (isRestart.equals("2")) {
            break;
        }
        process.createRandomBalls(size);
     }
     
     close();
}

어떠 분이 while문을 단순 true가 아닌 게밍의 상태로 처리하고 관리하도록 구현한 코드를 보았다. 상태는 switch문 사용해서 start, round, end 등 메서드 호출을 제어한다. 그렇게 하니까 코드가 더 간결하고 읽기가 쉬었다.

2주차 미션 코드 리뷰 - 레이싱 경주

너무 어려웠다. 구현은 하지만 이렇게 코드를 작성하는게 맞는건가? 생각이 많이 든다. 클린 코드와 리팩토링에 대해서 다시 공부해야겠다. 이후에 테스트 코드를 작성하려할 때 너무 많이 막혔는데 도대체 어떻게 하면 풀 수 있을지 갈피를 잘 못잡겠다. Unit Test라는 책을 샀는데 빨리 읽어버려야 겠다.

1. 자동차

public class RacingCar {

    private String name;
    private int position;

    public RacingCar(String name) {
        this.name = name;
        this.position = 0;
    }

    public boolean moveBy(int random) {
        if (random >= 4) {
            position++;
            return true;
        }
        return false;
    }

    public int getPosition() {
        return position;
    }

    public String getName() {
        return name;
    }

    // 테스트를 위해 추가
    @Override
    public String toString() {
        return "name : " + name;
    }
}

RacingCar 클래스는 자동차이다. 자동차는 전진하지만 제어된다. 그래서 moveBy 메서드를 만들어 random값을 받아서 처리한다. 이렇게 구현한 사람이 많을 것 같다.

2. 트랙

public class Track {
    private final int RANDOM_START = 0;
    private final int RANDOM_END = 9;

    public Map<String, Integer> round(List<RacingCar> racingCars) {
        Map<String, Integer> roundResult = new HashMap<>();
        racingCars.forEach(racingCar -> {
            int random = Randoms.pickNumberInRange(RANDOM_START, RANDOM_END);
            racingCar.moveBy(random);
            roundResult.put(racingCar.getName(), racingCar.getPosition());
        });
        return roundResult;
    }

    public List<String> selectWinners(List<RacingCar> racingCars) {
        List<String> winners = new ArrayList<>();
        int maxPosition = getMaxPosition(racingCars);

        racingCars.stream().filter(racingCar -> racingCar.getPosition() == maxPosition)
                .forEach(winner -> {
                    winners.add(winner.getName());
                });
        return winners;
    }

    private int getMaxPosition(List<RacingCar> racingCars) {
        int maxPosition = 0;
        for (RacingCar racingCar : racingCars) {
            maxPosition = Math.max(maxPosition, racingCar.getPosition());
        }
        return maxPosition;
    }
}

트랙에 역할은 경주에 지원한 자동차들을 받아서 1회차를 진행 시킨다. 그리고 우승자를 가린다. 두 개의 역할이 있는데 우승자를 다른 객체로 역할을 분배해도 될 것 같다.

3. 검증

public class Validation {

   public static boolean validateApplicantCars(String carNames) {
      if (hasSpace(carNames)) {
         throw new IllegalArgumentException("입력 값에 공백 존재");
      }

      if (!isEnglish(carNames)) {
         throw new IllegalArgumentException("영문 외에 글자 존재");
      }

      if (!isLengthNames(carNames)) {
         throw new IllegalArgumentException("5글자 이하만 가능");
      }
      return true;
   }

   private static boolean hasSpace(String carNames) {
      return carNames.matches("\"^[\\\\s]*$\"");
   }

   private static boolean isEnglish(String carNames) {
      return carNames.matches("^[a-zA-Z,]*$");
   }

   private static boolean isLengthNames(String input) {
      String[] carNames = input.split(",");
      for (String carName : carNames) {
         int length = carName.length();
         if (length < 1 || 5 < length) {
            return false;
         }
      }
      return true;
   }

   public static boolean validateRaceCount(String raceCount) {
      if (!isNumberType(raceCount)) {
         throw new IllegalArgumentException("정수 아닌 다른 값 잘못 입력");
      }

      if (!isPositiveNumber(Integer.parseInt(raceCount))) {
         throw new IllegalArgumentException("음수 입력 불가");
      }

      return true;
   }

   private static boolean isNumberType(String raceCount) {
      return raceCount.matches("^[\\d]*$");
   }

   public static boolean isPositiveNumber(int random) {
      if (random < 0) {
         return false;
      }
      return true;
   }
}

입출력과 검증은 1주차 피드백을 보고 역할을 분리했다. 클래스에 코드 라인 수가 길어졌다고 생각했다. 여기서 Validation은 변경하지 않은 부분과 변경 되는 부분을 나눠서 분리할 수 있다. Validation과 Validator로 역할을 나누어 수행하고, View는 Validation만 바라본다. 다음 주차에 해봐야겠다.

4. 자동차 경주

public class Application {
    public static void main(String[] args) {
        RacingGame racingGame = new RacingGame();
        racingGame.race();
        racingGame.result();
        racingGame.close();
    }
}

RacingGame의 역할은 전체를 컨드롤한다. 초기화와 입출력, 경주 시작과 결과까지 컨트롤하는 역할이다.


마무리

2주차 미션이 끝났다. 빨리 제출 마감하고, 다른 사람들 코드를 보고 싶고, 리뷰를 통해서 피드백을 받고 싶다. 배울건 많지만 시간은 한정적이다. 선택과 집중을 잘하자.