이번 주차 미션을 진행하면서 가장 많은 시간을 투자했던 부분입니다. 테스트를 생각해 보면서 구현을 진행해 보자고 생각했고 구현 기능 목록을 작성할 때도 이 기능은 어떻게 테스트할 것인지 생각하고 작성해 보았습니다. 또한 하나의 기능을 구현하고 하나의 단위 테스트를 실행하는 것으로 진행하였습니다.
처음에 테스트의 중요성을 잘 알지 못했지만, 이 방법으로 진행하면서 많은 장점을 알 수 있었습니다.
2주 차에서 일급 컬렉션을 사용했지만, getter의 사용으로 불변성이 지켜지지 않았다는 것을 알게 되었습니다. 3주 차 미션에서는 Lotto라는 일급 컬렉션을 주고이를 이용해서 구현하라는 요구사항이 있었습니다. 이전 주차에서 제가 정했던 getter의 사용 규칙에 따르면 로또의 번호는 getter를 사용해서 값을 외부로 나타내도 되었습니다. 요구조건이 로또의 번호를 나타내어 주는 것이기 때문입니다. 하지만 getter의 사용으로 값이 변환될 위험이 있었기에 불변성을 지키지 못한다고 생각했고 다른 방법을 학습하던 중 Collections의 unmodifiableList() 라는 메서드를 알게 되었고 Lotto에 적용해서 안전한 불변의 객체로 만들어 주었습니다.
unmodifiableList()를 사용해서 불변의 객체를 만들어 줄 때 그냥 컬렉션을 넣어서 사용하게 되면 원조의 컬렉션이 변경하게 되면 unmodifiableList()를 적용한 객체또한 변하게 된다는 사실을 알게 되었고 이를 해결하기 위해 new ArrayList<>()를 사용해서 방어적 복사를 적용합니다.
이번 주차 미션의 요구사항 중 Enum을 적용한다는 요구사항을 보고 Enum이란 무엇인지부터 공부하였습니다. 내가 지금까지 진행하면서 매직넘버나 매직리터럴을 처리해 주기 위해서 상수를 사용했었습니다. Enum이란 관련 있는 상수들을 묶어서 하나의 클래스처럼 사용할 수 있게 해주는 역할입니다. 어느 부분에 Enum을 적용할 수 있을까 생각하던 중 로또의 당첨 내역을 보았고 1등은 몇 개를 맞춰야 하는지 1등의 당첨 금액은 얼마 인지에 대한 정보가 1등이라는 문자에 연결되어 있다면 편리하게 사용할 수 있겠다고 생각했습니다. Enum을 사용해서 1등이라는 하나의 상수 명에 맞춘 개수, 당첨 금액 등과 같이 관련 있는 여러 값을 담아서 사용할 수 있다는 점이 직관성 개선에 많은 도움을 줬습니다.
Enum을 사용하기 위해 학습하던 중에 Enum의 values()의 함수에 대해서 알게 되었습니다. 내가 설계한 Enum들을 한 번에 가져오는 함수로 작성 순서대로 가져오게 됩니다. 이 함수를 이용해서 기능들을 구현하니 수월하게 작성이 되었습니다. 하지만 어떤 글에서는 ‘values() 로직은 유지보수 측면에서 아주 취약한 코드가 된다.’ 라고, 설명해 주고 있었습니다. 내부에서 선언된 필드 변수들의 순서만 바꿔도 무너지는 것이었습니다. 이러한 약점을 막기 위해서 values()를 사용하지만 내부 구조가 변경되어도 유지보수가 가능할 수 있게 해주는 함수를 설정해서 구현해 주었습니다.
리팩토링한 부분에 대해서 커밋을 진행하려 했지만, 실수로 커밋 메시지를 변경 내용과 다르게 입력하고 커 못해버린 것이다. 커밋을 되돌릴 수 있다는 이야기를 들은 기억이 있어서 확실하게 알아보지 않고 그냥 인텔리제이에 있는 여러 방법을 시도하다가 결국 리팩토링하기 전 상태의 코드 내용으로 되돌아가 버렸다. 많은 리팩토링 과정을 수행한 상태였기에 다시 코딩할 생각에 앞이 막막했습니다. 하지만 포기하지 않고 커밋에 관해서 공부해 보자는 생각했습니다. 학습하던 중 Git 로그를 확인해서 그 상태로 되돌아가거나(revert) 커밋을 취소(reset)하는 방법을 배우게 되었습니다. 결국 원래 상태로 돌아올 수 있었고 커밋 메시지 또한 다시 정상적으로 작성할 수 있게 되었습니다. 이 과정 덕분에 커밋을 관리하는 데에 두려움을 없앨 수 있었고 git 명령어 또한 배울 수 있었습니다.
입력받은 내용을 검증해서 객체로 변환해 주는 과정이 있다고 할 때 입력의 역할이 어디까지인가에 대해서 고민되었습니다. 클래스가 한가지 역할을 한다고 하면 입력을 담당하는 클래스는 입력만 받고 넘겨줘야 하는 것과 객체에 저장되기 위한 원시자료형까지 변환시켜 주는 것 중에서 어느 것이 옳은지 고민되었습니다. 만약 전자가 옳다면 원시자료형으로 변환을 시켜주는 클래스를 따로 구현해야 하는데 이러한 과정을 편리하게 만들어 주기 위해서 Stream이 생겨났다고 생각했기에 Stream을 통한 형 변환을 또다시 함수로 묶어줄 필요가 없다고 생각했습니다. 따라서 입력의 역할은 사용자로부터 입력을 받고 입력값에, 의미에 맞는 원시 자료형까지의 변환까지 하고 나만의 규칙을 정하게 되었다.
- 클래스(객체)를 분리하는 연습
- 도메인 로직에 대한 단위 테스트를 작성하는 연습
실수로 한번에 commit을 올려버렸던 것을 취소하기위해서 잘 알지도 못하는 기능들을 눌러보다가 commit로 drop되고 소스코드도 이전상태로 돌아가버렸다
이를 해결하기위해서 git 명령어를 학습해보았다.
이제 커밋 실수를 해도 걱정없이 회복시킬 수 있다.
try-catch를 사용해서 입력을 다시 받는것
불변성을 책인지기위한 unmodifiableList() 사용
로또 번호를 정렬했을때에 오류가 발생했던 이유는 입력을 받아와서 객체로 저장하는 과정에서 toList() 메서드를 사용해서 해결하였는데 이때 toList()는 불변성을 적용시키기때문에 입력해서 가져온 정보를 정렬해서 위치 변환을 시도했을때에 오류가 발생한 것이다.
클래스 내부 선언 순서 컨벤션
Static Variable : public -> protected -> private
Member Variable : public -> protected -> private
Constructor
Static Method
Other Method : 기능 및 역할별로 분류하여 기능을 구현하는 그룹별로 작성
Standard Method : toString, equals, hashcode 와 같은 메소드
Getter / Setter : 클래스의 가장 하단 부분에 위치
private 메서드가 테스트가 필요할 정도로 복잡하고 중요하다면, 해당 메서드는 private이어서는 안되었던 것이다
중복을 처리할때에 distinct() 함수를 사용해서 길이를 비교했는데, Set의 add() 함수를 사용해서 중복여부를 판단한다면 모든 내용을 확인하지 않고 중간지점에서도 종료될 수 있기게 효율적
unmodifiableList 사용해서 불변성 적용
긍정의 조건문을 사용하기위한 메서드 생성
toList() 메서드
처음에는 간단하게 리스트로 변환시켜주는 간단한 메서드라고 생각했지만 이후 기능을 구현하면서 toList를 남발하게되었는데 이 때 오류가 발생했었다.
이전에 unmodifiableList를 사용해서 불변성을 지켜주었는데 그 방법외에도 toList()를 사용해서도 지켜줄 수 있다는 것을 보았다. 그냥 List로만 변환시켜주는 것인데 불변성까지 지켜주는 것이었다. 그래서 입력값을 받아서 자른뒤 toList 메서드를 사용해서 리스트로 변환한뒤에 로또 번호로 만들어주는 과정에서 정렬을 실행하게되었고 이때 불변성을 어기게 되므로 예외가 발생한 것이다.
문자열 내에서 "%n" 을 사용하면 앞에 부분의 format이고 뒤에부분을 대입한다는 것을 알 수 있다.
try-catch를 사용해서 입력을 다시 받는것
git 사용법
실수로 한번에 commit을 올려버렸던 것을 취소하기위해서 잘 알지도 못하는 기능들을 눌러보다가 commit로 drop되고 소스코드도 이전상태로 돌아가버렸다
이를 해결하기위해서 git 명령어를 학습해보았다.