티스토리 뷰

 

정해진 답이 없는 것을 고민하는 과정이 힘들고 어렵기도 했지만, 그 속에서 다양한 생각과 방법을 떠올려보며 스스로 판단하여 선택하는 방법을 배울 수 있었습니다.

 

2주 차를 보내며

객체지향적인 설계 & 테스트 코드 작성

2주 차 과제는 숫자 야구 게임을 구현하는 것이었습니다. 숫자 야구 게임은 학교 수업에서도 다뤘던 적이 있을 만큼 많이 알려진 문제입니다. 숫자 야구를 구현하는 것은 크게 어렵지 않다고 생각했습니다. 이번 과제에서 추가된 프로그래밍 요구 사항은 총 4가지 입니다.

 

1. indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다
2. 3항 연산자를 쓰지 않는다.
3. 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
4. JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.

 

저는 이번 과제에서 객체지향적으로 설계하고 테스트 코드를 작성하는데 집중했고, 객체별로 기능을 작게 분리하면서 자연스레 함수가 한 가지 일만 하도록 했습니다. 3항 연산자는 평소에도 사용하지 않기 때문에 크게 신경쓰지 않았습니다. 기능 목록을 작성하기 전, 먼저 객체를 구분하고 해당 객체가 하는 일을 정의하기 위해 시간을 많이 투자했습니다.

숫자 야구 게임 객체 설계

 

숫자 야구게임을 실제 야구에 대입해보고 많은 고민을 한 끝에 제가 설계한 구조입니다. 야구는 투수, 타자, 심판, 경기 결과(볼카운트)로 구성된다고 생각해 숫자 야구에서도 동일한 흐름과 구조로 객체를 설계했습니다.

 

- BaseballGame: 전체 게임의 흐름을 제어하는 역할을 하는 객체입니다.

- Computer: 야구에서 '투수'의 역할입니다. 숫자 야구에서는 게임 마다 정답을 생성하는 역할을 담당합니다. 

- User: 야구에서 '타자'의 역할입니다. 숫자 야구에서는 정답을 맞추기 위해 숫자을 입력하는 역할을 하는 객체입니다.

- Referee: 야구에서 '심판'의 역할입니다. 숫자 야구에서는 사용자가 입력한 숫자를 정답과 비교해 판단하는 역할을 하는 객체입니다.

- GameResult: 야구에서 '경기결과(카운트)'의 역할입니다. 숫자 야구에서는 스트라이크, 볼 카운트를 가지고 결과를 출력하는 역할을 하는 객체입니다.

 

이렇게 기본적으로 설계를 마치고 기능 목록을 작성해보니 훨씬 명확하게 작성할 수 있었습니다. 이전에 프로젝트를 진행할 때 요구사항을 분석하고 기능 목록을 작성해서 개발을 했었지만 예상한 기능을 기반으로 작성하여 모호한 기능이 많았고 그에 따라 수정사항도 많이 생겼었습니다. 그래서 개발 시간도 더 많이 소요됐었고 테스트 코드를 작성하지 못했었습니다.

이전에 개발 시 작성했던 기능 목록

 

숫자 야구를 진행하며 작성한 기능 목록과 테스트 기능 목록입니다. 확실히 이전보다 명확하게 이해할 수 있는 것 같습니다. 객체에 따라 기능을 구분하여 정리하였고 기능 목록에 따라 테스트 기능 목록까지 자연스럽게 작성할 수 있었습니다. 설계를 하는데 시간이 많이 걸렸지만, 실제 개발을 진행하는 시간은 오래 걸리지 않았고 테스트 코드도 작성하기가 훨씬 편하다고 느꼈습니다.

숫자 야구를 진행하며 작성한 기능 / 테스트 목록


고민했던 점


설계를 마치고 기본적인 기능을 구현한 후 코드를 살펴보며 심판 객체의 스트라이크, 볼 카운트를 setter를 사용해 값을 할당하는 과정을 개선하고 싶었습니다. 객체의 상태를 외부에서 접근해 변경할 수 있는 가능성이 있다고 판단했기 때문입니다.
제가 생각한 개선 방법은 총 3가지였습니다.
1. 게임을 진행할 때 마다 심판 객체를 생성하며, 그 과정에서 값을 주입
2. 카운트를 계산하는 함수 내에서 지역변수를 사용해 카운트 값 반환
3. 심판이 스트라이크, 볼 카운트 변수를 가진 GameResult 객체를 만들어 반환

저는 야구게임이 한번 시작되면 해당 게임에서 사용자, 심판, 컴퓨터는 고정된(바뀌지 않는) 객체라고 판단했기 때문에 판정할 때마다 새로운 심판을 생성하는 1번은 제외하였습니다. 2번으로 진행할 경우 새로운 객체를 생성할 필요가 없다는 장점이 있었지만, 심판의 역할(정체성)이 모호해지는 것 같았습니다. 3번은 GameResult라는 좀 더 구체적인 객체를 이용해 값을 주입할 수 있는 장점이 있었지만 판정할 때마다 새로운 객체를 생성하는 오버헤드가 생긴다는 단점이 있었습니다.

2번과 3번 둘 다 코드로 작성해 보고 정말 많은 고민을 한끝에, 사용자가 숫자를 입력할 때마다 심판이 새로운 게임 결과 객체가 생성하는 것이 자연스러운 흐름이라고 판단하여 3번 방법을 선택해 로직을 개선했습니다.

왼쪽: 개선 전, 오른쪽: 개선 후

 

또한 테스트 코드를 작성하며 테스트를 위한 생성자나 함수를 작성하는 것이 적절한가에 대해 많이 고민했습니다. 비즈니스 로직과 상관없는 생성자나 함수가 코드 내에 위치하는 것이 불필요한 것 같았지만, 테스트 코드를 좀 더 명확하게 작성할 수 있다는 장점이 있었기 때문입니다. 어떤 것이 더 나은 방법일까 고민을 했지만 뭐가 더 좋다라고 정의할 수 없다고 느꼈습니다. 비즈니스 로직을 위한 코드와 테스트를 위한 코드 사이에서 스스로 근거를 가지고 적절히 판단해 결정을 내리는 것이 옳다는 생각이 들었습니다. 


 

글을 마치며

정해진 답이 없는 것을 고민하는 과정이 힘들고 어렵기도 했지만, 그 속에서 다양한 생각과 방법을 떠올려보며 스스로 판단하여 선택하는 방법을 배울 수 있었습니다. 1주 차 회고에서 stream을 적극적으로 사용하기로 했지만, 이번 주 과제에서는 일부 함수에서만 적용해보고 전반적으로는 적용하지 못한 부분이 아쉬움으로 남습니다. 다음 주차 부터는 좀 더 익숙해지도록 노력해야 할 것 같습니다.

 

2주 간 프리코스를 진행하며 커밋 메시지 작성법, 클린 코드 작성법, 객체지향적인 설계, 테스트 코드 작성법 등을 학습하며 스스로 부족한 점을 많이 찾았고, 이를 보완해 나가며 시야를 넓혀가고 있는 것 같습니다. 남은 2주 동안 더 많은 것을 배워 보도록 하겠습니다 !

댓글