마지막 주차에는 설계를 잘하고자 고민을 많이 했다. 사실 아직 최선의 방법인 것 같지는 않지만, 여기까지의 고민과정을 정리해보았다. 코딩 컨벤션은 그대로 지키면 되는데, 좋은 구현은 아직 어렵게느껴져서 계속 고민하는 과정 중이다. 🤔

🧱 설계

MVC 설계 과정은 2, 3주차에 많이 썼기에, 이번 글에서는 생략했다. 대신 이번에는 게임 전체 흐름과, Model에서 클래스 분리하는 과정을 정리해보았다.

게임 진행 흐름 - 순서도

이번에는 게임 흐름이 단순하지 않아서 이 흐름을 잘 정리하고 싶었다.(어디에 반복문이 있어야하고, 재시작을 해줘야 하는 지 등등..) 도메인단을 고민하기 전에, 아래처럼 전체 게임의 흐름을 순서도로 정리해보았다.

img

이 게임의 전체 흐름을 파악하기 위해 순서도를 계속 바꿔 그려가며 그렸고, 코드에서 저 흐름이 파악될 수 있도록 구현했다. 다음은 메소드명과 합쳐진 순서도이다. img

전체 흐름 메소드를 따로 빼고, 메소드명만으로도 흐름을 파악할 수 있도록 이름 짓고, 메소드 내부에서는 해당 메소드명에 맞는 기능만 구현하고자 했다. (아래 코드는 전체 흐름을 설명하기 위해 input/output 관련 View 코드는 생략했다.)

    private void runGame() {       // 전체 시작!
        createBridge(bridgeSize);  // 다리 생성

        startBridgeGame();         // 다리 게임 시작
        printGameResult();         // 종료
    }

    private void startBridgeGame() {

        do {
            playEachRound();         // 한 라운드 시작
        if (isFail()) break;     // 이동 성공?!
        } while (isFinalRound());    // 마지막 라운드까지 성공?!
    }

이 전체 흐름은 Controller 상에서 구현한 내용이다. 도메인단 관련 내용을 생각하기 전에 스스로 전체 흐름을 이해하고 싶어서, 이 부분을 먼저 그림으로 정리했었다. 그리고 본격적으로 Model 클래스들을 어떻게 나눌지 고민했다.

🧶 도메인단

img

도메인 클래스들

클래스 분리 - 객체 지향 설계? 일단 이전 과제들에서는 Move / Bridge / Referee 이런식으로, 유저 / 정답 / 판단하는 클래스 이렇게 분리했었다. 근데 이번에는 유저 / 정답 클래스로 분리하고, 유저의 이동 성공 여부를 유저 클래스 본인이 가지고 있도록 했다. 객체가 스스로의 상태를 가지고 있도록 구현하고자 했다. 아래는 주요 클래스들이 가진 상태/행위이다. 클래스명의 상태/기능만 가지게 하고 싶었다.

  • Move : 사용자의 이동 상태 (이동 방향, 이동 성공 여부)
  • Bridge : 다리 상태 (다리, 매 라운드 발판 위치)
  • Result : 게임 결과 (총 시도 횟수, 이동 기록)

사실 이렇게 유저와 정답을 판단해야될 때, Referee같은 판단하는 클래스가 있는 게 좋을 지? 없는 게 좋을 지 헷갈린다…! 객체 자신이 자기 클래스명에 맞는 데이터만 가지고, 그 테이터의 상태와 기능(메소드)만 갖도록 구현하고 싶었다. 아직 헷갈리고 모호한 부분들이 있어서, 객체 지향 설계에 대해 더 고민을 많이 해봐야 될거같다 📚 (사실 이 설계가 아직 마음에 들지는 않는다..!ㅠ)

⇒ 이 부분은 후에 더 공부하면서 리팩토링하고, 글 계속 업데이트 할 예정이다! 지금까지 설계하면서 고민한 과정도 기록하고자 했다!

BridgeGame - Controller? Service?

img

PR에 올린 코드에서는 저 도메인 클래스들을 BridgeGame라는 도메인 클래스가 관리하고 있다. 과제 규칙 상 BridgeGame에 View 코드가 있으면 안되서 BridgeGame을 Controller로 활용하지는 못했다.

근데 지금 생각해보니 BridgeGame을 Domain단으로 두지 말고 Service단으로 하는 게 좋았을 것 같다. 그러면 Domain 클래스들(Move / Bridge / Result)을 Service단에서 관리하게 할 수 있다. 비즈니스 로직은 도메인단 클래스에만 구현하고, Service 단에서 도메인들을 조합해서 로직을 구현하고, Controller단에서 이 Service단과 View단을 조합해서 구현하는 게 더 좋은 설계이지 싶다! 회고 쓰면서 그림 그리니까 더 명확히 잘 보이는 것 같다.

Result와 Record : 컴포지션

Result 클래스에 결과와 관련된 데이터들을 모아두었다. 즉 총 시도 횟수, 다리 이동 기록를 담고 있다. 여기서 Result 클래스는 Record 클래스를 컴포지션(합성)으로 참조하고 있다. (컴포지션이란? 상속보다 컴포지션을 사용하는 이유)

즉, 결과 관련 클래스(Result)에서 이동 기록 데이터를 따로 Record 클래스로 분리했다. 왜냐하면 다리 이동 기록이 2차원 List이고 로직 상 윗방향/아랫 방향 다리가 각각의 List 인스턴스 변수로 존재해야 했다. (기록 업데이트 시, 매번 윗 방향 다리/아랫 방향 다리 중 선택해야 했다. 근데 최초로 이동기록을 업데이트할 때, 2차원 리스트.get(0) 을 사용해서 접근하면 NPE가 발생하게 된다. 그래서 윗방향/아랫방향 다리를 따로 인스턴스 변수로 정의해주었다.)

또한 다리 기록만을 위한 기능/메소드가 꽤 필요했기에 아예 Record 클래스로 분리해주고, Result가 컴포지션(합성)으로 참조하게 했다.

추가로, 이번에 if-else 대신 Early-Return과 Enum을 적극 사용했는데,
이 부분은 길어져서 따로 글을 올릴 예정이다.

📜 회고

마지막 주차에는 객체지향적인 설계, 좋은 구현을 고민하면서 진행하고자 했다. 가장 어렵고, 고민되고 뭔가 더 개선할 수 있을 것 같은데, 길이 명확하게 보이지 않아 답답하기도 했다. 아직 자료를 찾아봐도 어렵거나 추상적인 내용으로 다가와서 공부를 더 해야 길이 보일 것 같다. 이번 마지막 주차에서 설계를 ‘잘’하려고 했는 데 너무 아쉬움이 남아 또 배우면서 리팩토링해야겠다. 일단 여기까지 구현한 내용을 토대로 계속 객체 지향, 좋은 구현에 대한 공부를 해서 개선해야겠다 🌱

프리코스 전체 회고

이번에 프리코스를 진행하면서 ‘코드’에 대해 새로운 관점이 생겼다. 클린 코드, 코딩 컨벤션, 구현, 커밋 메시지에 대한 고민을 처음 해봤다. 이전에는 기능 구현, 새 기술을 써보는 것에 집중했다면, 이번에는 좋은 설계를 위해 리팩토링하고, 협업할 때 어떻게 쉽게 내 코드가 읽힐까 고민하고, 클린 코드, 확장성 등등의 고민했다. 아예 새로운 관점이 생겼고, 앞으로 이런 식으로 공부해야겠구나..하는 깨달음(!!)을 얻는 기간이었다. 아직 시작이라 부족한 점이 많지만, 그래도 4주 동안 배운 내용도 많았다.

그리고 프리코스하면서 슬랙, 코수타, 디스커션에서 같이 소통하면서 진행하니까 더 지치지 않고 힘도 얻고, 더 많이 배울 수 있었다. 특히 피어 리뷰를 서로 해보는 게 재밌었는데, 내가 잘못 알려주지 않도록 공부해서 리뷰를 달려고 했었다. 그리고 같은 과제를 하는 다른 사람의 코드를 리뷰하니까 더 배우는 점이 많았던 것 같다. 나랑 다른 관점으로 설계하는 것도 보이고, 어디서 클래스 분리를 했고 유효성 검증을 했는 지가 보이면서, 이 사람은 이런 생각으로 여기서 클래스 분리했구나가 보여서 많이 배울 수 있었던 것 같다.

우테코 프리코스 하면서 많이 배우고, 커뮤니티로 함께 해서 정말 지치지 않고 즐겁게 진행했었다. 취준하면서 프로젝트를 기능 구현에만 초점을 두어서, 좋은 구현, 클린 코드와 관련해서 코드 리뷰를 받고 싶었다. 이번 우테코 하면서 클린 코드, 좋은 구현 등 관련해서 공부의 방향성을 어느 정도 찾은 것 같다. 코치님들의 조언으로 많이 배우고, 열정적인 동기분들보면서 자극도 많이 받은이 기간이 너무 소중하고 감사한 기회였다. 😃 혹시 내년에 누군가 이 과정을 고민 중이라면, 본과정은 못하더라도 프리코스만으로도 배우는 게 많으니, 꼭 경험해보길 추천드린다!