저번 주에는 정말 정신이 없어서, 지금에서야 2주 차 회고를 하게 되었습니다.
사실 미리 작성해 두긴 했는데, 지금에서야 올려요 ㅎㅎ..
2주 차 미션은 자동차 경주 미션이였습니다. 되돌아보면, 뭔가 슥슥 빠르게 작성은 되었는데, 디테일한 부분을 많이 놓친 느낌입니다.
저는 스프링을 시작한지는 겨우 2달이 되었고, 자바를 시작한 지는 6개월 정도 된 것 같습니다. 본격적으로 시작한거요!
기본적으로 자바도 스프링처럼 MVC 패턴으로 작성하고자 했습니다.
이런식으로요!
그래서 MVC 패턴에 대해서도 다시 공부하고, 1주 차 코드를 보면서 어떤 부분을 고쳐야 할지 많이 고민했던 것 같습니다.
특히 신경썼던 점은, 의존성을 많이 끊어내려고 노력했습니다. 의존하게 된다면 MVC 패턴으로 구현하는 이유가 없다고 생각했습니다.
또한, 객체 지향 생활 체조 원칙을 접하면서, 해당 방식을 많이 적용하려고 노력하였는데, getter 를 지양하라는 부분에서 많이 지키지 못했습니다.
그리고 이번 미션에서 가장 고민했던 부분은, 랜덤 값을 어떻게 처리하느냐 였습니다. 아마 이게 핵심일 것 같아요.
왜냐하면 랜덤 값을 Cars 내부에서 생성해버리면, 테스트 자체를 못하게 됩니다.
그래서 저는 랜덤 값을 생성하는 객체를 따로 만들어서, 그 객체를 주입받는 형식으로 구현했습니다.
이렇게 한다면, 다른 Fake 객체를 만들어서 테스트하기 쉽다고 생각했습니다. 이게 맞는 방법인지는 잘 모르겠지만, 테스트는 잘 되었던 것 같아요.
아무튼, 이제부터 제가 이번 미션에서 배웠던 점을 자세히 정리해 보겠습니다.
- 명확한 책임
2주 차 목표는 각 도메인마다 명확하게 책임을 나누는 것이었습니다.
문자열 덧셈 계산기 코드 리뷰 중 가장 인상 깊었던 문구는 “클래스마다 각각의 역할을 명확히 하는 게 어떨까요?”였습니다. 해당 문구를 보고 제가 구현한 코드를 살펴보니, 큰 틀의 책임은 나눴지만 작은 부분까지 세세하게 나누지 못했음을 알 수 있었습니다. 따라서 자동차 경주 미션에서는 책임을 명확하게 나누고자 하였습니다.
책임을 나누는 게 무엇인지 정확히 알기 위해 객체 지향의 사실과 오해 4장을 다시 펼쳐 봤습니다. 책을 통해 객체의 책임은 객체가 무엇을 알고 있는가, 무엇을 할 수 있는 가로 결정된다는 것을 마음속에 새기며, 신중하게 기능을 구현하였습니다. 한 예로, 저는 List<Car>를 필드로 가지는 Cars 클래스를 자동차들의 상태와 행위를 관리하기 위해 만들었는데, 우승자를 결정하는 책임을 Cars에 두는 것이 맞는지 분리해야 하는지 고민이 되었습니다. 그래서 Cars 클래스가 어떤 책임을 가질 수 있는지 분석하였습니다. 분석 끝에, Cars는 Car의 상태(이름과 움직임)를 관리하기에 Cars가 우승자를 결정하는 책임을 가지는 것이 응집성 측면과 더불어 “객체가 무엇을 할 수 있는가”에 해당된다고 판단하여, 우승자 결정하는 책임을 Cars 클래스에 두었습니다.
이 예시뿐 아니라, 모든 기능을 이런 방식처럼 분석하여 책임을 나누었습니다. 구현하기 전 충분히 고민을 하고 책임을 나누니, 불필요한 리팩터링도 많이 줄어들었고, 객체 간의 의존성을 줄일 수 있었습니다.
- 객체 지향 생활 체조 원칙
1주 차 피드백을 보다 보니, “객체 지향 생활 체조 원칙 5: 줄여쓰지 않는다(축약 금지)” 라는 문구가 있었습니다. 해당 문구를 통해 해당 원칙이 무엇인지 궁금해졌고, 객체 지향 프로그래밍을 잘 하기 위한 원칙이라는 것을 알게 되었습니다. 찾아보는 과정에서 자바지기 박재성 님의 TDD 리팩토링 강의를 접하게 되었고, 강의에서 말씀하신 대로 이 원칙을 자동차 경주 미션에 적용해 보았습니다.
모든 원시 값과 문자열을 포장하라는 원칙이 가장 기억에 남습니다. 저는 이번 미션에서 자동차 이름과 경주 횟수를 포장 클래스를 만들어 적용해 보았는데, 이를 통해 해당 원칙에 대한 중요성을 직접적으로 느낄 수 있었습니다. 원래는 String 값으로 사용했어야 할 자동차 이름을 CarName 클래스로 포장하니, 해당 객체가 뜻하는 의미가 명확해지고 코드의 가독성도 향상되었다는 것을 느꼈습니다. 또한, 원시 값 포장을 통해 검증 로직을 따로 뺄 수 있었는데, 이를 통해 책임을 명확히 분리할 수 있었던 점이 좋았습니다. 문자열 그대로 사용했다면, 해당 문자열에 대한 검증 로직을 다른 클래스에 구현해야 했을 것인데, 포장 클래스를 통해 검증 로직에 대한 책임을 확실히 나눌 수 있었습니다.
이뿐만 아니라 모든 원칙이 책임을 나누고 객체를 분할하는데 많은 도움이 되었습니다. 원칙을 적용하는 것은 어려웠지만, 적용했을 때의 쾌감과 제 코드가 객체 지향적으로 발전한다는 점이 뿌듯했습니다.
- 발전을 막는 안일함
평소 테스트 코드를 작성할 때, Junit5를 사용하여 단위 테스트를 구현하였습니다. 주로 assertEquals() 메서드를 통해 검증을 하였고, @ParamiterizedTest 어노테이션을 활용하지 않고 매개변수마다 별도의 테스트 코드를 작성했습니다. 예외 테스트 역시 assertThrows() 메서드만을 활용했습니다.
하지만 이번 미션에서 AssertJ와 Junit5에 대한 여러 메서드를 익히고 Baeldung 사이트를 통해 정확한 사용법을 익히고, 테스트 코드 작성에 대해 너무 안일했다는 생각이 들었습니다. AssertJ를 사용하니, 값 검증뿐만 아니라 메시지도 함께 검증할 수 있었습니다. 또한, @ParamiterizedTest 어노테이션을 통해, 각 상황에 맞는 매개변수를 모두 테스트하여 테스트에 대한 신뢰도를 높일 수 있었습니다.
제 목표가 오류 없는 데이터를 전달하는 것임에도 불구하고 테스트 코드 부분에서 안주했다는 사실이 정말 창피했습니다. 제가 살아오며 저도 모르게 안주한 순간은 많겠지만, 이렇게 안일함이 발전을 막을 수 있다는 느낌을 크게 받은 적은 처음입니다.
- 기능 구현 목록 작성
기능 구현 목록을 어떻게 작성할지에 대해 많은 고민을 하였습니다. 밥 먹을 때도, 자기 전에도 어떤 방식으로 작성하면 좋을지 계속 생각했습니다.
제가 작성한 문자열 덧셈 계산기의 기능 구현 목록을 보니, 어떤 기능을 구현하고자 하였는지 한눈에 파악하기 어려웠습니다. 예를 들어 문자를 구분자로 나눠야 하는 기능이 있으면, 저는 “문자의 구분자를 추출한다”, “구분자로 문자열을 나눈다” 와 같이 메서드 별로 세분화하여 작성하였습니다. 물론 이러한 방식이 잘못된 것은 아니지만, 처음 보는 사람이 기능 목록을 봤을 때, 어떤 기능을 구현하고자 하였는지 파악하기 어려울 수 있겠다는 생각이 들었고, 실제로 제가 다시 봤을 때도 파악하기 어려웠습니다. 또한, 작성하다 보니 기능이 아닌 메서드 별 구현 목록을 쓴 게 아닌가 의문이 들기도 하였습니다.
그래서 완전히 다른 방식으로 작성하고자 하였습니다. 어떤 방식이 좋을지 구글링 하던 중 명확한 메시지를 전달하는 방법에 관한 글들을 많이 접하게 되었고, 간단하지만 핵심 내용이 빠지지 않게 작성하면 되겠다는 생각이 들었습니다. 그래서 이번 주 차에는, “경주하는 기능을 구현한다.” 와 같이 단순하면서도 핵심을 파악할 수 있는 기능 구현 목록을 작성하였습니다.
요구 사항에 따라 기능 목록 대로 커밋을 하려고 하니, 기능 구현 목록이 단순해서 제가 어떤 생각을 가지고 해당 코드를 구현하였는지 다른 사람이 모를 수 있다는 생각이 들었습니다. 그래서 기능 구현과 더불어, 고민하고 수정한 부분들을 모두 마크다운 파일로 정리하여 함께 커밋 하며, 살아있는 문서를 만들기 위해 노력하였습니다. 또한, 커밋 메시지 제목을 “경주하는 기능을 구현”처럼 작성하였기에, 커밋 메시지 바디 내용은 Car.java처럼 파일명을 명시하여 구체적으로 어떤 기능을 추가하였는지 작성하였습니다.
해당 방식으로 기능 구현 목록을 작성하니, 제출 후에 어떤 기능을 작성하였는지 확실히 파악할 수 있었습니다. 또한, 간단한 기능 목록에 따라 커밋을 하니, 커밋 로그도 훨씬 깔끔하였고 일관성 있었습니다.
이렇게 제가 배운 점들을 나열하니, 제가 무엇을 몰랐고, 어떤 점을 새로 알게 되었고, 원래 어떤 부분을 알게 되었는지, 확실하게 정리가 되는 것 같습니다. 소감문 쓸 때, 배운 점을 포함하여 작성하라고 하신게 많이 도움이 되는 것 같습니다.
이처럼, 이번 주차 회고록 부터는 최대한 메타인지를 위해 제가 무엇을 배웠는지를 중심으로 작성하며, 제가 알았던 부분과 몰랐던 부분을 나누어 작성할 생각입니다.
감사합니다.
https://github.com/woowacourse-precourse/java-racingcar-7/pull/80
'우아한테크코스[프리코스]' 카테고리의 다른 글
[3주 차] TDD (1) | 2024.11.05 |
---|---|
[2주 차] 상수에 static 선언을 하는 이유에 대하여 - 1주 차 코드리뷰 (3) | 2024.11.05 |
[2주 차] Junit5 Parameterized Test (0) | 2024.11.05 |
[2주 차] AssertJ를 활용한 예외검증 (1) | 2024.10.27 |
[2주 차] 원시값 포장 (0) | 2024.10.26 |