시작하면서
지난 주 개선 사항은 다음과 같다.
TDD와 BDD를 상호보완적으로 사용하기- 단위테스트를 억지로 BDD로 작성하려 하지 않고, 기능 개발 시 TDD로 개발하다가 예외 처리와 전체 기능 테스트에서 BDD를 사용했다.
- 제어 역전을 구현할 수 있는 다른 패턴들 공부하기
MVC, MVP, MVVM, MVI, FLUX 등 다시 공부. 중간 객체를 어떻게 잘 활용할 수 있을지 생각해보고, 각 모듈의 역할에 대해서 고민하기- MVC, MVP, MVVM, MVI의 흐름을 직접 그려보면서 익혔다. FLUX와 REDUX는 MVI에서 파생된 개념이라 따로 공부하면 좋을 것 같다.
- 또한 2주차보다 3주차 미션의 구현 사항이 조금 더 복잡해짐에 따라, 자연스럽게 중간 객체의 필요성을 느끼게 되었다. 컨트롤러에서 입력 단위에 따라 로직을 나누어 모델에 대한 업데이트를 할 수 있도록 했다.
여러 예외 케이스를 생각해보고 정규식 검증하기옵저버 패턴 이용하기
옵저버 패턴에 집중하느라 제어 역전을 구현할 수 있는 다른 패턴들에 대한 공부를 하지 못한 것이 아쉽다. 4주차에는 꼭 진행해보자!!
배경
지난 주 학습에서, MVC 패턴에서는 모델이 뷰에게 데이터의 변경을 알리는 역할이 있다는 것을 배웠다. 그런데 이렇게 데이터의 변경을 알리면서도, 모델과 뷰 사이를 느슨하게 만들 수 있는 방법은 없을까? 그 방법이 바로 옵저버 패턴이다.
옵저버 패턴?
이벤트가 발생할 때마다 Observable은 모든 Observer에게 이벤트를 전파한다.
Observer
구독하는 주체
Observable
구독 가능한 객체
- observers - 이벤트가 발생할 때마다 전파할 observer 배열
- subscribe() - Observer 를 observers 에 추가
- unsubscribe() - Observer 를 observers 에서 제거
- notify() - 등록된 모든 Observer 들에게 이벤트 전파
MVC에서 옵저버 패턴이 어떻게 사용될 수 있을까?
MVC로 알아보는 로또 생성 과정
1. 사용자가 금액을 입력
2. View가 Controller에 입력 전달
3. Controller가 input을 LottoStore Model에 전달 (buyLotto)
4. LottoStore Model이 View에게 데이터 변경을 알림 → 둘 간의 의존성을 낮추는 방법으로, 옵저버 패턴을 사용한다.
5. View가 구입한 로또 정보 렌더링
View, Model의 관계
여기서 View는 Model의 업데이트를 기다리는 입장으로, Model에서 변경 사항이 생기면 해당 Model을 사용하는 View에서 모두 업데이트가 일어나야 한다. 즉 View는 Observer로, Model은 Observable로 구현할 수 있다.
옵저버 패턴 적용 전
모델을 업데이트 한 뒤, 컨트롤러에서 뷰에 직접 모델의 데이터를 넘겨주는 식으로 구현하였다.
async buyLotto() {
try {
const money = await this.views.input.getLottoMoney();
this.models.lottoStore.buyLotto(money);
this.views.output.printLottoCount(this.models.lottoStore.getLottoCount());
this.views.output.printLottos(this.models.lottoStore.getLottos());
} catch (error) {
this.views.output.printError(error.message);
await this.buyLotto();
}
}
옵저버 패턴 적용 후
옵저버 패턴으로 구현하면서, 컨트롤러에서는 모델에 대한 업데이트만 진행하고, 모델에서 뷰에게 간접적으로 데이터를 넘겨줄 수 있게 되었다. 구현 과정을 살펴보자.
async buyLotto() {
try {
const money = await this.views.input.getLottoMoney();
this.models.lottoStore.buyLotto(money);
} catch (error) {
this.views.output.printError(error.message);
await this.buyLotto();
}
}
MVC에 옵저버 패턴 적용하기
1. View가 Model을 구독하기
옵저버블인 모델에, 자신을 감시할 뷰들을 등록한다.
이후 lottoStore 모델은 Output 뷰의 updateLotto를 옵저버로 갖게 된다.
class Controller {
views;
models;
constructor(views, models) {
this.views = views;
this.models = models;
this.models.lottoStore.subscribe(this.views.output.updateLotto);
}
/* ... */
}
2. Model이 View에게 데이터 변경 알리기
모델(옵저버블)이 뷰(옵저버)에게 데이터의 변경을 알린다.
이때 옵저버 함수에 데이터를 전달할 수 있는데, lottoCount와 lottoList가 그것이다.
class LottoStore extends ObservableModel {
buyLotto(money) {
this.#money = this.#validate(money);
this.#lottoCount = Math.floor(money / this.LOTTO_PRICE);
this.#lottoList = new Array(this.#lottoCount).fill(0).map(LottoStore.#createLotto);
this.notify({
lottoCount: this.#lottoCount,
lottoList: this.#lottoList,
});
}
/* ... */
}
3. View의 옵저버 함수 실행
옵저버로 등록되었던 updateLotto가 전달받은 데이터와 함께 실행된다.
const OutputView = {
updateLotto({ lottoCount, lottoList }) {
OutputView.printLottoCount(lottoCount);
OutputView.printLottos(lottoList);
},
/* ... */
}
그 외 새로 배운 내용
전체 회고
잘한 부분
- MVC 패턴에 대해 다시 공부하고, 옵저버 패턴을 적용하는 시도를 해본 것.
- 컨트롤러에 지난주에 학습한 DI 패턴으로 모델과 뷰를 전달해준 것.
아쉬운 부분 / 남은 궁금증
- 테스트 유틸을 관리할 방법이 필요성을 느꼈으나, 아직 해결 방법을 적용하지 못해 여전히 중복된 코드가 존재한다.
- 제어 역전을 구현할 수 있는 다른 패턴들 공부하기 (지난주 개선사항 적용 X)
- MVVM, MVI 패턴을 개념적으로는 공부하였으나 와닿지 않음
어떻게 개선할 것인가?
- 테스트 유틸 관리방법 알아보기
- 제어 역전을 구현할 수 있는 다른 패턴들 공부하기
- 다음 미션은 MVVM or MVI 패턴으로 구현해보기
'프로젝트 > 우테코 프리코스' 카테고리의 다른 글
[우테코 프리코스 2주차 회고] 넌 전혀 MVC 하고 있지 않아 (1) | 2024.10.30 |
---|---|
[우테코 프리코스 1주차 회고] 첫 TDD는 계획대로 되지 않아 (2) | 2024.10.21 |