티스토리 뷰

요구 사항 분석

4주차 미션은 오징어 게임에서 나왔던 다리 건너기를 구현하는 것입니다. 입력한 길이의 다리가 생성되는데 위 칸과 아래 칸 중 이동할 수 있는 칸은 무작위로 정해집니다. 한 스텝당 위 칸과 아래 칸 중 선택하게 되는데 이동할 수 있는 칸은 1칸밖에 없기에 이동할 수 없는 칸을 선택하게 되면 게임을 재시작하거나 종료하게 됩니다.

 

다리 끝에 도달하면 성공하게 되는데 오징어 게임은 기회가 한 번뿐이지만 이 게임은 기회가 무제한이라는 점!

3주차와 마찬가지로 실행 흐름부터 정리하고 구현해야 할 기능에 대해 생각해 보았습니다. 크게 보았을 때 입력, 출력, 다리, 다리 생성, 다리 이동, 게임 재시작의 키워드를 확인할 수 있었고 키워드를 바탕으로 기능을 정리하였습니다. 구현한 기능은 아래와 같습니다.

 

Direction

  • 방향 정보를 생성한다.
  • 생성 코드를 생성한다.
  • 방향 입력으로부터 방향을 생성한다.
  • 생성 코드 입력으로부터 방향을 생성한다.

GameCondition

  • 재시작/종료 정보를 생성한다.
  • 상태 정보 입력으로부터 게임 상태를 생성한다.

Cell

  • 셀 정보를 생성한다.

Player

  • 게임 시도 횟수를 증가한다.
  • 게임 시도 횟수를 반환한다.
  • 이동 방향 정보가 추가된다.
  • 이동 방향 목록을 반환한다.

BridgeMaker

  • 길이에 해당하는 다리 모양을 생성한다.

Bridge

  • 플레이어가 이동할 수 있는 상태인지 검증한다.
  • 게임 종료 여부를 확인한다.

BridgeGame

  • 다리를 이동한다.
  • 다리 건너기 게임을 재시작한다.

BridgeController

  • 다리 건너기 게임을 실행한다.

InputView

  • 다리 길이를 입력한다.
  • 플레이어가 이동할 칸을 입력한다.
  • 게임 재시작/종료 여부를 입력한다.

OutputView

  • 이동 결과를 출력한다.
  • 최종 게임 결과를 출력한다.
  • 메시지를 출력한다.
  • 에러 메시지를 출력한다.

NumericConverter

  • 문자열에서 숫자로 변환한다.

기능 구현

MVC 패턴

이번 요구사항에서 인상적인 부분은 InputViewOutputView가 별도로 주어진 것입니다. 이전까지 입출력에 관한 제약이 없었지만, 입출력과 관련된 클래스를 제공함으로 MVC 구조에 대해 생각해볼 수 있었습니다. 또한 애플리케이션의 핵심 도메인에서 UI 로직을 분리하여 해당 도메인의 역할을 온전히 수행할 수 있게 하는 의도도 담겨 있는 것 같았습니다.

 

MVC 패턴은 Model, View, Controller로 분리하여 알맞은 역할을 수행하도록 하는 패턴입니다. 웹상에서 흐름을 도식화하면 아래와 같은 그림으로 나타낼 수 있습니다.

Model은 앱이 포함할 데이터를 의미합니다. 일반적으로 데이터가 변경되면 뷰에게 알리는데 뷰와 직접적으로 소통하는 것이 아닌 컨트롤러를 통해 데이터를 전달합니다. 데이터를 담고 있기에 DB와 소통이 발생합니다.

 

View는 앱의 데이터를 보여주는 방식을 정의합니다. 보통 유저가 보는 화면을 의미하게 됩니다. 컨트롤러로부터 데이터를 받아 그리는 역할을 수행합니다.

 

Controller는 입력에 대한 응답으로 모델과 뷰를 업데이트하는 로직을 담고 있습니다. 뷰 -> 모델, 모델 -> 뷰로 전달하기 전 가공하는 역할을 수행합니다.

저는 클래스의 역할에 맞게 다음과 같이 배치하였습니다. Controller에서 모든 비즈니스 로직을 처리하기보다는 중간 계층을 담당하는 Service 계층을 만들어 로직을 적절하게 분리해 보았습니다. 이는 기능의 분리로 이어져 객체가 가져야 할 기능에 대해서만 가져갈 수 있었던 것 같습니다.

 

MVC 패턴을 공부하면서 인상 깊었던 내용은 모델과 뷰가 직접적으로 소통하는 일이 없다는 점입니다. 모델과 뷰가 소통하기 위해서는 중간 매개체인 컨트롤러를 거쳐야 합니다. 이런 구조의 장점은 모델과 뷰가 다른 컴포넌트에 종속되지 않아 애플리케이션의 확장성과 유연성이 좋습니다. 중복의 문제도 해결할 수 있고 비즈니스와 UI 로직을 분리하여 유지보수를 독립적으로 수행할 수도 있습니다.

 

코드는 한번 작성되면 끝이 아니라 계속해서 변화되기에 코드를 작성할 때는 필연적으로 유지보수와 확장성에 대해 고려해야 함을 다시 한번 느낄 수 있었습니다.

객체 상태

객체는 상태를 가지기에 존재합니다. 그렇기에 상태가 변해야 하는 경우가 존재합니다. DB를 사용하게 되면 저장하고 조회하는 과정이 존재하기에 각 데이터를 불변으로 사용할 수 있었습니다. 하지만 이번 미션에서는 DB가 별도로 존재하지 않기에 변하는 데이터를 관리하기 위한 객체가 필요했고 조건 없는 불변보다는 변하는 상태를 관리하기 위한 객체를 생성하였습니다.

 

Player는 게임 실행 횟수와 이동에 따른 상태가 지속해서 변화합니다. Player 자체를 변하지 않게 관리함으로 변화하는 상태 속에서도 데이터 일관성을 유지할 수 있게 고민해 보았습니다. 싱글 스레드로 동작한다고 판단했기에 멀티 스레드 상황은 고려하지 않았지만 해당하는 상황까지 고려한다면 수정해야 할 부분이 존재합니다. 이 부분에 대해서는 더 찾아보고 공부해보고자 합니다.

반복적인 테스트

반복적인 테스트 코드를 작성해 보았습니다. 피드백에서 테스트 코드도 코드라는 말을 보았습니다. 단일 상황에 대한 테스트 코드만 작성해 왔는데 @ValueSource, @ParameterizedTest를 사용하면 입력을 매개변수로 받아 반복되게 테스트를 할 수 있음을 알게 되었습니다.

위 코드는 실제 제가 리팩토링한 테스트 코드입니다. 단일 값에 대한 테스트가 아닌 정해진 범위보다 짧은 길이 여러 상황을 소스로 지정해두고 반복적으로 테스트할 수 있도록 하였습니다. 아직 테스트 코드 작성에 있어 미숙한 부분을 깨닫게 되었고 테스트 코드의 가치를 높이기 위한 테스트 코드도 더 알아보고 싶습니다.

메서드는 최대한 작게

메서드를 작게 만들어서 오는 장점을 알 수 있었습니다. 이번 미션에서는 메서드가 10줄을 넘지 않도록 하는 요구사항이 존재했습니다. 이를 지키기 위해 메서드 작성 후 메서드 추출이 가능한 부분에 대해서는 분리를 통해 리팩토링하였습니다. 추출되는 부분은 단일 역할을 수행하게 되고 메서드는 자연스럽게 작게 완성되었습니다. 10줄이라는 제약으로 인해 자연스럽게 메서드를 작게 만들 수 있었고 메서드명에 알맞은 역할만 수행할 수 있었습니다.

 

가장 큰 장점은 가독성도 올라가 예상하지 못한 결과가 발생했을 때 이전과 다르게 빠르게 찾을 수 있었고 단일 기능을 가졌기에 메서드 오류의 전파도 발생하지 않았습니다. 물론 처음부터 메서드를 작게 만드는 것은 어려운 일이라 생각합니다. 온전한 기능을 완성하고 추출을 통해 작게 만들도록 코드를 리팩토링해보고자 합니다.

마치며

4주라는 시간이 짧았다면 짧고 길다면 긴 시간이지만 너무 빠져들어 있었기에 저에게는 짧게만 느껴졌습니다. 저 스스로도 성장할 수 있었던 시간이었지만 함께 미션을 진행하는 다른 분들과 미션이 끝나고 리뷰를 통해 함께 공부한 친구들 덕분에 사고의 전환과 확장도 할 수 있었던 좋은 경험이었습니다.

실제 미션이 끝나고 리뷰 과정에서 위와 같은 말을 들었을 때 지금껏 느껴보지 못한 짜릿함도 느낄 수 있었습니다. 우아한테크코스 본 과정도 얼마나 재밌을지 너무 궁금합니다. 기회가 된다면 꼭 참여하여 함께하는 크루들과 같이 더 재밌게 개발에 몰두해보고 싶습니다. 4주간 좋은 경험 만들어주셔서 감사합니다!

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Total
Today
Yesterday