성장일기

지난 2주 차 미션에서는 함수 분리와 함수 별로 테스트를 작성하는 것을 목표로 했는데요. 3주 차 미션에서는 2주 차에서 학습한 것에 2가지 목표를 추가했어요.

 

  1. 클래스(객체)를 분리하는 연습
  2. 도메인 로직에 대한 단위 테스트를 작성하는 연습

 

도메인 로직과 단위 테스트와 같은 용어들이 낯설 수 있지만, 작은 기능부터 테스트를 작성하는 연습을 시작해 보는 것입니다. 

 

과제를 제출할 때 이번 주차 목표를 중심으로 학습하면서 느낀 점을 소감문으로 작성해 주세요. 이때 학습한 '과정’을 잘 드러내 주세요.


 

이번 우아한 테크 코스 5기 3주 차 미션은 로또 게임 미션이다. 자세한 내용은 해당 우테코 깃허브를 통해 공개해놓고 있다.

 

 

나는 우테코 프리코스 전 과정과 최종 코딩 테스트까지 TDD로 개발을 도전하여 연습하고, 새로운 인사이트를 얻고 있다. 점점 미션의 난이도가 올라가서 주차마다 TDD에 대한 다양한 인사이트를 얻고, 우테코 커뮤니티를 통해 여러 사람들과 소통하며 성장하고 있음을 느꼈다. 이번 주차 미션을 완료하여 글을 쓰고 있지만, 벌써 다음 주차 미션이 기대가 된다.😊 (그리고 무엇보다 미션 주제가 너무 재밌다. 내가 완성한 프로그램을 가지고 놀기도 하며, 기능을 확장하고 싶은 생각이 들기도 한다.)

 

 

아래는 1,2주차를 TDD와 미션 요구사항을 진행하며 공부한 점과 느낀 점에 대한 회고록이다.

https://heesangstudynote.tistory.com/104

 

[우아한 테크 코스 5기] 프리코스 1주차 - 온보딩 회고

과제 진행 소감에는 미션을 진행하면서 느끼고 배운 점, 많은 시간을 투자한 부분 등도 포함하면 더 좋을 것 같습니다. 🙂 그리고 과제를 제출할 때 'git’과 '과정별 언어’를 학습하면서 느낀

heesangstudynote.tistory.com

https://heesangstudynote.tistory.com/105

 

[우아한 테크 코스 5기] 프리코스 2주차 - 숫자 야구 게임 회고

2주 차 미션에서는 1주 차에서 학습한 것에 더해 함수를 분리하고, 각 함수 별로 테스트를 작성하는 것에 익숙해지는 것을 목표로 하고 있어요. 이번에 테스트를 처음 접하시는 분들은 언어별 테

heesangstudynote.tistory.com

 

 

1. 2주 차 공통 피드백을 통한 부족했던 점

우테코 프리코스는 해당 주차의 미션을 완료하면, 해당 주차의 공통 피드백을 문서 형태로 받아볼 수 있다. 간단하게 정리해 보았다.

  1. README.md를 상세히 작성한다
  2. 기능 목록을 재검토한다
  3. 기능 목록을 업데이트한다
  4. 값을 하드 코딩하지 않는다
  5. 구현 순서도 코딩 컨벤션이다
  6. 변수 이름에 자료형은 사용하지 않는다
  7. 한 함수가 한 가지 기능만 담당하게 한다
  8. 함수가 한 가지 기능을 하는지 확인하는 기준을 세운다
  9. 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다
  10. 처음부터 큰 단위의 테스트를 만들지 않는다

 

이렇게 10가지의 피드백이 있었다. 물론 내가 잘한 부분도 있었지만, 피드백을 통해 이전 과제를 돌이켜봤을 때 부족했던 부분도 있어서 정리해보려고 한다.

 

일단 잘했던 부분은 2,3번의 기능 목록을 재검토하고 업데이트하는 부분은 TDD를 하다 보니 자연스럽게 하게 된 부분이라 잘 수행했다고 생각한다. 4번의 값을 하드 코딩하지 않는 부분도 잘 지켜왔고, 5번은 미션을 진행해오면서 궁금해서 구글링을 통해 해당 내용을 공부했고, 2주 차 회고록에도 정리했기 때문에 잘 지켜왔다고 생각한다.

 

10번의 처음부터 큰 단위 테스트를 만들지 않는 부분은 2주 차에서 TDD로 진행하며, 처음에 너무 큰 단위의 테스트로 접근해서 테스트 코드에 대한 빠른 피드백을 받지 못하고, 상당 시간 프로덕션 코드만 주구장창 개발을 진행하여 힘들었던 점이 있었다. 따라서 다시 기능 목록을 더 나누고, 수정하여 다시 접근했기 때문에 잘한 부분이라고 생각하고 2주 차 회고록에도 정리했었다.

 

대부분 잘해왔다고 생각했던 점들은 TDD로 진행하면서 자연스럽게 터득했던 부분이었다.

 

부족했던 부분은 6번의 변수 이름에 자료형을 사용하지 말라라고 되어있는데, 2주차 미션에 변수명에 자료형을 사용했던 부분을 발견했다.

나름 변수명에 최대한 의도를 들어내려고 네이밍에 자료형을 사용했었다. 하지만 변수명에 List, Set 등 컬렉션 네이밍은 항상 피해 왔지만, 자료형까지는 생각해보지 못했던 것 같다. 앞으로는 변수명에 컬렉션 네이밍뿐만 아니라 자료형도 사용하지 않도록 해야겠다.

 

7번의 한 함수가 한 가지 기능만 담당하게 하는 부분은 나는 나름 잘 해왔다고 생각하는데, 남들이 보기엔 어떨지 모르겠다. 따라서 8번 피드백을 토대로 함수가 한 가지 기능을 하는지 확인하는 기준을 세우려고 한다. 일단 라인수가 길면 해당 함수는 여러 가지 일을 하고 있을 확률이 높다고 생각한다.

 

따라서 함수가 10줄이 초과하지 않도록 의식적인 연습을 해보고, 스스로에게 해당 함수가 어떠한 기능을 하는지 하나의 기능으로 말할 수 있는지 설명해보는 과정을 거치려고 한다(네이밍에도 도움이 될 것으로 생각한다). 만약 해당 함수를 하나의 기능으로 말하지 못한다면, 함수는 여러 가지 일을 하고 있을 것이다.

 

9번의 경우는 이번 주차를 TDD로 진행하면서 테스트를 작성하는 이유에 대해 느낀 점이 있어서 이후 정리해 보려고 한다.

 

 

2. 3 주차 - TDD에 대한 인사이트

클래스 설계와 리팩토링 && 테스트 코드를 작성하는 이유

일단 이번 과제에서 커밋수만 무려 121개나 나왔다. 기능을 구현하는 커밋보다 추가로 테스트 코드를 작성하고, 리팩토링링하는 과정에서 많은 커밋이 발생하였다. 이번 과제에서 리팩토링을 엄청나게 진행했다. 단순히 메소드 이름을 바꾸고, 함수를 쪼개고, 위치를 바꾸는 일은 TDD 싸이클로 기능 단위의 커밋을 할 때 포함이 되지만, 그럼에도 커밋이 많이 발생했던 이유는 클래스 설계를 뒤엎는 리팩토링을 엄청나게 진행했기 때문이다. 

 

이번 주차 목표 중 하나는 클래스를 분리하는 연습이다. 이전 주차의 목표들인 기능을 작은 단위로 나눠서 구현하고, 메소드를 쪼개는 연습은 단기간의 노력으로 되지만, 클래스에 역할을 부여하여, 분리하고 프로그램의 전체적인 아키텍처를 구성하는 역량은 단기간 노력으로 힘들다는 점을 깨달았다. 특히 내가 설계한 게 맞는 설계인지 인지하는 것조차 어려웠다. 설계에 대한 경험이 많이 부족함을 느꼈다.

 

2주 차 미션에서 TDD가 클래스 설계에 대해 도움이 된다는 것을 느꼈지만, 이번 주차에서 서비스 규모가 조금 커지고, 복잡해 짐에 따라 TDD로 진행을 해도 설계가 쉽지 않았고, 계속해서 맘에 들지 않아서 많은 리팩터링을 진행했다.

 

개발 초기 단계에는 클래스들이 다음과 같은 책임을 가졌다.

  • Player : 게임 요구사항에 따라 로또를 만드는 책임, 게임을 플레이하는 책임, 리스트로 가지고 있는 로또들을 계산하는 책임
  • Answer : 플레이어의 로또와 당첨 로또를 비교(계산)하는 책임, 수익률과 통계를 계산하는 책임

 

즉 하나의 클래스에 너무 많은 책임을 가지고 있었다. 하지만 개발 초기에는 해당 도메인에 익숙지 않아서 해당 클래스가 많은 책임을 가지고 있는지 판단하는 것조차 어려웠다. 따라서 기능에 따라 클래스 설계를 뒤엎는 리팩터링을 끊임없이 진행했다.

 

하지만 클래스 설계를 뒤엎는 규모가 큰 리팩토링을 진행을 해도 TDD로 인해 미리 작성해 놓은 테스트 코드들 덕분에 부담없이, 과감하게, 안전하게 리팩토링을 마음 껏 진행했다. 테스트 코드를 한번 작성 해놓으면 10번이고, 20번이고 마음 껏, 불안할 때마다, 리팩토링을 할 때마다 테스트들을 돌려보며 심적 안정감이 들었다. 든든한 빽이 있다는 느낌을 받았다. 나는 TDD로 진행하면서 많은 장점을 느꼈지만, 이것이야말로 TDD의 가장 큰 장점이라고 생각한다.

 

수많은 리팩토링을 거친 후 (domain) 클래스들은 다음과 같은 책임을 가졌다.

  • Player : 게임을 플레이하는 책임, 가지고 있는 금액과 로또들을 Dto로 반환하는 책임
  • Answer : 플레이어의 로또와 당첨 로또를 비교(계산)하는 책임
  • Referee : 플레이 결과를 토대로 수익률과 통계를 계산하는 책임
  • Lottos(일급 컬렉션) : Lotto를 list로 가지고 관리하며, Lotto들 사이의 계산을 하는 책임
  • LottosCashier : 문제에서 제시한 금액 요구사항을 기준으로 Lottos를 생성하는 책임
  • LottoNumber(원시 값 포장) : Lotto 번호 한 개의 요구사항을 담당하는 책임
  • LottoResult(enum) : Lotto의 결과 값에 대한 정의와 계산을 하는 책임

 

처음에는 해당 도메인에 익숙지 않아서, 어눌했던 클래스 설계가 테스트 코드를 믿고 끊임없는 리팩토링을 진행하는 과정에서 나는 해당 도메인에 적응하고 익숙해지게 되었다. 따라서 그 과정 속에서 자연스럽게 클래스 설계가 아름다워지는? 과정을 경험했다.

 

 

단위 테스트의 비용

TDD로 개발을 진행하다 보니 테스트를 어디까지 할 것인가? 에 대한 궁금증이 생겼다. 이번 주 목표 중 하나가 도메인 로직에 대한 단위 테스트를 작성하는 연습이었다. 하지만 나는 단위 테스트의 기준을 어디까지 둬야 하나? 에 대한 의문이 생겼다. 크게 2가지를 생각했다.

 

  1. 의미 있는 기능을 단위로 봐야 한다.(하나의 메서드일 수도 있고, 하나의 기능에 여러 메소드일 수도 있다.)
  2. 하나의 메서드를 단위로 봐야 한다.

 

따라서 유닛 테스트의 정의를 찾아본 결과 "단위 테스트는 클래스 범주 내에서 작은 단위(함수 단위)의 기능에 대한 유효성을 검증하는 테스트입니다.라고 하며 위키백과에도 "모든 메서드에 대한 테스트 케이스를 작성하는 것"이라고 말하고 있다. 즉 단위 테스트란 모든 메서드의 테스트 코드를 작성하는 것을 의미했다.

 

TDD로 개발을 진행하면서 하나의 기능을 완벽하게 아주 작은 단위로 쪼개서 접근하면, 하나의 기능이 하나의 메서드를 담당하겠지만, 그렇지 않은 경우도 있다. 하나의 기능에 대한 테스트 코드를 작성하는 중에 여러 클래스와 여러 메서드들이 생겨날 수 있다.

 

즉 TDD는 기능을 단위로 쪼개기 때문에 해당 기능을 검증하는 상황의 테스트 코드는 만들어지지만, 메서드 하나하나의 단위 테스트에 대해서는 작성되지 않는다. 따라서 나는 이번 과제에서 TDD로 개발을 완료한 후 domain 로직의 메서드를 하나하나 확인하면서, 해당 메서드에 대한 단위 테스트가 작성되어 있지 않을 경우 테스트 코드를 작성하는 과정을 거쳤다.

 

처음에는 TDD로 개발을 무리 없이 마치고, 기능 별 테스트 코드로 리팩터링 과정도 문제없이 개발을 완료하였는데, 도메인 로직의 메서드를 일일이 확인하며, 작성되어있지 않은 단위 테스트를 작성하는 비용을 들어야 하나?라는 의문이 있었다.

 

하지만 곰곰이 생각하여 내린 결론은 지금은 괜찮겠지만, 기능 요구 사항이 어떻게 바뀔지 모르고, 서비스 규모가 커지는 상황에서 작성되어 있지 않은 단위 테스트로 인하여 버그가 발생할 수도 있겠다는 생각이 들어 작성하는 방향이 맞다는 결론을 내었다.

 

그리고 해당 내용과 관련하여 피드백을 얻고자 클린 코드 책에서 관련 내용을 찾아보았고, TDD에 관한 포비의 강좌에서도 책에서와 비슷한 포비의 조언이 있었다. 책이나 포비의 의견은 비슷했고 이를 정리하자면 다음과 같다.

https://www.youtube.com/watch?v=Sb3fMvIIsqQ 

 테스트 코드를 나중에 작성하기는 쉽지 않고 귀찮다. 리팩토링도 마찬가지다. 나중에 해야지 해야지 하고 기능 구현에만 초점을 두고 만족하고 넘어가고 안 한다. 하지만 TDD는 사이클 내에 테스트와 리팩터링 단계가 있다. 저번 주에 작성한 코드의 테스트 코드를 작성하고, 리팩토링 하는 것은 어렵다. 오늘 아침에 작성한 코드는 오후에 테스트 코드를 작성하고, 리팩토링 하는 것은 어렵지 않다. 하지만 5분 전에 작성한 코드는 지금 당장 리팩토링 하기 쉬우며 테스트 코드가 항상 존재한다. 이게 TDD의 장점이다.🎉

 

위 문장들은 책과 포비를 통해 내가 작성한 문장이고, 클린 코드 책에는 아래와 같은 내용이 적혀있다.

물론 나쁜 코드도 깨끗한 코드로 개선할 수 있다. 하지만 비용이 엄청나게 많이 든다. 코드가 썩어가며 모듈은 서로서로 얽히고설켜 뒤엉키고 숨겨진 의존성이 수도 없이 생긴다. 오래된 의존성을 찾아내 깨려면 상당한 시간과 인내심이 필요하다. 반면 처음부터 코드를 깨끗하게 유지하기란 상대적으로 쉽다. 아침에 엉망으로 만든 코드를 오후에 정리하기는 어렵지 않다. 더욱이 5분 전에 엉망으로 만든 코드는 지금 당장 정리하기 아주 쉽다. 그러므로 코드는 언제나 최대한 깔끔하고 단순하게 정리하자. 정대로 썩어가게 방치하면 안된다.

 

이번 주차를 통해 클래스 분리, 도메인 로직에 대한 단위 테스트를 연습하면서 많은 것을 느낄 수 있었고, TDD를 통한 인사이트를 정리하자면 다음과 같다.

 

  1. 개발 중에 기능에 대한 테스트 코드가 항상 존재하기 때문에 클래스 설계를 뒤엎는 리팩토링을 하더라도 두려움이 없다.(든든한 빽이 있는 느낌이다.)
  2. TDD는 사이클 중에 테스트 코드를 작성하는 과정과, 리팩토링 하는 과정을 강제하여, 기능 구현만의 만족을 통한 개발자의 귀찮음과 나태함을 잡아줄 수 있다. (TDD를 하기 전에는 그냥 기능 구현하고 테스트 코드 작성하면 되는 거 아닌가? 에 대한 해답을 이번 주차에 찾을 수 있었다. 5분 전에 작성한 코드는 지금 당장 리팩토링 하기가 가장 쉽다!)

 

 

3. 프리코스 커뮤니티(아고라) 참여

2주 차 미션을 진행하면서 상수를 public static finalprivate static final로 선언했을 때의 각각의 특징과 장단점이 무엇이 있을까? 또한 어떠한 상황에서 어떤 것을 쓰는 게 좋을까?라는 고민을 했었다. 아고라를 살펴보니 나와 같은 고민을 하고 있는 분이 질문을 올려주셔서 다른 사람들의 의견을 보고, 나의 의견도 공유할 수 있었다.

 

또한 아고라 글 중 TDD의 단점에 대한 주제로 토론이 올라왔다. 마침 나는 프리코스를 TDD로 도전하고 있기 때문에 경험을 통해 느낀 점을 공유했다.

 

프로그래밍에 관해 비슷한 주제로 의견을 주고받거나, 주제에 대해 토론을 하는 것이 너무 재미있고 그 과정에서 함께 성장할 수 있고, 새로운 인사이트도 얻을 수 있었다. 우테코에 합격해서 현장에서 공통된 주제로 토론을 진행하면 더 몰입감이 있고 재미있을 것 같다는 생각이 들었다.

 

 

4. 프리코스 커뮤니티(피어 리뷰) 참여

2주 차에 진행한 미션에 대해 나의 코드를 리뷰받을 수 있었고, 나 또한 다른 사람의 코드를 리뷰해주었다.

 

이번 주차에는 세 분에게 리뷰를 받을 수 있었고, 나 또한 세분에게 리뷰를 해주었다. 

더보기

 

이번 주차에는 클래스 설계에 대해 많은 고민이 있었고 어려움이 있었다. 이전 주차는 코드 리뷰를 클린 코드, 컨벤션 위주로 보았다면, 이번 주차에는 클래스 설계를 어떻게 했는지 중점적으로 코드를 살펴보고, 질문하고, 배우려고 한다.

 

우테코 과정에 합격하면 현직 개발자 선배를 통해 코드 리뷰를 받을 수 있는데, 합격해서 꼭 받고 싶다! 리뷰를 통해 얼마나 성장할지 벌써부터 기대가 된다.

 

 

5. 마무리

"결과에 후회하지 않도록 최선을 다하자"

 

어렸을 때 책 읽기를 그렇게 싫어했는데, 개발을 좋아하고 나서부터 책을 찾아서 읽게 되었다. 프리코스를 끝내고 읽고 싶은 책은 이펙티브 자바, 클린 코드, 객체지향의 사실과 오해, 오브젝트, 클린 아키텍쳐(이번 미션을 통해 아키텍쳐에 대해 공부 해보고 싶다는 생각이 듬)이다.

 

우테코 합격한다면? 본 코스 시작하기전에 오브젝트까지는 여유롭게 읽을 수 있지 않을까? 싶다.(항상 욕심은 많다ㅋㅋ..)

공유하기

facebook twitter kakaoTalk kakaostory naver band