[우아한테크코스 7기] 2주차 프리코스 회고

dev_yuni·2024년 10월 30일
7

우아한테크코스

목록 보기
2/4
post-thumbnail

들어가기 전에!

2주차 피드백

이번 2주차 미션은 더욱 설계를 견고하게 하기로 다짐을 하고 시작했습니다.
이전의 설계를 세심하게 하지 않아 구현하며 많이 수정된 부분이 많았고 이로 인해 혼동이 많았습니다. 그래서 이번 회고에서는 설계 과정과 배운 점을 위주로 작성해보려 합니다!

목표는!

  • 객체가 어떤 역할과 책임을 가지고 서로 어떤 협력을 하는지 고민하고 설계하자.
  • 강한 결합을 피하고 변경과 확장에 용이한 구조로 구현하자.
  • MVC 패턴을 사용해보자.
  • 테스트 코드를 짜보자.
  • 완성하자!!

자동차 경주!

기능 요구 사항

초간단 자동차 경주 게임을 구현한다.

  • 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
  • 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
  • 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
  • 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
  • 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.
  • 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
  • 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
  • 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.

실행 결과

경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
시도할 횟수는 몇 회인가요?
5

실행 결과
pobi : -
woni : 
jun : -

pobi : --
woni : -
jun : --

pobi : ---
woni : --
jun : ---

pobi : ----
woni : ---
jun : ----

pobi : -----
woni : ----
jun : -----

최종 우승자 : pobi, jun

이렇게 진행했어요!

관련 서적 읽기

객체 지향의 개념으로 구현을 한다고 했지만 ‘객체 지향이 뭐야?’ 라는 질문을 받았을 때 정확한 개념을 설명하는 것이 어려웠습니다. 그래서 관련 서적을 통해 객체 지향에 대해서 알아보기로 했습니다.

적용해보기

머리로만 익히는 것보다 이번 문제를 통해 실제로 적용시켜 보며 문제를 접근했습니다.

1. 어떤 협력이 필요할까?

  • 사용자가 자동차 이름과 시도 횟수를 입력한다.
  • 자동차는 각자 이름과 현재 위치(상태)를 가진다.
  • 진행자는 여러 번의 시도를 통해 자동차를 이동시킨다.
  • 심판은 각 자동차의 상태를 기반으로 누가 우승했는지 발표한다.

2. 협력을 위한 행동은 무엇일까?

  • 사용자는 게임을 시작하고 입력을 한다.
  • 진행자는 규칙에 맞게 자동차를 이동시킨다.
  • 각 자동차는 규칙에 따라 이동한다.
  • 심판은 자동차들의 상태를 보고 우승자를 판단하고 결과를 반환한다.

3. 행동을 누가, 어떤 객체가 수행할까?

  • 사용자는 "게임을 시작한다"
    • 게임에 필요한 정보를 입력하고 게임을 시작합니다.
  • 진행자는 "게임을 진행한다"
    • 자동차를 이동시키고, 시도 횟수만큼 게임을 진행합니다.
    • 각 라운드마다 자동차들이 이동하도록 요청합니다.
  • 자동차는 "이동한다"
    • 규칙에 따라 이동하며, 상태가 업데이트됩니다.
  • 심판은 "우승자를 뽑는다"
    • 자동차 상태를 확인하고 가장 멀리 이동한 자동차를 우승자로 판단합니다.

이러한 생각을 기반으로 각 객체가 무엇을 해야 하는지(Doing)와 무엇을 알아야 하는지(Knowing)를 나누어 정리해봤습니다.

필요한 객체

입력 처리 객체

Knowing: 입력 값 ( 시도횟수, 이름 )

Doing: 사용자의 입력을 받고, 적절한 형식으로 반환하는 역할

입력 검증 객체

Knowing: 입력 값

Doing: 사용자의 입력이 유효한지 확인하는 역할

게임 흐름 관리 객체

Knowing: 게임의 상태

Doing: 전체 흐름을 관리하는 역할

자동차

Knowing: 자신의 이름, 자신의 상태

Doing: 이동하는 역할

자동차 상태를 가지는 객체

Knowing: 각 자동차의 이름과 상태

Doing: 자동차의 현재 상태를 관리하는 역할 (현재 위치와 이름)

이동 결과를 가지는 객체

Knowing: 자동차의 이동 과정

Doing: 각 자동차의 이동 결과를 기록하는 역할

이동 규칙을 가지는 객체 (전략패턴)

Knowing: 이동을 위한 규칙

Doing: 규칙을 관리하는 역할

이동을 지시하는 객체

Knowing: 규칙, 자동차들

Doing: 규칙에 맞게 자동차에게 이동을 명령하는 역할

심판

Knowing: 최종 이동 결과

Doing: 우승자를 판단하는 역할

결과 출력 객체

Knowing: 우승자와 실행 결과

Doing: 우승자와 실행과정을 출력하는 역할

그래서 제가 생각한 입력 규칙은 아래와 같습니다!

[이름]

  • 5자 이하여야 합니다. ex) pobii (x)
  • 영어로만 이루어져야 합니다. ex) 우테코, pob2 (x)
  • 중복된 이름은 사용할 수 없습니다. ex) woni, woni (x)
  • 이름은 쉼표로 구분됩니다.

[시도 횟수]

  • 숫자만 입력할 수 있습니다. ex) aa (x)
  • 1부터 10까지만 입력할 수 있습니다. ex) 11 (x)

구현

아래는 PR 링크입니다! 아직 부족한 부분이 많지만 리뷰는 언제나 환영입니다 🙇🏻‍♀️

코드 보러가기 👀

흐름도

알게됐어요!!!

MVC 패턴

출처:MVC 패턴

MVC 소프트웨어 디자인 패턴의 세 가지 부분은 다음과 같이 설명할 수 있습니다.

  • 모델: 데이터와 비즈니스 로직을 관리합니다.
  • 뷰: 레이아웃과 화면을 처리합니다.
  • 컨트롤러: 모델과 뷰로 명령을 전달합니다.

블로그의 게시물을 예시로 들어보면,

  1. 사용자가 제목과 본문을 입력하고 "작성하기" 버튼을 클릭합니다.
  2. 컨트롤러(Controller)는 입력한 정보를 받아 "데이터베이스에 저장해"하고 모델에게 전달합니다.
  3. 모델(Model)은 전달 받은 게시글 데이터를 데이터베이스에 저장한 후, 컨트롤러에 알려줍니다.
  4. 컨트롤러에서는 게시글 작성이 완료된 것을 확인하고, 사용자에게 게시글 목록으로 이동하게 지시합니다.
  5. 뷰(View)는 게시글 목록 데이터를 화면에 표시하고, 자신이 작성한 게시글을 확인할 수 있게 합니다.

이렇게 각각의 요소가 자신만의 역할을 수행하기 때문에, 특정 부분을 수정하거나 업데이트해야 할 때 관련된 역할에만 집중하면 됩니다. 이를 통해 코드의 유지보수가 쉬워지고, 역할이 명확히 나뉘어 더 체계적인 설계를 할 수 있습니다.

MVC 패턴을 이해할 때 도움이 된 블로그입니다! - MVC 패턴이 뭐야

전략 패턴

전략 패턴은 알고리즘들의 패밀리를 정의하고, 각 패밀리를 별도의 클래스에 넣은 후 그들의 객체들을 상호교환할 수 있도록 하는 행동 디자인 패턴입니다.

public interface MoveStrategy {
    boolean canMove();
}


public class RandomMoveStrategy implements MoveStrategy {

    private static final int MIN = 0;
    private static final int MAX = 9;
    private static final int MOVE_THRESHOLD = 4;

    @Override
    public boolean canMove() {
        int randomValue = Randoms.pickNumberInRange(MIN,MAX);

        return randomValue >= MOVE_THRESHOLD;
    }

}

저는 자동차의 이동 규칙을 유연하게 적용할 수 있도록 MoveStrategy 인터페이스를 통해 이동 여부를 결정하는 로직을 추상화했습니다. 이 방식은 추후 새로운 이동 규칙이 필요하거나 기존 규칙을 변경할 때 다른 코드에 영향을 주지 않으므로 유지보수가 용이하다는 장점이 있습니다. 또한, 전략 패턴 덕분에 자동차 외의 다른 객체가 이동 규칙을 필요로 할 때도 같은 구조를 재사용할 수 있습니다.

고민했어요!

입력 값 검증은 누가?

입력값을 검증하는 클래스를 따로 분리할지, 객체가 직접 검증하도록 할지 고민했습니다.
검증 클래스를 분리하면 검증 로직을 재사용하기 쉬워지고, 코드가 깔끔해지며 단일 책임 원칙을 충족하는 장점이 있습니다.
반면 객체가 직접 검증하게 되면, 해당 객체가 스스로 데이터의 일관성을 관리하므로 결합도가 낮아지고 유지보수에 유리할 수 있습니다.
단일 책임 원칙을 지키기 위해서 저는 입력값 검증 클래스를 분리해서 사용하기로 결정하였습니다.

시도횟수는 문자열로? 숫자로?

시도 횟수를 검증할 때, 문자열로 받아 숫자로 변환할지, 숫자만 받도록 할지 고민했습니다.
최종적으로 문자열로 받는 방식을 선택했습니다. int로 받을 경우 타입이 고정되어 안정적이지만, 문자열 입력 시 예외 처리가 필요합니다. String으로 받을 경우 변환 과정이 추가되지만, 다양한 형식으로 입력한 값을 더 쉽게 처리할 수 있습니다.
저는 예외를 처리하는 과정이 가독성을 해칠 수 있다고 생각하여 문자열로 받는 것이 더 효과적일 것이라 판단했습니다.

테스트 코드는 어디까지?

테스트 코드 작성 시, 어느 범위까지 테스트를 수행해야 할지 고민했습니다.
모든 객체를 개별적으로 테스트해야 할지, 주요 로직과 상호작용을 중심으로 테스트를 구성해야 할지에 대한 고민이었습니다.
초기에는 각 객체의 메서드 단위로 테스트하는 것이 좋다고 생각했지만, 점차 객체 간의 협력이 중요한 만큼 주요 기능이 잘 작동하는지를 중심으로 통합 테스트를 강화하는 방향으로 방향을 잡았습니다. 이를 통해 단위 테스트와 통합 테스트를 적절히 조화시켜야 한다고 느꼈습니다.

마지막으로!

이번 미션을 하면서 아쉬웠던 점은 객체들이 자신의 역할을 가지면서 협력하는 것을 목표로 하였지만 설계한대로 코드가 구현되었는 지에 대한 확신이 들지 않았습니다. 너무 역할과 책임, 협력에 초점을 둔 것이 아닐까하는 생각도 들었습니다.
이전 리뷰에서 '일급 컬렉션','불변 객체'에 대해 피드백을 받아 적용시켜보려 노력했지만, 아직 완전히 이해하지 못해서 부족한 부분도 있는 것 같습니다.
하지만 이러한 경험이 많은 것들을 배울 수 있는 좋은 기회였다고 생각합니다.
다음 미션에서는 위의 개념들을 학습하고 아쉬웠던 부분을 보완할 수 있도록 노력할 것입니다!
긴 글 읽어주셔서 감사합니다 😊


참고

profile
꾸준히 성장하는 백엔드 개발자

2개의 댓글

comment-user-thumbnail
2024년 10월 31일

굉장히 많은 내용을 고민하고 적용하고자 하셨네요!
책까지 읽으면서 진지하게 고민하시는 부분이 인상깊었습니다.

흐름도도 잘 만들어주셔서 좋은 것 같아요!
mermaid를 활용하면 README에도 적용할 수 있을 것 같습니다.

남은 프리코스도 화이팅해서 꼭 우테코에서 뵈면 좋겠네요!
고생하셨습니다!

+) 감자 너무 귀엽네요.. ㅋㅋㅋㅋㅋ 🤣

1개의 답글