맨 처음 설계 과정에서 페어와 함께 클래스다이어그램을 그렸다.
모든 세부사항, 구체적인 연관관계를 그렸다.
하지만 UML 정식 형식과 많이 달랐고, 너무 복잡해 이해하기 힘들었다.
페어프로그래밍을 종료하고, [UML 실전에서는 이것만 쓴다 / 로버트 C.마틴] 책을 보았다.
그런데 오히려 로버트 마틴은 "세세하게 이것저것 다 적어놔봤자 알아보기만 힘들고,
구체적인것은 코드 구현시점에 정하면 된다. 모두가 잘 이해할 수 있도록 간소화 된 형식으로 UML을 작성해야 한다." 라고 했다.
점선, 집합, 합성도 구분하지 말고, 단순히 관계만 알아보기 쉽게 표현하라고 했다.
로버트 마틴의 말을 참고해 최대한 이해하기 쉽도록 다시 UML을 그렸다.
핵심 비즈니스 로직을 가지는 도메인 객체들을 domain 패키지에, UI와 관련된 객체들을 view 패키지에 구현했다.
MVC 패턴 기반으로 view 패키지의 객체가 domain 패키지 객체에 의존할 수 있지만,
domain 패키지의 객체는 view 패키지 객체에 의존하지 않도록 구현했다.
Validation을 최대한 도메인 객체 내부의 생성자에서 했다.
아무리 생각해도 도메인 내부에 어울리지 않는 Validation 작업들은 도메인 외부 InputView의 함수에서 했다.
리뷰어 휴가 README.md 파일의 기능구현 목록에 체크박스 적용을 제안해, 적용했다.
구현이 완료된 것들과 아직 완료되지 않은 것들이 직관적으로 구별되어 구현 진행과 관리가 더욱 편해졌다.
모든 이름들을 영어 문법을 지키면서 최대한 구체적으로 자세하게 지으려다 보니,
지나치게 길어지고 중복되는 부분들이 생겨, 오히려 의미전달력이 떨어지는 현상이 발생했다.
리뷰어 휴가 공유해 준 영어권에서 작성된 글을 참고하여
최대한 직관적으로 명확하게 알아볼 수 있도록 네이밍을 변경했다.
문법을 지킨 길고 자세한 이름들 보다 더욱 직관적으로 알아보기 쉬워졌고, 코드가 깨끗해졌다.
코치 제이슨이 로또번호 객체 캐싱을 제안해, 이를 적용했다.
반복되어 사용되는 객체(로또번호)들을 캐싱해 재사용하였다.
new 연산자를 통해 계속 생성할 필요 없이 캐싱되어있는 로또번호들을 가져다가 쓰니
메모리도 절약되고, 코드도 깔끔해졌다.
리뷰어 휴가 원시값에 대한 유효성 검사 시, 예외에 Custom Exception을 적용해볼 것을 제안해 적용다.
예외가 더욱 명확해져, 다른 예외들과 직관적인 구분이 가능해졌다.
리뷰어 휴의 제안으로 Integer.valueOf() API 를 참고하여 캐싱 방식을 개선했다.
LottoNumber.valueOf() 와 같이 API 사용 형식이 더욱 깔끔하고 명확해졌다. 의미 전달력도 매우 향상되었다.
수익률 소수점 계산에서 BigDecimal API 사용이 필요했다.
사용법을 제대로 숙지하지 않은 채 사용해, 소수점 셋 째 자리에서 반올림이 되지 않는 버그가 발생했다.
BigDecimal API 문서 와 구글링을 참고하고, 학습 테스트 를 통해 정확한 API 사용법을 익혀 버그를 고쳤다.
맨 처음 구현할 때, 구글링을 통해 찾은 잘못된 자료 를 참고해서 코드를 작성했다. 사용법이 애매할 때는 학습 테스트 로 제대로 익힌 뒤에 사용해야 겠다.
Enum을 사용해 관련된 상수들을 하나로 묶어 명확하고 편리하게 활용했다.
상수와 관련된 값들을 같이 묶어 활용했다.
딜러가 몇 장의 카드를 반환할지 결정하는 데에 전략패턴을 사용했다.
전략패턴을 적용하면 최소한의 코드 수정으로 전략을 다르게 가져갈 수 있고, 확장에도 자유로움을 알 수 있었다.
페어 멍토의 제안으로, 애플리케이션 전체에서 공유되는 카드 뭉치 객체에 싱글톤 패턴을 적용했다.
애플리케이션 전체에서 공유되는 객체에 대해서는 static 적용만 해왔는데, 싱글톤 패턴 적용에 대해 새롭게 배웠다.
Domain, Controller, View간의 의존성을 분리하기 위해 DTO를 사용했다.
필요한 데이터들을 알맞게 담아 넘겨줄 수 있었다.
체스 보드 세팅을 외부에서 주입하도록 했다.
테스트 코드에서 Custom한 기물 배치를 할 수 있도록 해, 테스트에 용이한 구조를 만들었다.
Enum 클래스로 상수들을 종류별로 묶어서 관리했다.
상수들을 종류별로 편리하게 관리하고 사용할 수 있었다.
한 Enum 클래스에서 다른 Enum 클래스의 상수들을 묶어 활용하는 것을 경험할 수 있었다.
컬렉션을 클래스로 포장해 일급 컬렉션으로 만들었다.
함수를 통해 컬렉션 활용을 다양하게 할 수 있었다.
캡슐화를 하여 컬렉션의 남용을 방지했다.
체스 보드 칸의 위치들과 기물들을 캐싱했다.
객체를 새로 생성하지 않고 재사용해, 메모리를 절약할 수 있었다.
static 메서드를 사용해 객체를 편리하게 가져올 수 있었다.
동일한 맥락의 테스트 코드들을 Nested 어노테이션을 사용해 묶었다.
Nested 어노테이션을 사용하지 않을 때는 연관된 테스트들끼리 구별하기가 어려웠는데, 이를 사용하니 훨씬 편해졌다.
@BeforeEach, @AfterEach의 적용 범위가 해당 어노테이션이 있는 클래스 범위에 한정되는 장점을 활용할 수 있었다.
처음에는 테스트 코드를 체스 보드의 위치값 문자열로만 테스트해, 작성하기도 어렵고 이해하기도 어려웠다.
테스트하기 쉬운 코드로 리팩토링 하고 나니 테스트 코드가 직관적이 되어 이해하기도, 작성하기도 쉬워졌다.
Stream 을 적극 활용해 컬렉션을 자유롭게 다루었다.
함수의 개수와 depth를 효과적으로 줄일 수 있었다.
Board
가 Piece
를 알고, Piece
가 Board
를 알고 있었다.MoveChecker
객체로 완전히 분리했다.Board
와 Piece
간의 의존성을 현저히 낮출 수 있었다.Pieces
도메인에서 했다.ScoreCalculator
객체로 분리했다.Spring, DI, OOP
Spring, MVC
Mysql Jdbc Driver
만 사용해서 작성했던 DAO 클래스들에 JdbcTemplate
를 적용했다.queryForObject()
로 DB에서 값을 조회할 때, 결괏값의 개수가 0개이면 EmptyResultDataAccessException
가 발생해, try ~ catch
문으로 직접 예외를 처리해야 하는 부분이 아쉬웠다.JDBC
OOP, Domain, Test, Spring
Test, DB
Spring, MVC, Exception
HTTP, REST, API
HTTP, Cookie
보안, Hash