2주차 목표
2주차의 목표는 MVC패턴 적용과 테스트 코드와 친해지기였다.
운이 좋게도 이번주 학습 목표가 '테스트 코드에 익숙해지기'여서 테스트 코드에 집중할 수 있었다.
MVC패턴 적용하기
MVC패턴을 적용하기 위해 먼저 model, view, cotroller패키지를 만들어두고 어떻게 설계해야할지 고민했다.
각 컴포넌트들이 서로 어떻게 대화해야하는지 공부하고,
프리코스 커뮤니티에 올라온 수많은 1주차 미션 PR들 중 MVC패턴을 적용한 10명의 코드를 분석해봤다.
누군가는 view가 controller에서 받은 값을 분해해서 보여주기도하고 또 다른 누군가는 view가 완전히 출력 형식으로 변환된 값만 받도록 하는 등, 같은 디자인 패턴이라도 각기 다른 모양 가지고 있었다. 그래서 나는 필요한 기능들을 컴포넌트의 역할에 맞게 분배하고 기능구현을 시작했다.
DTO도 사용해보려 했지만 기능 구현만으로도 생각할게 많아서 그러지 못했다. 3주차때 가능하면 DTO를 사용해보려한다.
MVC패턴으로 개발하면서 controller의 역할이 모호했던게 고민이었다.
아래 코드에서 보듯이 controller가 자동차 경주를 진행시키도록 만들어 놓으니 controller가 model에 너무 많이 간섭하는 것 같았다.
public class RacingCarController {
private InputView inputView;
private RacingCarService service;
public RacingCarController(InputView inputView, RacingCarService service) {
this.inputView = inputView;
this.service = service;
}
...
public void racing() {
service.createCarList(userInputCarNames());
Integer tryNumber = service.parsingTryNumber(userInputTryNumber());
OutputView.racingResultMessage();
if (tryNumber == 0) {
OutputView.racingResult(service.racingResult());
}
for (int step = 0; step < tryNumber; step++) {
service.move();
OutputView.racingResult(service.racingResult());
}
findWinner();
}
...
지금보니 model에서 자동차 경주를 진행시키고 controller가 명령만 내리도록 하는게 controller 본연의 뜻인 '제어장치'의 역할에 충실할 것 같다.
또 하나의 고민은 의존성이다. 각 컴포넌트마다 만든 클래스들은 서로 연관관계가 있었다. 메서드에서 객체를 생성해 값을 반환하기도 하고, 클래스안에서 다른 클래스 인스턴스를 생성해 사용하기도 했다.
그래서 의존성에 관한 자료를 찾다보니 생성자로 의존성을 주입하는 방식을 알게 되었다.
위의 코드를 보다시피 controller에 생성자로 의존성을 주입했다.
문제는 Application에서 controller를 실행시킬때 필요한 의존성을 선언해야했다.
public class Application {
public static void main(String[] args) {
RacingCarController controller = new RacingCarController(new InputView(),
new RacingCarService(new RandomNumber()));
controller.racing();
}
}
이러면 클라이언트가 알 필요 없는 것들까지 알게되는 꼴이어서 어떻게 할지 고민하다가 시간이 없어 일단 제출했다.
제대로 공부해서 3주차때는 이를 보완할 것이다!
테스트 코드와 친해지기
이때까지 스스로 테스트 코드를 작성해본 적이 없어서 제공된 테스트 코드들을 참고하며 작성했다.
잠시 테스트 주도 개발을 해볼까 주제 넘은 생각을 해봤으나 먼저 기능 구현부터 하고 단위 테스트를 하기로 했다.
4일차에는 구조를 완전히 바꾸는 작업을 했다.
원래 자동차마다 객체를 생성하지 않았지만 Car
클래스를 만들어 자동차 하나마다 상태를 가지게했고, 그 자동차들을 모아놓은 CarList
도 만들었다. 그러다보니 구조를 바꾸기 전에 작성해놓은 테스트 코드가 전혀 작동하지 않아서 테스트 코드도 완전히 바꿔야했다. 그래서 테스트 코드를 새로 작성한 뒤에는 내부 로직이 변경되더라도 큰 틀에서의 동작은 바뀌지 않도록 기능을 만들어 나갔다.
given-when-then을 이렇게 쓰는 구나
테스트 코드를 작성하며 처음에는 무작정 값만 맞는지 비교했지만, 프리코스 커뮤니티에서 given-when-then패턴의 테스트코드를 보고 이 패턴을 다시 공부했다. 이론만 알고 있던 개념이었는데 책과 인터넷으로 예시 코드를 살펴보면서 테스트 코드를 직접 작성해나갈 수 있었다.
예시 코드들 중에서 재밌는 걸 발견했는데, 하나의 테스트 메서드에 여러 인자들을 전달해서 한 번의 실행으로 여러 상황을 테스트할 수 있는 방법이 있었다. 덕분에 중복될 뻔한 테스트 코드를 작성하지 않을 수 있었다.
보여줄게 완전히 달라진 나
2주차 미션 3일차까지는 테스트 코드를 작성해도 Application을 실행해 기능테스트를 많이 했는데, 문득 생각해보니 제출 날에 가까워질 수록 Application 실행을 거의 안하고 있었다. 점점 테스트 코드 작성에 익숙해지다보니 테스트 코드로 확인하는게 훨씬 빠르고 정확하다는 것을 깨달았다. 결과적으로 테스트 코드를 작성하면서 개발하니 저번 주보다 훨씬 빨리 기능을 구현할 수 있었다.(남은 시간은 삽질과 테스트 코드 작성에 썼다..)
단위 테스트를 수박 겉핥기로만 알고 있어서 내친김에 학교 도서관에서 단위 테스트에 대한 책을 읽어봤다. 바로 이 책이다.
https://www.yes24.com/Product/Goods/104084175
단위 테스트 - 예스24
소프트웨어 개발에 있어 단위 테스트는 이제 선택이 아니라 필수가 됐다. 단위 테스트에 대한 오해를 바로잡고, 올바른 단위 테스트에 대한 원칙, 테스트를 작성하는 스타일과 효과적인 테스트
www.yes24.com
단위 테스트에도 다양한 테스트 전략이 존재하며, 테스트 코드로 값 검증뿐만 아니라 구조 리팩토링도 가능하다는 것을 알게 되었다.
책을 정독해서 3주차에는 더 나은 테스트 코드를 작성해 빠르게 기능을 구현하고 리팩터링에 더 많은 시간을 할당할 계획이다.
헛된 삽질은 없다
이틀 동안 입출력 테스트 하나가 통과되지 않아서 별의별 방법을 다 찾아봤다. 로직을 수차례 변경하고 테스트 코드도 다시 작성해도 통과되지 않아서 제공되는 테스트 라이브러리들을 자세히 살펴봤다. 답은 거기에 있었다. 출력시 항상 trim()이 적용되서 테스트에서 기대하는 출력이랑 달랐던 것이다! 바로 테스트 코드를 수정했더니 통과했다. 앞으로 다른 사람들이 만들어 놓은 라이브러리를 사용할때는 좀더 꼼꼼히 내부 로직을 살펴봐야겠다고 다짐했다.
정답은 없지만 오답은 있다
마지막으로 코드 리뷰 링크를 남긴다. 가루약 같은 쓴 소리도 달게 받겠다.
https://github.com/woowacourse-precourse/java-racingcar-6/pull/1638/files
[자동차 경주 게임] 정재윤 미션 제출합니다. by ddolboghi · Pull Request #1638 · woowacourse-precourse/java-rac
github.com
'우테코 6기 프리코스' 카테고리의 다른 글
4주차 회고 (0) | 2023.12.20 |
---|---|
3주차 회고 (4) | 2023.11.09 |
1주차 회고 (0) | 2023.10.25 |