[우테코6기] 프리코스 1주차 회고

JWbase·2023년 11월 1일
1

우테코

목록 보기
1/1
post-thumbnail

회고는 처음이라 어떤 방식으로 써야할 지 감이 없어 내가 하고싶은 말을 그냥 주저리주저리 써 볼생각이다.

지원하기 까지..

작년에 처음 개발에 입문했을 때 우테코에 대해 알게 되었지만 지원하지는 않았다. 그 이유로는 코딩을 한 번도 해본 적 없어서 두려운 마음도 있었고, 우테코는 선발과정부터 실제 과정이 진행되는 기간이 길었기 때문이다.

당시에 나는 여유가 없었고 최대한 빨리 학습해서 취업을 해야겠다는 생각뿐이었다.
1년의 시간 동안 자바를 학습하고 스프링에 대해서 학습하고 실제 웹 애플리케이션을 만들어보는 프로젝트를 진행하는 과정에서 즐거움도 많았지만 마음 한편으로는 어딘가 계속해서 갈증이 있었다. 우연히 우테코 6기가 열린다는 글을 보게 되었고 내가 지원해도 되는가에 대해서 정말 많이 고민을 했던 것 같다.

두려움 때문에 망설였던 적이 많았는데 이번에는 뭐에 씐 듯 안 해보면 정말 후회할 것 같다는 생각에 지원을 결정하게 되었다. 우테코에서 강조하는 것처럼 몰입을 경험해보고 싶어서였을까? 결과적으로는 프리코스를 진행하면서 즐거워 하는 스스로를 보고 지원하길 잘했다는 생각을 여러 번 했다.

커뮤니티

내가 관심있는 부분에 대해서 여러 사람들과 이야기 나누는 것을 굉장히 좋아한다. 하지만 온라인 커뮤니티에 대해서는 조금 회의적이었다.

그 이유는 온라인으로 진행되는 스터디를 해보신 분들은 아시겠지만 개인의 사정으로 오래 지속하기가 쉽지 않은 점 때문이다.

한 주동안 경험한 우테코 디스코드는 굉장히 즐거웠다. 열정 넘치는 사람들이 정말 많았고 생각해보지도 않았던 그리고 나와 같은 고민이 공유되면 즐거워서 과제 진행 중 디스코드에서 여러 글을 읽는 데 시간을 더 많이 쓴적도 있었다.

우테코를 진행하는 동안 더 자주 들여다 볼 것같다. 합격하지 않아도 커뮤니티에는 계속 참여할 수 있었으면 좋겠다..

1주차 과제

과제를 진행할 때 요구사항은 프로그래밍만이 아니다. 세가지로 분류 되어있는데

  1. 프로그래밍 요구 사항
  2. 과제 진행 요구 사항
  3. 기능 요구 사항

세 가지 요구사항을 전부 만족하면서 과제를 진행해야 한다. 별거 아니라고 생각할 수 있지만 커뮤니티에 요구사항에 대한 글들이 많이 올라오는 것을 보면서 음.. 생각보다 요구 사항 지키는 것이 힘든 일이구나 싶어 요구사항을 꼼꼼하게 몇번이고 다시 읽어보게 되었다.

프로그래밍 요구사항

개발 환경을 세팅하는 부분에 대해서는 사실 크게 고민 했던 적은 없다. 지금 껏 했던 방식은 그냥 https://spring.io/ 를 통해 셋팅하는게 다였으니까..?

처음에 Java 환경부터 다시 설정하기 시작했다. 나는 Java11을 사용하고 있었는데 Java17로 변경하기 위해 jdk를 고르는 것 부터 고민이었다. 쇼핑할 때 가장 좋은 제품을 사고 싶은 마음이랄까.. 이때 jdk의 종류를 정말 많이 찾아 봤던 것 같다.

결과적으로 나는 Liberica Open JDK를 사용하게 되었는데 결정적인 이유는 https://dejavuhyo.github.io/posts/which-version-of-jdk-should-i-use/ 해당 블로그글에서 Spring Boot 프레임워크는 빌드팩의 런타임으로 Liberica JDK를 선택했다는 글을 보고나서 결정했다. 지금 와서는 과제에서는 큰 고민하지 않고 JDK를 선택해도 괜찮을 것 같다. 그래도 고민하는 과정에서 jdk에 대해 학습했던 좋은 경험이었다.

Java 코드 컨벤션 같은 경우에는 나는 스스로를 믿지 못해서 개발할 때 항상 IDE가 제공해주는 자동정렬 기능 단축키부터 찾고 코딩 중간마다 자동정렬을 하는 습관을 가지고 있어서 크게 생각해보지 않았던 부분이다.

인텔리제이에 GOOGLE에서 사용하는 컨벤션을 적용시켜 자동정렬을 최대한 이용하고 있지만 아직까지 줄바꿈은 자동으로 정렬해주지 않기 때문에 이 부분을 고민했던 것 같다.
나는 메소드와 메소드 사이에 개행말고는 기본적으로 최대한 개행을 두지는 않는 방식으로 진행하고 있다. 이 부분은 계속 개발을 하다보면 다른 결정을 할 날이 올지도..

과제 진행 요구 사항

과제 진행 요구 사항도 공부할 점이 많이 있었다. git을 사용한지는 1년이 넘었지만.. git을 사용하다 에러를 만난 경험이 많아 아직까지 나에게는 두려운 존재다.

PR을 해본 경험이 없었는데 이번 과제를 진행하면서 첫 PR을 보낼 때는 코드리뷰를 받아 볼 생각에 두근두근하면서 제출했던 것 같다.
코드리뷰를 통해 2주 차엔 좀 더 나은 코드를 작성할 수 있을 것 같다는 막연한 믿음이(?) 생겼다.

정말정말 어려운 부분이 기능 구현 README 작성이었는데 README를 작성하려고 기능 요구 사항을 몇번이나 읽었는지 모르겠다. 여기서 느낀점은 기능 구현을 작성하다 보면 어떻게 해야 내가 가장 편하게 코드를 작성할 수 있을지 고민하면서 작성하게 된다는 점이다.

결국 프로그래밍에 들어가는 시간을 줄일 수 있고 또 내가 구현해야하는 프로그램을 얼마나 잘 이해할 수 있는지에 대한 척도도 된다고 생각한다. 그래서 그런가 항상 뭔가를 빼먹었다는 찜찜한 느낌을 지울수가 없다..

# 🚀 숫자 야구 기능 목록

## 게임 시작 기능
- [X] 게임 시작 문구 출력

## 컴퓨터 수 생성 기능
- [X] 1부터 9까지 서로 다른 임의의 3개의 수 생성

## 플레이어 수 입력 기능
- [X] 서로 다른 3개의 수 입력
- [X] 입력 받은 수를 예외 처리
    - [X] [예외] 중복된 숫자를 입력한 경우
    - [X] [예외] 숫자가 아닌 문자를 입력한 경우
    - [X] [예외] 3개의 수가 아닌 경우
    - [X] [예외] 1 ~ 9 사이의 수가 아닌 경우

## 컴퓨터와 플레이어의 숫자 비교 기능
- [X] 컴퓨터와 플레이어의 숫자 위치 비교
- [X] 자릿수와 숫자가 모두 일치하면 스트라이크 +1
- [X] 숫자는 맞지만 자릿수가 다르면 TotalCount +1
- [X] TotalCount - 스트라이크 = 볼

## 플레이 결과 출력 기능
- [X] 스트라이크가 3인경우 성공 문구 출력 및 게임 종료
- [X] 스트라이크, 볼 둘다 0인 경우 낫싱
- [X] 스트라이크, 볼 갯수 출력

## 재시작 및 종료 기능
- [X] 재시작 문구 출력
- [X] 1을 누르면 재시작 기능
- [X] 2를 누르면 종료 기능
    - [X] 1, 2가 아닌 잘못된 값을 입력할 경우

최종적으로 작성하게 되게된 기능 목록인데 사실 이것도 코드를 작성하면서 여러번 수정을 거쳤다. README작성 너무 어렵다!
2주차에는 좀 더 나은 REAMDE를 작성하려고 노력하겠지만 마음대로 될지..
그래도 기능 목록을 스스로 고민해서 작성하고 개발을 해보는 건 처음이라 이것만으로도 성장하는 느낌을 받을 수 있었다.

기능 요구 사항

하루동안은 한 줄의 코드도 작성하지 못했다. 그 이유는 처음부터 완벽한 구현을 하고 싶어서 고민하는데 너무 많은 시간을 써버렸기 때문이다.
나처럼 고민 만하다가 시간을 쓰지 말고 프로그래밍은 일단 요구 사항을 만족하면서 돌아가게 만드는게 우선이라는 점을 기억했으면 한다!

전략패턴


public interface NumberSelectionStrategy {
    List<Integer> createNumbers();
}

컴퓨터 숫자와 플레이어가 입력한 숫자는 비교를 위해 생성 결과가 같지만 번호를 생성하는 방식만 다르다고 판단했다.
그래서 번호를 다른 전략으로 생성하는 전략 패턴을 사용해서 구현했다.


public class RandomNumberSelectionStrategy implements NumberSelectionStrategy {
    private static final int DIGIT_COUNT = 3;
    private static final int MIN_NUMBER = 1;
    private static final int MAX_NUMBER = 9;

    @Override
    public List<Integer> createNumbers() {
        List<Integer> randomUniqueNumbers = new ArrayList<>();
        while (randomUniqueNumbers.size() < DIGIT_COUNT) {
            int randomNumber = Randoms.pickNumberInRange(MIN_NUMBER, MAX_NUMBER);
            addUniqueNumber(randomUniqueNumbers, randomNumber);
        }
        return Collections.unmodifiableList(randomUniqueNumbers);
    }

public class UserInputNumberSelectionStrategy implements NumberSelectionStrategy {
	@Override
    public List<Integer> createNumbers() {
        return convertStringToIntegerList(guessNumber);
    }
 }

다시 보니까 클래스명이 명확하지 않아보여서 조금 더 나은 클래스명을 썼으면 어땠을까라는 생각이 든다.
예를 들어 NumberGenerator같은 이름이 좀 더 명확헤 보일 것 같다.

HintView


public class HintView {
    private static final String STRIKE = "스트라이크";
    private static final String BALL = "볼";
    private static final String NOTHING = "낫싱";
    private final List<String> message = new ArrayList<>();

    public HintView(GameResult result) {
        addBallMessage(result.ball());
        addStrikeMessage(result.strike());
        addNothingMessage(result.ball(), result.strike());
    }

    public void addBallMessage(int ball) {
        if (ball != 0) {
            message.add(ball + BALL);
        }
    }

    public void addStrikeMessage(int strike) {
        if (strike != 0) {
            message.add(strike + STRIKE);
        }
    }

    public void addNothingMessage(int ball, int strike) {
        if (ball == 0 && strike == 0) {
            message.add(NOTHING);
        }
    }

    public void printHint() {
        System.out.println(String.join(" ", message));
    }
}

플레이어가 숫자를 입력하면 힌트를 출력해줘야 하는 부분이 있다.

1볼 1스트라이크

이번 과제에서는 콘솔이 VIEW의 역할을 하기 때문에 기본적으로 입력할 때 나오는 InputView와 출력 시 나오는 OutputView로 나눠서 구현을 했다.
그런데 나는 힌트를 출력해주는 부분을 따로 객체로 빼서 각 조건에 맞는 힌트를 List에 담아 힌트를 출력하는 방식으로 구현했다.
이 부분을 회고에 작성하게 된 이유는 지금와서 생각해보면 Hint라는 Model을 만들고 거기서 힌트에 대한 값을 관리하고 printHint()라는 메서드는 컨트롤러를 통해 힌트를 전달해 OutputView에서 출력하는 역할을 맡기는게 좀 더 나은 방식이라는 생각이 들어서다.

일급 컬렉션

아마 이번 과제를 통해서 가장 많은 사람들이 찾아 본 토픽이 일급 컬렉션이 아닐까 생각한다.
먼저 일급 컬렉션이 무엇인지 대해 살펴보면 https://jojoldu.tistory.com/412 평소에도 자주보는 향로님의 블로그 글을 참고해보면 좋을 것 같다.


public class Numbers {
    private final List<Integer> values;

    private Numbers(List<Integer> values) {
        this.values = values;
    }

    public static Numbers createNumbersFrom(NumberSelectionStrategy numberSelectionStrategy) {
        return new Numbers(numberSelectionStrategy.createNumbers());
    }

    public GameResult calculateResult(Numbers other) {
        int strikeCount = countStrikes(other);
        int ballCount = countTotalMatches(other) - strikeCount;
        return new GameResult(ballCount, strikeCount);
    }

    private int countStrikes(Numbers other) {
        return (int) IntStream.range(0, values.size())
                .filter(i -> getValueAt(i).equals(other.getValueAt(i)))
                .count();
    }

    private int countTotalMatches(Numbers other) {
        return (int) this.values.stream()
                .filter(other::contains)
                .count();
    }

    public Integer getValueAt(int index) {
        return values.get(index);
    }

    public boolean contains(Integer value) {
        return values.contains(value);
    }
}

각 자릿수를 리스트로 갖고 있는 Numbers를 일급 컬렉션으로 사용했다.
코드에서 보면 알 수 있듯이 일급 컬렉션을 사용하니 스트라이크 갯수와 같은 비즈니스 로직들을 해당 클래스에서 함께 관리할 수 있다.
또한 필요한 로직만 메소드를 통해 전달 받을 수 있어서 캡슐화를 지킬 수 있다는 장점이 있는 것 같다.
이번 미션을 통해 일급 컬렉션에 대해 처음 알게 되었는데 앞으로 진행 될 미션에서 일급 컬렉션을 적극적으로 사용해보는 연습을 많이 해 볼 생각이다.

매직넘버

이번 미션을 진행하면서 처음 알게 된 용어들이 굉장히 많이 있는데 피드백도 많이 받은 부분이다.
매직넘버란 숫자의 의미가 명확하지 않고 파악하기 힘든 수치 값들을 말한다.

쉽게 말해서 코드를 봤는데 저게 뭘 의미하는 숫자야? 라는 생각이 들면 매직넘버가 쓰여진거라고 생각하면 될 것 같다.


    public List<Integer> createNumbers() {
        List<Integer> randomUniqueNumbers = new ArrayList<>();
        while (randomUniqueNumbers.size() < 3) {
            int randomNumber = Randoms.pickNumberInRange(1, 9);
            addUniqueNumber(randomUniqueNumbers, randomNumber);
        }
        return Collections.unmodifiableList(randomUniqueNumbers);
    }

해당 코드를 보면 저기서 의미하는 3, 1, 9같은 숫자는 처음 코드를 읽으면 무슨 의미로 쓰였는지 단번에 이해하기 힘들 수 있다. 해당 코드를


    public List<Integer> createNumbers() {
        List<Integer> randomUniqueNumbers = new ArrayList<>();
        while (randomUniqueNumbers.size() < DIGIT_COUNT) {
            int randomNumber = Randoms.pickNumberInRange(MIN_NUMBER, MAX_NUMBER);
            addUniqueNumber(randomUniqueNumbers, randomNumber);
        }
        return Collections.unmodifiableList(randomUniqueNumbers);
    }

이렇게 상수로 치환해주면 명확하게 파악할 수 있어서 매직넘버를 이해하기 쉬운 상수로 치환해주는 방법을 적극적으로 사용할 것 같다.

개선하고 싶은 부분

다음 부분들을 2주차 미션을 진행하면서 개선해보려고 한다.

  • 클래스, 메서드 및 변수명 고민에 좀 더 많은 시간을 사용하기
  • 기능 단위로 커밋하기
    - 1주 차때는 커밋메시지 컨벤션을 지키려는 점에 초점을 맞춰서 커밋시기에 대해 신경을 쓰지 못했다.
  • 처음부터 완벽한 코드를 짜려고 하지않고 리팩토링을 통해 발전 시키기
  • 단위 테스트 적용해보기

코드리뷰

회고를 작성하면서 제출했던 미션을 다시 돌아봤는데 부족한 실력을 다시 한번 보게되는 느낌이라 외면하고 싶었다.
특히 코드리뷰를 할지 정말 많이 고민했는데 그 이유는 내 실력이 부족한데 내가 남의 코드를 리뷰할 수 있을까? 그리고 정확하지 않은 지식으로 괜한 리뷰를 통해 부정적인 영향을 끼치지 않을까하는 걱정에서 였다.
디스코드에서 수많은 코드리뷰 요청글들을 보고 나서 다른 사람들이 남긴 리뷰들을 보면서 용기내서 첫 리뷰를 남겨 보았고 내 코드에 대한 리뷰도 받아 볼 수 있었다.

이 PR은 내가 처음 받은 코드리뷰인데 첫 리뷰가 칭찬이라 기분이 좋아 좀 더 적극적으로 코드리뷰에 응 했던 것 같다. 사실 내 코드를 리뷰 받는 것보다 내가 코드 리뷰를 했을 때 긍정적인 피드백이 돌아오면 기분이 좋아 더 열심히 했던 것 같다.

profile
기억 저장소!!

1개의 댓글

comment-user-thumbnail
2023년 11월 23일

정우님 오랜만에 들렸는데 좋은 소식이 있으셨네요! 축하합니다~ ㅎㅎ

답글 달기