프리코스 3주차를 진행하며

박세건·2023년 11월 9일
0

회고록

[테스트를 작성해야 하는 이유]

이번 주차 미션을 진행하면서 가장 많은 시간을 투자했던 부분입니다. 테스트를 생각해 보면서 구현을 진행해 보자고 생각했고 구현 기능 목록을 작성할 때도 이 기능은 어떻게 테스트할 것인지 생각하고 작성해 보았습니다. 또한 하나의 기능을 구현하고 하나의 단위 테스트를 실행하는 것으로 진행하였습니다.
처음에 테스트의 중요성을 잘 알지 못했지만, 이 방법으로 진행하면서 많은 장점을 알 수 있었습니다.

  • 구현한 내용에 대한 결함을 초기에 발견할 수 있다.
    로또 당첨 내용을 출력해 주는 기능을 구현한 뒤 바로 테스트 코드를 작성하였고 테스트하면서 2등의 내용이 빠져있다는 사실을 바로 알고 수정할 수 있었습니다.
  • 리팩토링할 때 안정성을 확보할 수 있다.
    내가 올바르게 리팩토링을 진행했는지 빼먹은 부분 없는지 지금까지 만들어 놓았던 단위 테스트를 통해서 확인받을 수 있었습니다.
    물론 테스트 코드를 작성하면서 구현을 진행하기에 시간을 더 사용해야 하지만 기존까지 진행했던(구현 후 검증) 방법의 단점을 보완시켜 줄 수 있다고 생각했기에 앞으로도 테스트를 기준으로 생각하고 구현하는 방식을 사용할 것입니다.

[불변성]

2주 차에서 일급 컬렉션을 사용했지만, getter의 사용으로 불변성이 지켜지지 않았다는 것을 알게 되었습니다. 3주 차 미션에서는 Lotto라는 일급 컬렉션을 주고이를 이용해서 구현하라는 요구사항이 있었습니다. 이전 주차에서 제가 정했던 getter의 사용 규칙에 따르면 로또의 번호는 getter를 사용해서 값을 외부로 나타내도 되었습니다. 요구조건이 로또의 번호를 나타내어 주는 것이기 때문입니다. 하지만 getter의 사용으로 값이 변환될 위험이 있었기에 불변성을 지키지 못한다고 생각했고 다른 방법을 학습하던 중 Collections의 unmodifiableList() 라는 메서드를 알게 되었고 Lotto에 적용해서 안전한 불변의 객체로 만들어 주었습니다.
unmodifiableList()를 사용해서 불변의 객체를 만들어 줄 때 그냥 컬렉션을 넣어서 사용하게 되면 원조의 컬렉션이 변경하게 되면 unmodifiableList()를 적용한 객체또한 변하게 된다는 사실을 알게 되었고 이를 해결하기 위해 new ArrayList<>()를 사용해서 방어적 복사를 적용합니다.

[Enum]

이번 주차 미션의 요구사항 중 Enum을 적용한다는 요구사항을 보고 Enum이란 무엇인지부터 공부하였습니다. 내가 지금까지 진행하면서 매직넘버나 매직리터럴을 처리해 주기 위해서 상수를 사용했었습니다. Enum이란 관련 있는 상수들을 묶어서 하나의 클래스처럼 사용할 수 있게 해주는 역할입니다. 어느 부분에 Enum을 적용할 수 있을까 생각하던 중 로또의 당첨 내역을 보았고 1등은 몇 개를 맞춰야 하는지 1등의 당첨 금액은 얼마 인지에 대한 정보가 1등이라는 문자에 연결되어 있다면 편리하게 사용할 수 있겠다고 생각했습니다. Enum을 사용해서 1등이라는 하나의 상수 명에 맞춘 개수, 당첨 금액 등과 같이 관련 있는 여러 값을 담아서 사용할 수 있다는 점이 직관성 개선에 많은 도움을 줬습니다.

[Enum의 values()]

Enum을 사용하기 위해 학습하던 중에 Enum의 values()의 함수에 대해서 알게 되었습니다. 내가 설계한 Enum들을 한 번에 가져오는 함수로 작성 순서대로 가져오게 됩니다. 이 함수를 이용해서 기능들을 구현하니 수월하게 작성이 되었습니다. 하지만 어떤 글에서는 ‘values() 로직은 유지보수 측면에서 아주 취약한 코드가 된다.’ 라고, 설명해 주고 있었습니다. 내부에서 선언된 필드 변수들의 순서만 바꿔도 무너지는 것이었습니다. 이러한 약점을 막기 위해서 values()를 사용하지만 내부 구조가 변경되어도 유지보수가 가능할 수 있게 해주는 함수를 설정해서 구현해 주었습니다.

[무너지다]

리팩토링한 부분에 대해서 커밋을 진행하려 했지만, 실수로 커밋 메시지를 변경 내용과 다르게 입력하고 커 못해버린 것이다. 커밋을 되돌릴 수 있다는 이야기를 들은 기억이 있어서 확실하게 알아보지 않고 그냥 인텔리제이에 있는 여러 방법을 시도하다가 결국 리팩토링하기 전 상태의 코드 내용으로 되돌아가 버렸다. 많은 리팩토링 과정을 수행한 상태였기에 다시 코딩할 생각에 앞이 막막했습니다. 하지만 포기하지 않고 커밋에 관해서 공부해 보자는 생각했습니다. 학습하던 중 Git 로그를 확인해서 그 상태로 되돌아가거나(revert) 커밋을 취소(reset)하는 방법을 배우게 되었습니다. 결국 원래 상태로 돌아올 수 있었고 커밋 메시지 또한 다시 정상적으로 작성할 수 있게 되었습니다. 이 과정 덕분에 커밋을 관리하는 데에 두려움을 없앨 수 있었고 git 명령어 또한 배울 수 있었습니다.

[입력의 역할]

입력받은 내용을 검증해서 객체로 변환해 주는 과정이 있다고 할 때 입력의 역할이 어디까지인가에 대해서 고민되었습니다. 클래스가 한가지 역할을 한다고 하면 입력을 담당하는 클래스는 입력만 받고 넘겨줘야 하는 것과 객체에 저장되기 위한 원시자료형까지 변환시켜 주는 것 중에서 어느 것이 옳은지 고민되었습니다. 만약 전자가 옳다면 원시자료형으로 변환을 시켜주는 클래스를 따로 구현해야 하는데 이러한 과정을 편리하게 만들어 주기 위해서 Stream이 생겨났다고 생각했기에 Stream을 통한 형 변환을 또다시 함수로 묶어줄 필요가 없다고 생각했습니다. 따라서 입력의 역할은 사용자로부터 입력을 받고 입력값에, 의미에 맞는 원시 자료형까지의 변환까지 하고 나만의 규칙을 정하게 되었다.

3주차의 목표

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

공통 피드백

  • README를 상세히 작성한다
    어떤 프로젝트인지, 어떤 기능을 담고 있는지 마크다운 문법 공부
  • 기능 목록을 재검토
    변경될 수 있는 가능성이있는 기능 목록은 사용하지 않는다 -> 변경될 수 있기 때문
    구현해야 할 기능 목록을 정리하는데 집중
    예외적인 상황도 기능 목록에 정리 - 구현하면서 계속 추가
  • 기능목록은 추가적으로 업데이트 하자
  • Readme 좀 더 노력해서 작성

2주차 리뷰

  • 입력을 받고 파싱해주는 클래스를 따로 처리
  • Validator 단위 테스트 하기
  • 일급컬렉션 확실히 학습
  • 출력 역할 분리
  • 생성자 주입은 final로 불변성을 보장시키자
  • 오류 메시지 파일 분리
  • string.format() 을 사용해서 출력문 폼 정해주기
  • console.close()

추가 해볼 사항

  • BeforeEach
  • Enum(에러메시지, 출력메시지)

느낀점에 추가하기

옳은 것 또한 중요하지만 내가 생각하고 느끼고 배운것을 작성하는 것이 중요하다고 생각한다.

  • 테스트에 대해 어떤 유용함을 느꼈는지
  • 클래스가 한가지 역할을 한다고 했을때 입력을 담당하는 클래스는 입력만 받아주는 것이 옳은가 입력하고 객체에 저장되기위한 원시자료형까지 변환시켜주는 것이 옳은가
    만약 전자가 옳다면 형변환 시켜주는 클래스를 따로 구현해야하는데 이때 스트림으로 만들어줄 수 있는 기능을 따로 함수로 지정해서 저장 시켜줘야한다. 이러한 과정을 편리하게 만들어주기 위해서 Stream이 생겨났다고 생각했기에 원시 자료형으로의 변환은 스트림으로 진행한다.
    대신 객체를 다른 객체로 변환해주는 것은 당연히 메서드로 생성해줘서 관리해주는것이 맞다고 생각한다.(이렇게 나만의 규칙을 정함)
  • git 사용법

실수로 한번에 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 명령어를 학습해보았다.

새로 적용해본 것

  • 테스트할 방법을 생각 -> 구현 -> 테스트 순서로 테스트할 방법을 생각하고 그에 맞춰서 구현을 함
  • try-catch로 입력을 받을때까지 반복
  • 컬렉션을 생성해서 정보를 저장시킨뒤에 불변성을 보장하고 싶다면 Collections.unmodifiableList() 메서드를 사용한다
    • 이때 주의할 점은 새로운 컬렉션으로 복사한 다음 사용해야한다는 것이다.
      • 그 이유는 원본 컬렉션을 포장하는 느낌이라 원본을 수정하면 unmodifiableList() 적용시킨 컬렉션도 수정된다.
  • format을 통한 출력
profile
멋있는 사람 - 일단 하자

0개의 댓글