티스토리 뷰
어떻게 Model과 api(데이터)를 효과적으로 연결할까? (MVVM 패턴)
기술 과제(React + TypeScript)에서 MVVM 패턴을 적용해야했습니다.
MVVM 패턴을 처음 적용하다 보니 크게 감이 잡히지 않아 여러 문서를 보고 따라하는 식으로 처음 진행을 했는데 MVVM패턴을 적용해 나갔습니다.
그러나 Directory구조 잡기, 데이터 분리, Model과 데이터를 연결등 많은 문제들 중 Model과 데이터를 어떻게 효과적으로 연결하지? 라는 부분을 나누려고 합니다.
우선 내용에 들어가기 앞서 MVVM패턴에 대해 간략하게 알아보도록 합시다.
👣 설명
MVVM(Model - View - ViewModel) 패턴
View: 사용자가 화면에서 보는 것들에 대한 구조, 배치, 그리고 외관
ViewModel: View에 대한 추상화로 Model에 데이터를 요청하고 가공하여 View에 보내주는 역할
Model: 데이터를 관리하는 부분 (서버와 같은 역할 / 비지니스 로직)
Model–view-viewmodel (MVVM) is an architectural pattern in computer software that facilitates the separation of the development of the graphical user interface (GUI; the view)—be it via a markup language or GUI code—from the development of the business logic or back-end logic (the model) such that the view is not dependent upon any specific model platform.
-Wikipedia-
다음과 같이 위키피디아에서 정의를 합니다. 추가로 사용자 인터페이스의 이벤트-기반 프로그래밍을 단순화하기 위해 발명되었습니다.
간략하게 이야기를 하면 View, ViewModel, Model로 분리하여 객체를 관리하고 표현하기 쉽게 만드는 아키텍처 패턴이라고 보면 됩니다.
MVVM 형태
간단하게 유로(Euro) 정보를 보여주는 코드를 MVVM패턴으로 관리하는 것을 예로 들어보겠습니다.
(Directory 구조는 한눈에 파악하기 쉽게 MVVM과 같은 네이밍을 사용하였습니다.)
📦src
┣ 📂models
┃ ┗ 📜EurModel.ts
┣ 📂viewModels
┃ ┗ 📜EurViewModel.ts
┣ 📂views
┃ ┣ 📂EurInfosView
┃ ┃ ┣ 📜EurInfos.style.ts
┃ ┃ ┗ 📜EurInfos.tsx
┃ ┣ 📂EurSectionView
┃ ┃ ┣ 📜EurSection.style.ts
┃ ┃ ┗ 📜EurSection.tsx
┃ ┗ 📜index.ts
┣ 📜App.tsx
┗ 📜index.tsx
각 파일 별로 보도록 하겠습니다.
Model(EurModel
) - ViewModel(EurViewModel
) - View(EurSection
|| EurInfos
)
Model
: 데이터를 관리하는 부분 (서버와 같은 역할 / 비지니스 로직)
데이터(EurInfos
)를 연결하고 가공
import { EurInfosTypes } from '../interfaces/eur';
class EurModel {
EurInfos: EurInfosTypes[];
constructor(EurInfos: EurInfosTypes[]) {
this.EurInfos = EurInfos;
}
async getEurInfos() {
//Euro 데이터 가져오기 (EurInfos의 0번에 저장되어 있다.)
return this.EurInfos[0];
}
}
export default EurModel;
ViewModel
: View에 대한 추상화로 Model에 데이터를 요청하고 가공하여 View에 보내주는 역할
Model(EurModel
)을 받아와서 연결
import EurModel from '../models/EurModel';
class EurViewModel {
model: EurModel;
constructor(model: EurModel) {
this.model = model;
}
async getEurInfos() {
return await this.model.getEurInfos();
}
}
export default EurViewModel;
View
: 사용자가 화면에서 보는 것들에 대한 구조, 배치, 그리고 외관
매개변수로 ViewModel(EurViewModel
)을 넘겨주어서 ViewModel(EurViewModel
)내부에 있는 함수(getEurInfos()
)를 사용
function EurSection({ props }: { props: EurViewModel }) {
const [EurInfo, setEurInfo] = useState<EurInfosTypes>();
useEffect(() => {
const eurInfosToViewModel = props.model.getEurInfos();
setEurInfo(eurInfosToViewModel);
}, []);
if (!EurInfo) return null;
return (
<div>
<EurInfos EurInfos={EurInfo} />
</div>
);
}
export default EurSection;
Model - ViewModel 연결 (중요 부분)
Model(EurModel
)와 ViewModel(EurViewModel
)을 연결하여 View에서 ViewModel(EurViewModel
)을 사용할 수 있도록 한다.
=> Model - ViewModel을 연결하며 Model에 데이터를 줘야 합니다.
Model과 ViewModel을 연결하여 ViewModel을 View(EurSection
)에 props로 전달해주기 위해서는 다음과 같이 함수 내부 전역에서 동작할 수 있도록 작성해야 합니다. 또한 데이터(api)를 불러오는 부분을 Model 내부에서 받아오게 되면 class
의 constructor
에서 async/await
를 사용하여 데이터를 받아오기 쉽지 않으며 받아와도 함수 내부 전역에서 동작하는 것과 같습니다.
export const App = () => {
const EurInfos = await getEurInfosApi();
const EurModel = new EurModel(EurInfos);
const EurViewModel = new EurViewModel(EurModel);
return (
<div className='App'>
<EurSection props={EurViewModel} />
</div>
);
};
export default App;
👣 결과/해결
다음과 같이 data를 useLayoutEffect()
를 사용해서 처음 한번만 불러오게 만들었고useState
에 ViewModel(EurViewModel
)을 저장해서 보내주는 방식으로 문제를 해결하였습니다.
export const App = () => {
const [EurViewModel, setEurViewModel] = useState<EurViewModel>();
const setModel = async () => {
const EurInfos = await getEurInfosApi();
const EurModel = new EurModel(EurInfos);
setEurViewModel(new EurViewModel(EurModel));
};
useLayoutEffect(() => {
setModel();
}, []);
if (!EurViewModel) return null;
return (
<div className='App'>
<EurSection props={EurViewModel} />
</div>
);
};
export default App;
👣 마무리
MVVM 패턴을 적용시켜보니 로직을 확실하게 분리할 수 있는 장점이 있다는 것을 알게 되었습니다.
그러나 기능이 많은 어플리케이션을 개발하게 될 경우 Model과 ViewModel을 하나로 통일을 하게 되면 내부 함수양이 엄청날 것 같았고 Model과 ViewModel을 많이 만들게 되면 Model과 ViewModel을 연결하는 것도 많은 비용을 필요로 할 것같다는 생각이 들어 Model과 ViewModel을 초기 구성이 까다로울 것 같았습니다.
추가로 react에서 mvvm을 적용할 때 npm을 찾아보니 react-mvvm이라는 library도 있어 사용해보면 더 빠르게 이해할 수 있을 것 같습니다.
아직까지는 container 패턴으로 가볍게 분리하는 것이 좋은 것 같지만 패턴은 생각보다 자유롭게 만들 수 있어 얽매이지 않고 넓게 생각해서 코드 틀을 잡아야 겠습니다.(협업에서의 패턴 사용하는 것 보고싶다....)
패턴을 적용한 부분들이 많이 부족하여 글을 읽고 문제가 있는 부분들을 알려주는 것을 환영합니다. :)
👣 REF
https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%8D%B8-%EB%B7%B0-%EB%B7%B0%EB%AA%A8%EB%8D%B8
https://medium.cobeisfresh.com/level-up-your-react-architecture-with-mvvm-a471979e3f21
https://velog.io/@rjsdnql123/TILReact%EC%99%80-MVVM%ED%8C%A8%ED%84%B4
https://velog.io/@k7120792/Model-View-ViewModel-Pattern
'Tip and Error' 카테고리의 다른 글
첫 서비스 개발을 어떻게 잘 할 수 있을까? (0) | 2023.05.06 |
---|---|
React + TypeScript + Babel + ESLint & Prettier With Webpack 세팅 (CRA X) (0) | 2023.03.18 |
TypeScript에서 Type을 어떻게 쉽게 파악할까? (4) | 2023.01.29 |
TypeScript에서 왜 import * as $ from '' 을 사용할까? (2) | 2022.12.27 |
프론트엔드 아키텍처 & 디자인 (0) | 2022.12.08 |