모눈종이에 사각사각

[프리코스] 4주차 회고 본문

활동기록

[프리코스] 4주차 회고

모눈종이씨 2022. 11. 24. 22:21

0. 시작

벌써 마지막 미션이다. 4주가 이렇게 빨리 지나가다니...

이번주 미션은 다리 건너기 미션이었다.

문제를 읽었는데 맞게 이해한건가? 싶던 찰나에 슬랙에 관련 영상을 올려주신 분들이 있어 잘 이해할 수 있었다.

더보기

https://youtu.be/JUrErvCiPDc -> 오징어 게임의 다리 게임(매운맛)

https://youtu.be/dAvPLL4y0uE -> 런닝맨의 다리 게임(순한맛)   

이번주의 미션은 3주차 미션에 리팩터링이 추가되었다!

3주차에도 일단 작성한 다음에 다 고쳤던 기억이 있었기에 이번에도 비슷하게 진행하려고 했다.

 

그러나 시작하기 전에 3주차 미션의 코드 리뷰를 통해 부족한 점은 무엇인지, 개선할 점이 있는지 먼저 학습했다.

1. 3주차 코드리뷰

1-1. static 메서드의 사용

UserInput클래스의 메서드를 static으로 작성한 것에 대해 질문을 주신 분이 계셨다.

나는 UserInput 클래스 안에 있는 메서드들이 인스턴스 변수를 사용하지 않기 때문에 static 메서드를 사용했었다.

그런데 질문을 받고 궁금하여 클린 코드 책을 찾아보았더니 다음과 같은 말이 있었다.

 

그런데 간혹 우리는 static으로 정의하면 안 되는 함수를 static으로 정의한다....(생략) ... 일반적으로 static 함수보다 인스턴스 함수가 더 좋다. 조금이라도 의심스럽다면 인스턴스 함수로 정의한다. 반드시 static함수로 정의해야겠다면 재정의할 가능성은 없는지 꼼꼼히 따져본다.  - 클린코드 p.381~382

 

재정의 할 가능성이 있는지 살펴본 후 정해야하는 문제인 것 같다.

 

이번주의 나의 결론은, InputView와 OutView 클래스는 재정의할 가능성의 거의 없으며, 인스턴스 변수 또한 사용하지 않기 때문에 static 메서드로 구현해도 괜찮겠다고 생각했다.

1-2. 예외 메시지의 간소화

3주차의 예외 처리 메시지를 출력하는 부분에서 이렇게 작성했었다.

try {
	// 코드생략
} catch (IllegalArgumentException e) {
    System.out.println(ERROR_MESSAGE);
    throw new IllegalArgumentException(ERROR_MESSAGE);
}

이 부분을 이렇게 하지 말고 다음의 코드처럼 각 예외처리에서는 throw만 해주고, 마지막에 예외처리 메시지를 한꺼번에 출력하는 코드를 작성하면 매번 작성했던 예외 처리 메시지 출력 코드의 반복을 줄일 수 있었다.

throw new IllegalArgumentExceptioin(ERROR_MESSAGE);
try {
	// 코드생략
} catch (IllegalArgumentException e) {
    System.out.println(e.getMessage());
}

정말 좋은 방법이어서 알려주셔서 감사했다..

2. 기능 요구사항

기능 요구사항을 꼼꼼하게 읽으면서 각 단계를 구조화 시켜보고, 각 단계별로 구현해야 할 사항을 readme에 꼼꼼하게 정리했다. 지난주에 빠뜨린 내용이 많았어서 더움 꼼꼼하게 살펴보았다.

 

이번주는 메서드의 길이가 10을 넘지 않아야 하고, 파라미터의 개수가 3개까지만 허용 되는 등의 요구사항이 추가되어 구현하는 동안 어떻게 메서드를 분리할 수 있을까에 대한 고민을 굉장히 많이 했다.

3. 일급 컬렉션, 원시타입 포장

이번주에 공부를 하면서 일급컬렉션원시타입 포장에 관련한 글을 읽었다.

 

컬렉션을 Wrapping하면서 그 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고 한다.

지난주에 나는 코드를 작성하다보니 일급컬렉션의 형태로 코드를 작성했고, 이러한 것을 지칭하는 용어를 알게 되었다.

일급컬렉션을 사용했을 때의 장점은 다음과 같다.

  1. 일급 컬렉션 관련 로직을 검증하는 코드를 넣을 수 있다.
  2. 불변성을 보장한다.
  3. 상태와 행위를 한 곳에서 관리할 수 있다.
  4. 이름이 있는 컬렉션을 만들 수 있다.

또한 변수를 선언할 때, 원시 타입의 변수를 선언하는 방법과 원시 타입의 변수를 객체로 포장한 변수를 선언하는 방법이 있다는 것을 알게 되었다. 원시 타입의 변수를 포장함으로써 얻을 수 있는 장점은 자신의 상태를 객체 스스로 관리할 수 있어 유지 보수에도 도움이 된다는 것이다.

 

이번에는 일급 컬렉션과 원시 타입 포장을 적극 활용하여 코드를 작성해보았다.

이는 3주차 공통 피드백에도 있었던 "객체는 객체스럽게 사용한다" 에도 해당하는 내용이라고 생각했기 때문이다.

 

다음의 이미지는 사용자에게 입력받는 값들을 포장한 클래스들이다.

왼쪽부터 차례대로 다리 크기, 움직임(U또는D), 재시작여부 명령어(R또는Q)를 나타내는 클래스이다.

원시값 포장

세 클래스 모두 처음에 생성자로만 생성할 수 있고, 생성자에서 검증까지 마친다.

이렇게 함으로써 InputView에서 각각의 값들을 검증하는 코드를 없앨 수 있었고, 코드가 훨씬 깔끔해졌다.

그리고 final 키워드를 사용해 값을 변경할 수 없도록 했다.

 

다음은 무작위 값으로 생성된 다리를 일급 컬렉션으로 만든 것이다.

사용자의 움직임 값을 매개변수로 받아 값을 확인하는 로직을 Bridge 클래스 내부에서 처리할 수 있도록 했다.

4. 테스트 코드를 꼼꼼하게 작성하자

지난주에 생각하지 못했던 부분 중 하나가 값의 예외 상황이었다.

0이 들어왔을 때, 음수가 들어왔을 때, 타입을 벗어난 값이 들어왔을 때 등 아주 다양한 상황이 존재한다.

이번에는 이 모든 경우의 수를 다 확인하기 위한 테스트 코드를 작성하여 테스트 하였다.

 

우선 정상입력이 들어왔을 경우를 테스트했다.

 

그리고 경곗값을 포함해서 타입을 벗어나는 값이 들어오는 경우를 테스트했다.

@ParameterizedTest와 @ValueSource 어노테이션을 이용해 여러 값을 한 번에 테스트 할 수 있었다.

5. Enum 활용하기

다리 게임에서 1은 U(위쪽 다리), 0은 D(아래쪽 다리)를 나타낸다.

1과 U, 0과 D는 관련 있는 값을이라고 생각했기 때문에 Enum으로 관리했다.

 

findByCode메서드로 랜덤 숫자값에 해당하는 mark를 번환하도록 했다.

6. 클래스 분리

6-1. BridgeGame에서 InputView와 OutputView 분리하기

InputView 클래스와 OutputView 클래스를 BridgeGame에서 사용할 수 없다는 요구사항이 있었는데 나중에 발견해서 BridgeGame에서 InputView와 OutputView 클래스를 분리하여 다시 구현했다.

 

GameController 클래스를 만들어서 GameController 클래스에서는 사용자에게 입력받고, 출력하는 기능과 게임을 반복할 수 있도록 하는 기능을 넣었다.

그리고 BridgeGame 클래스에서는 사용자 입력값을 바탕으로 움직이고, 이동 지도에 저장하고, 재시작 시에는 시도 횟수와 움직임을 초기화 하는 등의 기능을 하는 메서드로 구성했다.

그리고 Bridge클래스에 movement 객체를 인자로 넘겨 게임의 상태를 조회할 수 있도록 하는 메서드도 구현했다.

 

GameController 클래스 일부
BridgeGame 클래스 일부

6-2. BidgeMap 분리하기

List<String>으로 top과 bottom 지도를 따로 만들어서 관리를 했다. 이 둘은 서로 관련이 있는 것이라고 생각했기 때문에 BridgeMap 클래스를 만들어 분리하였다.

그리고 addMap 메서드를 만들어 BridgeMap 내부에서 스스로 판단해 List에 집어넣도록 구현했다.

7. 소감

제출완료!

3주차에 요구사항을 꼼꼼하게 읽지 않아 반영하지 못한 부분이 있던 것, 예외 처리를 꼼꼼하게 하지 못한 것에 대한 아쉬움이 있었다. 이번주에는 그부분을 반영하기 위해 신경을 썼다.

미션에 리팩토링이 있었듯이 우선 기능과 구조를 파악하고 코드를 구현하 다음에 하나씩 리팩토링을 거쳐 나가면서 클래스와 메서드를 분리하고, 객체를 객체스럽게 만들기 위해 노력했다.

코드 리뷰를 통해 성장할 수 있었고, "객체를 객체스럽게" 사용하기 위해 고민했던 한 주였다.

 

 

 

Comments