
| MVC | MVP | MVVM |
|---|---|---|
| Model - View - Controller | Model - View - Presenter | Model - View- ViewModel |
![]() | ![]() | ![]() |
| Controller가 View와 Model 사이를 중재 | Presenter가 View와 Model 간의 모든 논리를 관리 | ViewModel이 비지니스 로직을 담당 |
React 에 가장 적합한 디자인 패턴이 MVVM이라고들 하는데, 그 이유가 뭘까
* React
- Component 기반으로 UI를 설계
- State와 Props를 사용한 단방향 데이터의 흐름
* MVC
- Controller가 View와 Model을 직접연결하고 있기 때문에,
단방향 데이터의 흐름을 구현하기 어려움
* MVP
- Presenter를 통해서 단방향 데이터의 흐름은 구현이 가능하나,
View와 Presenter의 결합도가 높음
* MVVM
- ViewModel이 상태를 관리하고, View에서 이를 보여주는 방식이라
단방향 데이터 흐름과 역할을 구분을 명확하게 구분할 수 있음
MVVM 패턴은 React의 상태 관리 방식과 잘 맞아떨어지며,
View와 Model 간의 느슨한 결합(Loosely Coupled Structure) 을 가능하게 한다.
따라서 React와 MVVM은 가장 자연스럽고 이상적인 조합이라고 한다.
src/
│
├── model
│ ├── api
│ ├── recoil
│ └── type
│
├── view
│ ├── component
│ ├── navigaton
│ └── screens
│
└── view-model
Model
//user.ts
export const userInfoState = atom<MemberInfoType>({
key: 'userInfoState',
default: defaultUserInfo,
})
View
const EditUserInfo_View = () => {
const {memberInfo, formatAddress, addCertifyIcon} = useEditUserInfo_ViewModel()
const navigation = useNavigation<NativeStackNavigationProp<any>>()
const certifyIconData = addCertifyIcon()
return (
<SafeAreaView className={'bg-white'}>
<HeaderGoBackImg goBack={navigation.goBack} title={'내 정보_MVVM'} />
<View className={'flex items-center mx-5 h-full '}>
<InfoRow label={'아이디'} value={memberInfo.id} onPress={() => {}} />
<InfoRow label={'활동명'} value={memberInfo.name} />
<View className={'flex justify-center items-center w-full mt-7 px-10'}>
<WidthFullButton
title={isToggled ? 'On' : 'Off'}
onPress={() => {
handleButtonTitle()
}}
/>
</View>
</View>
</SafeAreaView>
)
}
export default EditUserInfo_View
View - Model
import {useRecoilValue} from 'recoil'
import {userInfoState} from '../model/recoil/member.ts'
export const useEditUserInfo_ViewModel = () => {
const memberInfo = useRecoilValue(userInfoState)
const formatAddress = (contents: any) => {
if (contents === null) {
return ''
}
return contents
}
const addCertifyIcon = () => {
if (!memberInfo.email) return null
return {
iconSource: require('../../../assets/profile/bule-certify-mark.png'),
emailText: memberInfo.email,
}
}
return {
memberInfo,
formatAddress,
addCertifyIcon,
}
}
- useState와 useEffect를 상태를 업데이트 하는 방식은 수동
- ViewModel에 포함된 상태값(useState)이 바뀌면 그것과 연결된 UI도 다시 렌더링 되는 자동 방식 지원
Custom Hook으로 동일한 상태값을 제공할 수 있는가?