이 포스트는 Danijel Vincijanovic
의
Level up your React architecture with MVVM
을 보고 공부하면서
Hooks로 유사하게 옮겨본 것입니다.
(제가 공부하면서 이해한 부분을 정리한 것이라서 부족하거나 틀린 정보가 있을 수 있습니다.
말씀해주시면 수정하겠습니다.)
1990년대 마틴 파울러에 의해 나온 MVP패턴에서 파생된 패턴입니다.
Model-View-ViewModel
로 구성되어 있습니다.
자세히 알아보기전 간단히 Model, View, ViewModel에 대해 설명해보자면 다음과 같습니다.
Model은 프로그램에서 사용되는 실제 데이터이며, 불러오거나(Query) 업데이트하는 로직이 있습니다.
두뇌와 같은 역할을 합니다.
View는 사용자가 상호 작용하는 UI 계층으로, 쉽게 말하자면 유저가 보는 화면을 의미합니다.
주로 비지니스 로직을 처리하지 않고, UI에 연관된 로직만 수행합니다.
몇몇 사람들은 UI에 연관된 로직수행과 Input에 대한 부분을 수행하는 것을
View Controller라고 부르기도 합니다.
ViewModel은 모델에 액세스하고 비즈니스 로직을 처리합니다.
이제 React에서 어떻게 구현이 될지 한번 같이 보시죠!
View는 철저히 보여지는 것에 대한 것을 수행합니다.
가령 React의 경우 PC(Presentational Component) + CC(Container Component)에 대한
이해가 있으시다면 PC라고 보시면 좋을 것 같습니다.
const ViewComponent = ({books, handleToggleFavorite, handleClickNeverView}) => {
return (
<div>
<BooksList
books={books}
handleToggleFavorite={handleToggleFavorite}
handleClickNeverView={handleClickNeverView}
/>
</div>
)
}
props를 받아서 주로 화면을 렌더링하는 것들이죠
사실 ViewController라고 할까, View에다가 예시를 들까하다가
여러 아티클들을 보면서 공부의 목적으로 좀 더 명확히 이해하고 싶어서 분리하였습니다.
ViewController는 모든 View에 관련된 로직이 있으며,
ViewModel에 대한 참조를 소유합니다.
View는 ViewModel을 인식하지 못하고, 필요한 데이터 및 이벤트를 전달하기 위해서
ViewController를 의존합니다.
const ViewControllerComponent = ({viewModel}) => {
const [isNeverView, setIsNeverView] = useState(false);
const handleToggleFavorite = useCallback((bookId) => {
viewModel.toggleFavorite(bookId)
}, [viewModel]);
const handleClickNeverView = useCallback(() => {
setIsNeverView(!isNeverView);
}, [isNeverView]);
return (
<ViewComponent
books={viewModel.getBooks()}
handleToggleFavorite={handleToggleFavorite}
handleClickNeverView={handleClickNeverView}
/>
)
}
ViewModel은 일반적으로는 JavaScript 클래스로 구성돠어
UI에 따라 다르게 조정하여 어디서나 쉽게 재사용 할 수 있습니다.
ViewModel에 필요한 모든 종속성은 생성자를 통해 주입되므로 테스트하기가 쉽습니다.
ViewModel은 모델과 직접 상호 작용하며 ViewModel이 모델을 업데이트 할 때마다
모든 변경 사항이 자동으로 View에 반영됩니다.
class ViewModel {
constructor(bookStore) {
this.store = bookStore
}
getBooks() {
return this.store.getBooks()
}
toggleFavorite(bookId) {
this.store.toggleFavorite(bookId)
}
}
export default ViewModel
모델이 데이터 소스 역할을합니다.
응용 프로그램의 글로벌 저장소. 네트워크 계층, 데이터베이스,
서비스의 모든 데이터를 작성하여 쉽게 제공합니다.
실제로 모델을 업데이트하고 부작용이 없는 로직을 제외한 다른 로직은 없어야합니다.
class Model {
constructor() {
books = [
{id: 'RCB-123',name: "React Cook Book", isFavorite: false},
{id: 'VCB-123',name: "Vue Cook Book", isFavorite: false},
{id: 'ACB-123',name: "Angular Cook Book", isFavorite: false}
];
}
getBooks() {
return this.books
}
toggleFavorite(bookId) {
const target = this.books.filter(item => item.id === bookId)[0];
target.isFavorite = !target.isFavorite
}
}
MVVM의 일부가 아니지만 하나의 구성 요소를 사용하여
모든 것을 함께 붙이기 위해 공급자를 사용합니다.
이 구성 요소는 ViewModel을 인스턴스화하고 필요한 모든 종속성을 제공합니다.
또한 ViewModel의 인스턴스는 props를 통해 ViewController 구성 요소로 전달됩니다.
공급자는 모든 것을 연결하는 것이 목적이므로 논리없이 깨끗해야합니다
const BooksProvider = () => {
const model = new Model()
const [viewModel] = useState(new ViewModel(model))
return (
<ViewControllerComponent viewModel={viewModel} />
)
}
이렇게 React에서 MVVM 패턴이 무엇인지 알아보았습니다.
보다 명확한 이해를 위해 원 저자의 사이트(https://medium.cobeisfresh.com/level-up-your-react-architecture-with-mvvm-a471979e3f21)에 가서 한번 더 보시는 것을 추천드립니다.
감사합니다.
axios 같은 라이브러리를 이용한 data fetching은 Model, ViewModel, ViewController, View, Provider 중 어디서 이루어지는게 맞을까요?