철학
- 요구사항이 추가되거나 서비스가 확장되었을때 유지 보수가 쉽도록 한다.
- 각각의 영역이 서로 무엇을 하는지 모르는 상태여야 어느 부분을 고치거나 추가해야할지 빠르게 파악할 수 있다.
구조

Controller
- 정의 : Model과 View, View와 View 사이의 관계를 맺어준다, Model의 상태를 View에게 제공한다.
Controller가 비대해지면 계층을 하나 더 두거나 util함수로 빼야할것으로 예상한다.
-> 결국은 똑같은데 파일만 나누는 행위라고 생각하여 나누지 않는다.
서버로부터 받은 데이터를 View에 전달한다.
등록한 이벤트에 대한 핸들하는 로직을 처리한다.
Controller에서 Model의 상태를 set하고 그에 해당하는 상태를 View에게 전달한다.
코드를 통하여 Controller에 대하여 간략하게 알아본다.
Controller는 Model(store)이나 View(searchBar)에 대한 코드가 있습니다. Model과 View에 대한 작업을 통제해야하기 때문이다.
const controller = {
init({
store,
category,
categoriesDropBox,
inputDropBox,
searchBar,
searchBarDropBox,
}) {
this.store = store;
this.searchBar = searchBar;
onKeyupKeywords({ this.handleKeyupKeywords });
onFocusInput({ this.handleFocusInput });
onChangeInput({ this.handleChangeInput })
...
handleChangeInput() {
const keyword = 'text';
this.store.setState({ ...this.store.state, keyword });
this.searchBar.render({ keyword });
}
...
};
Model
- 정의 : 클라이언트의 상태를 저장하는 보관소
클라이언트의 view를 바꾸기 위하여 필요한 상태들의 보관소
Model을 통하여 상태를 set할 수 있다.
서버로부터 받아온 데이터는 어디서 관리할 것인가? 서버로부터 받아온 데이터는 서버의 상태이기 때문에 그 자체로 클라이언트 요청에 따라서 변경될 일이 없다. 따라서 Controller가 서버로부터 받아서(util함수를 호출하여) View에 전달하고자 한다.
클라이언트에서 사용하는 데이터는 상태인가? UI에 사용되는 데이터는 사용자 입력에 따라 바뀔 필요가 없어서 상태가 아니다.
코드를 보면서 Model에 대하여 감을 잡아보자.
Model은 Controller의 요청에 의해 상태를 저장하고 전달하는 역할만 해야하기 때문에 상태에 대한 코드만 존재한다.
class Store {
constructor() {
this.state = {
isBarDropBoxVisible: false,
keyword: "",
keyupKeyword: [],
selectedCategory: "전체",
};
}
setState(newState) {
this.state = newState;
}
}
export default Store;
View
- 정의 : 화면과 관련된 처리를 한다. (Model과 연관이 없고 Model에서 받은 데이터를 이용한다)
화면에서는 어떤 일이 일어나는가?
1. 사용자의 이벤트가 발생한다.
- 사용자의 이벤트 발생등록은 화면에 해당
- 이벤트 처리는 뷰에 해당하는가? 이벤트에 대한 처리는 Model과 View의 관계, View와 View의 관계가 필요할것이라 예상하여 Controller에서 관리하는게 더 적절하다.
2. 화면에 보여질 UI를 생성하여 DOM에 추가한다.
- 템플레이팅하여 DOM에 append한다
3. 화면에 보여지는 스타일을 입힌다.
- css스타일을 적용한다.
View는 여러개 일 수 있을까? View를 여러개일수록 나중에 재활용할 수 있다고 생각한다. 템플레이팅, 스타일 입히는것도 재각각이므로 따로 해주고 이 각각을 필요시 덧붙여 사용하는것이 재활용하기 좋다고 생각한다.
코드를 통하야 View에 대하여 살펴보겠다.
View에서는 다른 View나 Model에 대한 코드가 아예 없다. 다른 View의 객체나 state가 아예 존재하지 않는것이다. 단, 유일하게 상태에 따라서 변화가 있는 render함수에서만 주입받는다.
import { targetQuerySelector } from "../util/util.js";
class SearchBar {
constructor() {
this.$search = targetQuerySelector({
className: "search",
});
}
onFocusInput({ handleFocusInput }) {
this.$search.addEventListener("focus", (event) => {
handleFocusInput();
});
}
onChangeInput({ handleChangeInput }) {
this.$search.addEventListener("keyup", (event) => {
handleChangeInput();
}
}
onKeyupKeywords({ handleKeyupKeywords }) {
this.$search.addEventListener("keyup", (event) => {
handleKeyupKeywords();
}
}
render({ keyword }) {
this.$search.value = keyword;
}
}
export default SearchBar;
개선할 사항
Controller에서 View를 다 알고있는데 View의 메소드를 하나하나 다 수정하는 과정에서 어떤 View를 수정해야할지 헷갈리고 복잡하다고 생각이 들었다. View의 인터페이스만 알면 view와 viewModel간의 의존성도 제거할 필요성을 느꼈다. 시도해보기 전에 간단한 예제로 실험하였는데 다소 의존성이 떨어진것 같지만 의존성이 제거되었다고 할 수 있는지 의문이 들었다.
class ViewOne {
constructor() {}
render() {
console.log("view one");
}
}
export default ViewOne;
class ViewTwo {
constructor() {}
render() {
console.log("view two");
}
}
export default ViewTwo;
class ViewInterface {
constructor() {
this.view = null;
}
setView(view) {
this.view = view;
}
renderView() {
this.view.render();
}
}
export default ViewInterface;
import ViewOne from "./View/ViewOne.js";
import ViewTwo from "./View/ViewTwo.js";
import ViewInterface from "./View/ViewInterface.js";
const viewOne = new ViewOne();
const viewTwo = new ViewTwo();
const viewInterface = new ViewInterface();
viewInterface.setView(viewOne);
viewInterface.renderView();
viewInterface.setView(viewTwo);
viewInterface.renderView();
이런식으로 하여도 setView하는 부분에 해당 객체를 알아야 set하기 때문에 의존성이 제거 되었다고 할 수 있을까?
-> viewOne과 viewTwo를 알고 있다고 하더라도 해당 객체를 set하거나 특정 메소드를 실행시키는 것이 아니고 대신에 인터페이스를 set하고 인터페이스의 특정 메소드(renderView)를 실행하기 때문에 의존성이 없다고 생각한다. 즉, set을 하거나 특정 메소드를 사용하는것이 아니라 get하는 것이라면 의존성이 상당히 적어졌다고 생각한다. 그렇다면 인터페이스와 viewOne, viewTwo도 의존성이 생긴것 같은데 의존성이라는것이 완전하게 제거되기는 힘든것일까?
추가적으로, 상단 viewOne, ViewTwo의 부모 viewComponent가 있고 인터페이스에서 viewComponent를 받는다면 viewComponent의 자식은 모두 render를 가지는 것을 보장하기 때문에 인터페이스에서 render함수를 실행할때 render함수를 지녔다는것을 확신할 수 있을것이다. 물론, 자바스크립트에서 viewComponent타입만 들어오도록 강제할 수는 없겠지만 확신은 할 수 있을것이다.
느낀점
다른 팀원들과 다른 블로그들에서 MV*패턴에 대하여 각자 세부적으로 다른점이 있다는것을 알았다. 그것은 Model이 무엇인지, Controller의 역할이 무엇인지 정의하고 생각한 논리 흐름이 조금씩 다르기 때문이라고 생각한다. 여기서 중요한것은 '다르다'는 것이 아니라 '어떻게 정의하고 어떤 논리대로 생각했는지'라고 생각한다. 그래서 나의 기준으로 요소를 정의하고 구현한것을 정리하고 개선해나갈 수 있었다.
MVC패턴 외의 다른 패턴들을 추가 작성할 예정이다.
'코드스쿼드' 카테고리의 다른 글
코드스쿼드 FE 4주차 회고(+ 프로젝트 설계) (0) | 2022.03.11 |
---|---|
카카오페이지 데이터 통신 (FE 3주차) (0) | 2022.03.01 |
코드 스쿼드 CS 회고 (0) | 2022.02.11 |
댓글