각종 후기/우아한테크코스

[우아한 테크코스 3기] LEVEL 1 회고 (15일차)

제이온 (J.ON) 2021. 2. 16.

안녕하세요? 제이온입니다.

 

오늘은 새로운 미션이 오픈되었고, 새로운 페어인 완태와 한 팀이 되었습니다. 방금까지 같이 개발하느라 힘들었지만, 꽤 즐거웠습니다 ㅎㅎ

 

 

데일리 미팅

오전 10시에 데일리 미팅이 진행되었습니다. 공지사항을 전달받은 이후, 오늘의 말하기 주제인 '최근에 Flex한 것은 무엇인가?'에 대해서 이야기를 나누었습니다. 다들 맥북이나 비싼 키보드를 이야기하셨지만.....저는 돈이 없어서 6900원짜리 손목 보호대를 보여드렸습니다. 빈부격차가 느껴지는 하루였습니다 ㅎㅎ

 

 

학습 로그 및 회고 작성 수업

오늘은 TDD 수업 전에 학습 로그와 회고 작성하는 수업을 들었습니다. 학습 로그는 미션을 수행한 후 PR을 보낼 때 자기가 무엇을 중점적으로 공부하였으며, 무슨 내용을 공부하는지 간략하게 메모를 남겨놓는 것이었습니다. 이렇게 함으로써 자기가 해당 기간동안 무슨 학습을 하였는지 빠르게 알 수 있다는 장점이 있습니다.

 

다음으로, 포비가 회고 작성 수업을 해 주셨는데 저는 원래도 매일 매일 회고를 하고 있어서 모두 와닿는 이야기였습니다. 회고를 통해 자기가 부족한 점을 느끼고 더 나은 방향으로 나아가는 길을 열어주기 때문이죠. 회고 수업을 들으면서 앞으로도 매일 매일 오늘 한 활동을 기록해야겠다고 다짐하였습니다.

 

 

TDD 수업

TDD의 아버지, T버지라고 불리는 포비가 TDD 수업을 해 주었습니다. TDD의 다양한 장점을 이야기하셨는데, 제가 인상깊게 들었던 부분은 '인간답게 살자'는 부분이었습니다. TDD를 하지 않으면, 매번 개발을 하고 나서 변화가 생기지 않기를 기도해야하는데, TDD로 인해 점진적 기능에 대해 자동 테스트 코드를 작성해 놓으면 유지보수에 용이하다는 강점이 있습니다. 그 외에 많은 장점을 약팔이 하셨고, 사실 이 부분은 제 스스로 느껴야한다고 말씀해 주셨습니다.

 

TDD는 아래와 같은 방법으로 구현합니다.

 


1. 입력, 출력 조건을 먼저 따진다.


2. 컴파일 에러를 해결한다. (클래스, 생성자, 메소드 등등)


3. 테스트 실패가 되었다면, 프로덕션 코드를 구현한다. (테스트 성공을 위해서면 악행을 저질러도 괜찮다.)


4. 테스트 성공이 되도록 초록불을 보면서 즐거움을 만끽한다.


5. 프로덕션 및 테스트 코드를 리팩토링한다.


6. TDD가 뭔가 막힌거면 설계가 부족한 것이다. 설계를 좀 더 자세히 생각한다.


 

인텔리제이 프로젝트를 만들면, 크게 java와 test 패키지로 나누는데, java 패키지를 프로덕션 코드라고 생각하면 됩니다. 처음에는 프로덕션 코드에 전혀 손을 대지 않고, 설계 후 test 코드를 작성합니다. 그렇다면, 프로덕션 코드는 없으므로 테스트 코드 내에서 클래스나 메소드가 없으므로 컴파일 에러가 생깁니다. 그리고 그제셔야 프로덕션 코드에 클래스나 메소드 등등을 추가해 주는 것이죠. 이렇게 점진적으로 기능을 구현해 나가야합니다.

 

이때, 테스트 성공을 위해서 비효율적인 코드더라도 초록불이 뜨도록 어떤 악행을 저질러도 괜찮습니다. 일단 초록불이 떠야, 개발자가 심신의 안정을 느끼므로 그 이후에 리팩토링을 차근 차근 하시면 됩니다. 하지만, TDD를 짜다가 아무리 봐도 그 다음 기능 테스트를 못하겠다면 설계가 부족한 것이므로 설계를 다시 깊게 생각해야합니다. 

 

 

대부분의 개발자는 한 가지의 일을 집중을 잘 못합니다. 가령, B라는 기능을 구현하다가 A라는 기능에 맘에 안 드는 것이 있으면 리팩토링해 버리려는 욕구를 참기가 힘듭니다. 그래서 to-do 리스트를 작성하는 습관을 들이시는 것이 좋습니다.

 

to-do 리스트는 구현할 기능을 기록해 놓고, 다 한 기능은 체크를 해 놓습니다. 이때, 위에서 말한대로 다른 기능을 리팩토링하고 싶다는 욕구가 생긴다면, 구현하지 말고 to-do 리스트에 그 내용을 기록하는 것입니다. 이렇게 함으로써, 한 가지의 일에 집중하여 능률을 상승시킬 수 있습니다.

 

 

저는 처음에 포비 수업을 듣고 나서도 TDD를 어떻게 해야하나 정말 막막하였는데, 실제로 이 수업 내용 하나 하나가 피가 되고 살이 되었습니다...

 

 

페어 프로그래밍

이번 미션의 주제는 '로또 구매'입니다. 로또는 1 이상 45 이하의 숫자를 맞춘 개수에 따라 상금을 타는 방식인데, 프로그램 흐름은 아래와 같습니다. 참고로 복권 한 개당 1000원입니다.

 

 

 

 

이 프로그램을 구현할 때 제약 사항은 아래와 같습니다.

 

 

 

 

저번 수업때 배웠던 원시값과 문자열 포장, 일급 콜렉션을 잘 사용해야겠습니다. 그리고 enum을 적용하라고 하였는데, 필요한 상황에서 잘 이용해야할 듯 합니다.

 

 

이전 미션에는 '나봄'과 페어가 되었는데, 이번에는 '완태'와 페어가 되었습니다. 완태 역시 원래부터 전공자는 아니었고 물리학과에서 컴공으로 넘어온 것이라고 합니다. 그럼에도 불구하고 실력이 뛰어나서 배울 것이 많다고 느꼈습니다.

 

처음에 아이스 브레이킹을 통해 간단히 친해지고 미션을 진행하였습니다. 프로그램이 다 완성된 것은 아니므로 제가 배운 점을 위주로 이야기하겠습니다.

 

먼저, README 작성입니다. 저는 기능 별로 요구 사항을 나누고, 객체의 이름을 정하여 그 객체가 해야하는 역할을 정하곤 하였습니다.

 

 

 

 

위와 같이 간략하게 객체의 이름을 적어 주었습니다. 하지만, 완태는 객체의 이름은 언제든지 달라질 수 있으므로 이렇게 명확하게 단어로 적지 말고 적당히 한글로 뭉뚱그리는 편이 좋다고 하였습니다. 이번 README에서는 이렇게 작성하였지만, 다음부터는 명확하게 객체의 이름을 설정하지 말아야겠다고 생각하였습니다. 실제로 개발하면서 여러 번 바뀌었기 때문이죠..

 

 

다음으로, Git GUI 사용 방법입니다.

 

 

 

 

이건 제가 정말 등잔 밑이 어둡다라는 말을 잘 느꼈는데요, 좌측 상단 쪽에 'Commit' 버튼이 있었습니다. 

 

 

 

 

그리고 좌측 하단에 Git 이라는 기능이 있는데, Commit 버튼과 Git 버튼을 적절히 이용하여 편하게 버전 관리를 할 수 있었습니다. 이전 미션에서는 터미널로 열심히 노가다했는데 삶의 질이 올라갔습니다 ㅎㅎ

 

 

또한, 로또 숫자 하나 하나를 추상화한다는 점이 인상깊었습니다. 저는 6개의 숫자가 들어있는 로또 자체에서 유효성 검사를 진행하였는데, '보너스 볼'의 유효성을 어떻게 체크할지 고민하던 중, 완태가 각각의 로또 숫자를 추상화하자고 제안하였습니다.

 

로또 숫자를 추상화하게 되면, 관련된 상수도 정리할 수 있으며 유효성 처리도 그 안에서 하므로 로또 객체의 부담을 줄이게 됩니다. 또한, 자동으로 '보너스 볼'의 유효성 검사도 할 수 있게 됩니다.

 

로또의 생성자는 기존의 List<Integer>였고, 외부에서 List<LottoNumber>가 되도록 인자를 넘기는 것은 불편하다고 생각이 들어서 그대로 냅두고 그 생성자만 변경을 하려고 하였습니다. 그런데, 완태는 로또 객체는 멤버 변수로 'List<LottoNumber> numbers'를 갖고 있는데 외부에서 List<LottoNumber>로 생성자를 만들 수 없는 것은 이상하다고 이야기해 주었습니다.

 

다시 고민해 보니 확실히 그렇다는 생각이 들었습니다. 분명 멤버 객체의 있는 속성을 다른 곳에서 생성을 못한다는 것은 바람직하지 않아보였죠. 그런데, 문제가 하나 있습니다.

 

 

    public Lotto(final List<Integer> numbers) {
        //
    }
    
    public Lotto(final List<LottoNumber> numbers) {
        //
    }

 

 

위 두 코드는 메소드 오버로딩을 허용하지 않습니다. 따라서, static 메소드로 생성자 비슷한 역할을 하는 방식을 적용해야한다는 것을 알게 되었습니다. 그런데, 저는 JCF를 공부하면서 보았던 HashSet 구조가 갑자기 생각이 났습니다. HashSet은 신박하게도 위 코드처럼 메소드 오버로딩이 허용되지 않는 생성자를 만들 때, dummy 데이터를 추가하였습니다. 이를 차용하여 이번 프로젝트에 적용한 것은 어떤지 완태에게 제안하였습니다.

 

완태도 JDK의 HashSet 구조를 보고 신기해 하였고, 리뷰어 님도 이에 대해 어떻게 생각하실지 궁금해서 한 번 적용해 보기로 하였습니다. 정리된 코드는 다음과 같습니다.

 

 

    public Lotto(final List<Integer> numbers) {
        this(numbers.stream().map(LottoNumber::new).collect(Collectors.toList()),true);
    }

    public Lotto(final List<LottoNumber> numbers, boolean dummy) {
        validateNumberCount(numbers);
        validateDistinct(numbers);
        this.numbers = numbers;
    }

 

 

물론.. 바람직한 코드라고는 생각이 들지 않지만 JDK의 개발자도 사용한 코드를 리뷰어 님께서 어떻게 받아들일지는 참 기대가 됩니다 ㅋㅋㅋㅋ

 

 

마지막으로, 로또 번호가 모두 같은 두 로또 객체는 같은 객체인지 판단하는 것이었습니다. 저는 처음에 맞다고 생각하였는데, 완태는 로또 번호가 모두 같은 로또가 2개 있으면 그것은 2개지, 1개가 아니라고 하였습니다. 생각해보니 만약에 로또 번호가 모두 같은 로또가 같은 객체라면 공동 1등의 개념은 없어지는 것과 다름 없습니다. 이후에, 저는 각 로또 객체에 식별 번호를 붙여주는 것은 어떨까 고민을 해 보았습니다.

 

 

정리

오늘은 수업도 빡빡하고 페어프로그래밍도 10시까지 해서 힘든 하루였습니다. 하지만, 배운 것이 너무 많아서 당일 날인 오늘도 모든 내용을 기록하지는 못했다고 생각합니다. 다행히 이번에도 저보다 잘하고 친절한 페어를 만나서, 협업하는 내내 즐거웠고 다양한 의견을 들을 수 있어서 굉장히 유익한 시간이었다고 생각합니다.

 

내일은 나머지 입, 출력 및 당첨 결과를 구하는 기능을 구현해야겠습니다.

추천 글