지난 10월 19일부터 우아한테크코스 프리코스에 참여하고 있다. 첫 주차는 시험기간 및 과제, 여러 일과 겹쳐 회고를 남기지 못했는데, 이번 2주차와 남은 3, 4주차는 돌아보는 시간을 가지며 소중한 기억들을 기록하고자 한다!
1. 요구사항 분석 및 목표 설정
지난 1주차가 Java 환경 설정, 기본적인 프로그램 구현과 git, 컨벤션을 준수하는 것이 목적이었다면, 이번 주차의 목적은 메서드가 한 가지 일만 하도록 하게 하는 등의 좀 더 객체지향 프로그래밍을 유도하는 요구사항 등이 추가되었다.
개인적으로는 클래스를 분리하여 프로그래밍을 하는 것이 오랜만이고 자바도 오랜만에 사용하다 보니 첫 주차에는 무엇보다도 적응하는 데에 목적을 두고 기본적인 프로그래밍 구현, 메서드 분리에 신경을 써서 과제를 수행했다. 그리고 2주차 목표를 다음과 같이 설정했었다.
- 객체 지향 프로그래밍 원칙에 따라 클래스 분리를 해볼 것
- 테스트 코드 작성을 통해 테스트를 경험해볼 것
2주차에 미처 하지 못했던 클래스 분리를 가장 큰 목표로 설정했고 테스트 코드를 통해 코드의 완성도를 더하고 싶었다.
우선 첫 번째로 1주차 과제 제출 후 받은 공통 피드백을 살펴보았다.
- 요구사항을 정확히 준수한다.
- 커밋 메시지를 의미 있게 작성한다.
- git을 통해 관리할 자원에 대해서도 고려한다.
- PR을 보내기 전 브랜치를 확인한다.
- 이름을 통해 의도를 드러낸다.
- 축약하지 않는다.
- 공백도 코딩 컨벤션이다.
- 공백 라인을 의미 있게 사용한다.
- space와 tab을 혼용하지 않는다.
- 의미 없는 주석을 달지 않는다.
- IDE의 코드 자동 정렬 기능을 활용한다.
- Java에서 제공하는 API를 적극 활용한다.
- 배열 대신 Java Collection을 사용한다.
요구사항을 정확히 준수하는 것은 가장 큰 우선순위로 둬야 한다는 것을 다시금 생각하게 되었고, 공백도 코딩 컨벤션으로 가독성에 큰 역할을 담당한다는 것을 깨닫게 되었다. 그래서 이번 주차에는 공백을 특히 더 신경쓰며 과제를 진행했다.
이후에는 우테코에서 제공받은 숫자 야구 게임을 이용한 기초 강의 영상을 돌려보며 내가 빼놓은 것은 무엇인지, 기본적으로 가져야 할 자세가 무엇인지 학습하였다. 객체 지향 프로그래밍에 대한 조언은 다음과 같았다.
- 기능을 가지고 있는 클래스를 인스턴스화(=객체) 한다.
- 필요한 기능을 (역할에 맞는) 인스턴스가 수행하게 한다. (의인화)
- 각 결과를 종합한다.
너무나도 당연한 얘기들이지만 당시에는 뒤통수를 맞은 것 같았다. 객체 지향 언어를 배운다면 도입 부분에 학습할 내용인데, 생각하고 있지 못했다는 것이 아쉬웠고 바보같았다. ㅠㅠ
2. 기능 구현
이제 본격적으로 과제를 수행하기 시작했다. 과제는 자동차 경주 게임으로, 지난 주차 과제인 숫자 야구 게임보다 요구사항이 늘어났고 무엇을 주안점에 두는 지도 다르게 느껴졌다.
자동차 경주 게임 저장소: https://github.com/woowacourse-precourse/java-racingcar-6
이번 주차에서 내가 세운 첫 번째 목표가 클래스를 분리하는 것이었지만, 막상 시도하려니 쉽지 않았다. 돌이켜보면 예전 자바를 처음 배울 당시 프로그래밍이 낯설어 객체 지향 보다는 프로그램이 돌아가도록 하는 것에 급급했었던게 생각났다... ㅋㅋㅜ
그래서 우선은 프로그램을 메서드의 단일 책임 원칙만 신경쓰며 구현을 진행하였고, 이후 리팩토링을 통해 클래스를 분리하고자 하였다.
구현을 하는 데에 있어서 가장 신경 썼던 부분은 변수명과 메서드명 네이밍, 가능한 한 자바8 이후 문법 사용하기였다. 또 예외 처리는 비교적으로 자율적이기 때문에 무엇을 예외로 지정할 것인지 고민도 많이 했다.
우여곡절 끝에, 코드 작성을 완료하였고 기본 제공 테스트코드도 모두 통과했다. 이제는 리팩토링을 무진장 할 차례였다...!
애초에 '클래스 분리만 하면 되니까 기존 코드를 크게 수정하지 않고 나누기만 잘 하면 되겠지...?' 라는 생각으로 임했는데, 오산이었다... 리팩토링을 진행하며 가장 많이 신경써야 했던 부분은 '객체화'였다. 객체를 인스턴스화 하여 사용할 것이기 때문에 당연히 프로그램 로직 자체가 바뀌어야 했다.
private static void moveForward(List<Integer> moveCountList) {
for (int i = 0; i < moveCountList.size(); i++) {
moveOrNot(moveCountList, i);
}
}
private static void moveOrNot(List<Integer> moveCountList, int i) {
int random = Randoms.pickNumberInRange(0, 9);
if (random >= 4) {
move(moveCountList, i);
}
}
private static void move(List<Integer> moveCountList, int i) {
Integer currentValue = moveCountList.get(i);
moveCountList.set(i, currentValue + 1);
}
<기존 코드>
public class Car {
private final String name;
private int position = 0;
public Car(String name) {
this.name = name;
}
public void moveOrNot() {
int random = Randoms.pickNumberInRange(0, 9);
if (random >= 4) {
this.position++;
}
}
public String getName() {
return name;
}
public int getPosition() {
return position;
}
}
private void moveForward() {
for (Car car : cars) {
car.moveOrNot();
}
}
<변경 이후 코드>
위는 자동자가 전진할 때의 로직인데, 자동차를 'Car'로 객체화 하면서 로직 자체가 바뀌었다는 것을 알 수 있다.
리팩토링하는 과정이 쉽진 않았지만, 하면서 분명 느낀 것은 '코드가 훨씬 이해하기 쉽고 직관적이게 되는구나!' 였다. 길지 않은 프로그램이지만 분명 클래스를 분리하고 객체를 도입하는 것이 제3자의 입장에서도 훨씬 와닿을 것 같았다. 리팩토링 과정은 OOP의 장점을 몸소 느끼게 되는 계기가 되었다.
이외에도 클래스를 RacingGame, InputReader, MessagePrinter, RandomGenerator, Validator로 분리하여 다음과 같이 역할을 분할 하였다.
- RacingGame: 프로그램 전반적인 동작을 나타내는 컨트롤러 역할
- InputReader: 입력값 관리
- MessagePrinter: 출력값 관리
- RandomGenerator: 난수 생성
- Validator: 예외 처리 관리
3. 테스트 코드 작성
리팩토링을 끝내고, 이번에는 테스트 코드 작성에 돌입했다.
우선은 예외처리를 담당하는 아래의 테스트 코드들을 작성하는 것을 목표로 두고 작성했다.
이때 자동차 이름이 빈 문자열인 경우에 테스트를 통과하지 못해 미처 몰랐던 기능 코드를 수정했고, 테스트 코드가 필요한 이유를 여기서도 몸소 체험할 수 있었다!
이제 기능 코드를 작성하려고 보니... 애초에 코드 작성 단계에서 테스트를 고려하지 않다보니(정확하게는 코드 작성 단계에서 고려해야 할 줄 몰랐던...) 테스트 하기가 쉽지 않았다. 기능들 대부분이 접근제어자 private으로 되어있고, 가능한 것 마저도 사용자의 입력을 받는 Console.readLine()을 테스트 해야 하는데 이 방법은 mock 라이브러리를 이용해야 가능하다는 것을 알게 되었다...
mock을 사용해도 된다 vs 안된다 의견이 분분한 가운데, 애매하니 사용하지 않았고, 결국 기능 테스트코드는 한 개도 작성할 수 없었다 ㅜㅜㅜ
정말 테스트를 얕봤구나, 싶었다. 테스트를 좀 더 깊이 고민하고 미리 고려하여 코드를 작성했어야 했는데, 그러지 못했고 테스트의 벽에 부딪혔다. 정말로 다음 주차를 준비할 때에는 테스트를 고려하여 단위테스트를 잘 작성할 수 있도록 성장해야겠다.
아래의 향로님 블로그도 읽어보며...
4. 소감
- 아쉬운 점 및 부족한 점
- 처음 구현 단계에서 곧바로 클래스를 분리하지 못한 점이 아쉬웠다.
- 테스트에 대해 깊이 있는 고민을 하지 못한 점이 아쉽다.
- 예외 처리의 경우의 수에 대해 더 꼼꼼하게 고려하지 못했다.
- 다음 주에 도전해 볼 것들 및 개선할 점
- 처음부터 객체 지향 프로그래밍 원칙을 준수하며 프로그램 구현하기
- 설계 패턴 적용해보기
- 테스트 방법에 대해 밀도 있는 학습과 더불어 이번 주차에 가로막혔던 문제점들을 해결하기
그리고 이번 주차 공통 피드백에 있는 다음의 내용은 나에게 또 충격을 주었다. 나의 짧은 코딩길(?)에서 변수명에 자료형을 붙이는 건 늘 습관이었는데...(특히 ~List) 사소한 것 하나 하나 많이 배운다!
지난 주차와 비교해봤을 때, 고민하는 부분이 달라졌다는 것은 분명 한 단계 나아갔다고 생각한다. 이번 주차 과제 메일에 아래의 문구가 마음속 깊이 와닿았다.
프리코스 커뮤니티에는 정말 실력자분들도 많고 그 분들의 결과물을 볼 때면 매번 놀라곤 한다. 나와 고민점도 다르고...
조급해 하지 않고 나와 비교하자, 그리고 무엇보다도 즐겁게 하자 :)
'기타' 카테고리의 다른 글
[취준] 싸피 SSAFY 12기 전공반 지원 후기 (9) | 2024.07.16 |
---|---|
[우아한테크코스] 프리코스 3주차 회고 (0) | 2023.11.22 |
SW 코칭프로그램 1~6주차 내용정리 (0) | 2022.07.15 |