[우아한테크코스 #3] UI와 도메인 로직의 분리, 그리고 모델링

NinjaJuunzzi·2022년 2월 27일
3

우아한테크코스

목록 보기
5/21
post-thumbnail
post-custom-banner

기존의 모델링

페어와 함께 생각해본 설계도를 도식화해보면 위와 같습니다. 여기에서의 각 Entity는 다음과 같은 역할을 합니다

기존의 모델링 - Entity들의 역할

LottoGameManager : 게임을 시작하고, DomainView 를 셋팅하는 역할을 수행합니다. 도메인의 메소드를 직접 호출하여 데이터를 변형하고, 뷰의 렌더링 메소드를 직접 호출하여 화면을 그립니다

LottoGameView : 새로운 도메인과 함께 화면을 그려냅니다. Domain과 분리되어 있으나, render method 들이 Domain 형식에 종속적입니다.

LottoGameModel : 게임에 사용되는 도메인을 관리합니다. 데이터를 변형하는 메소드를 가지고 있습니다.

기존의 모델링의 문제점

LottoGameManager가 도메인과 뷰의 메소드를 직접 호출하고 있습니다.


// ... LottoGameManager Class

  onSubmitChargeInputForm = (e) => {
    e.preventDefault();
    try {
      const { value: chargeInputStr } = this.$chargeInput;
      const chargeInput = Number(chargeInputStr);
      this.triggerChargeInputAction(chargeInput);
    } catch ({ message }) {
      alert(message);
    }
  };

  triggerChargeInputAction(chargeInput) {
    //  직접 도메인의 메소드를 호출하여 변경합니다.
    this.lottoGameModel.createLottoList(chargeInput);
    const lottoList = this.lottoGameModel.getLottoList();
    
    // 직접 뷰의 메소드를 호출하여 변경합니다.
    this.lottoGameView.renderLottoSection(lottoList);
  }

이 경우 다음과 같은 문제점들이 발생할 수 있습니다.

1. 뷰나 도메인의 메소드 명 또는 인자의 수가 변경되는 경우 LottoGameManager에서도 수정해주어야합니다.

개인적으로 변경하는 곳이 많아질수록 파급효과가 커진다고 생각합니다. (휴먼에러 등)

2. 도메인과 뷰는 자신이 할 일을 스스로 하지 못하는 애송이가 됩니다.

도메인과 뷰에 어떤 일을 해달라는 요청이오면, 그에 맞게 스스로 할일을 수행해야하는데 지금의 코드에선 할일을 다른 클래스가 지시하고 수행시킵니다. ( 객체자체가 능동적이지 못하고, 독립적이라 할 수 없습니다.)

그래서 변경하는 이유는 ?

사진에서 보이듯 이번 프로젝트의 목표는 UI와 도메인 영역을 분리하는 것입니다. 도메인과 UI의 분리는 설계 및 구현에서 제대로 수행되었다고 생각되지만, 그들이 각개의 모듈인가를 생각해보면 아닌 것 같습니다. 그렇기 때문에 각 Entity들이 좀 더 확실한 모듈이 되도록 다시 설계해보았습니다.

도메인과 뷰를 사용하는 입장에서 (LottoGameManager) 좀 더 선언적으로 도메인과 뷰를 동작하게 하고싶습니다. (지금 처럼 모든 일을 하나하나 찝어주며 시키는게 아니라, 어떤 일을 하세요! 라고 요청하면 그들이 알아서 일을 하도록)

새로운 모델링

1.DomainUI가 좀 더 능동적으로 동작했으면
2.Game Manager에서 선언적인 코드 작성이 가능해졌으면

위 두 아이디어에 집중하여 설계를 다시 해보았습니다.

새로운 모델링 - 변경된 Entity들의 역할

Managers

매니저들은 업무를 지시하거나, 수행하는 역할을 합니다.

LottoGameManager : DomainManager - ViewManager 에게 새로운 데이터로 할 일을 전달합니다. 기존의 기능 (DOM select, Domain, View가 할일을 모두 수행시키는 기능은 하지않습니다)

DomainManager : Lotto Game에서 요구되는 도메인을 관리합니다. LottoGameManager 로 부터 할일과 데이터(사용자가 입력하는)를 전달받으면, 그에 맞게 도메인 데이터를 변형하는 업무를 수행합니다.

ViewManager : Lotto Game의 모든 뷰들을 관리합니다. 마찬가지로 LottoGameManager 로 부터 할일과 데이터를 전달받으면 그에 맞게 화면에 그리는 업무를 세부 뷰들에게 지시합니다.

Domains

도메인 데이터를 가지고 있으며, 독립적으로 변형할 수 있는 능력을 갖추었습니다.

Lotto : 로또 도메인 입니다. lottoNumbers(숫자 6개의 배열)라는 멤버를 가지고 있습니다.

Views

LottoContainerView : 다음 사진의 UI와 관련된 업무를 수행합니다.

LottoResultView : 다음 사진의 UI와 관련된 업무를 수행합니다.

결과적으로 다음과 같은 코드가 되길 기대합니다

// LottoGameManager Class

  triggerChargeInputAction(chargeInput) {
    // 도메인에게 새로운 데이터와 할 일에 대한 정보를 가진 액션 키를 넘깁니다.
    const lottoList = this.#lottoDomainManager.mutateDomainState({
      newData: chargeInput,
      actionKey: MUTATE_DOMAIN_KEY.NEW_CHARGE_INPUT,
    });

    // 뷰에게 새로운 데이터와 할 일에 대한 정보를 가진 액션 키를 넘깁니다.
    this.#lottoViewManager.renderView({
      newData: lottoList,
      actionKey: RENDER_VIEW_KEY.UPDATE_LOTTO_LIST,
    });
  }

위 와 같이 매니저에서 할 일에 대해 알려주면, 도메인은 다음과 같이 일합니다

// LottoDomainManager Class

 mutateDomainState({ newData, actionKey }) {
    if (actionKey === MUTATE_DOMAIN_KEY.NEW_CHARGE_INPUT) {
      // 새로운 데이터와 함께 할 일을 수행합니다.
      this.#createLottoList(newData);
      return this.lottoList;
    }
  }

도메인에서 업데이트 된 새로운 데이터와 함께 할 일 정보가 넘어오면, 뷰는 다음과 같이 일합니다.


// LottoViewManager Class

renderView({ newData, actionKey }) {
    if (actionKey === RENDER_VIEW_KEY.UPDATE_LOTTO_LIST) {
      // 새로운 데이터와 함께 각 뷰들이 할 일을 명령합니다. (할 일을 수행하는 단계)
      this.#containerView.renderLottoSection(newData);
      this.#resultView.showWinNumberInputSection();
    }
    if (actionKey === RENDER_VIEW_KEY.UPDATE_VISIBLE_STATE) {
      this.#containerView.renderAlignState(newData);
    }
  }
profile
Frontend Ninja
post-custom-banner

0개의 댓글