[2021-03-04 ์ต์ข ์์ ]
๋ฐ๋จ
KOS ํ๋ก์ ํธ ์งํ ์ค์ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Redux, Mobx)๋ฅผ ์ถํ ๋์ ํ๊ธฐ๋ก ํ๊ณ , ๋จผ์ MVVM ํจํด๋ง ์ฌ์ฉํ๊ธฐ๋ก ํ์๋ค.
ํ์ง๋ง ๊ธฐ์กด์ ๋์์๋ ํด๋ ๊ตฌ์กฐ๋ค์ ๋ชจ๋ react์ Redux / Mobx ๋ฅผ ์ฐ๋ํ์ฌ ๋ง๋ค์ด๋ธ ๊ตฌ์กฐ๋ค์ด์๋ค.
๋ค์์ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ MVVM ํจํด์ ๊ตฌํํ ์ฌ์ดํธ์ด๋ค.
https://github.com/davinci2015/react-mvvm-pokemon ( Class ์ด์ฉ )
https://velog.io/@dlrmsghks7/whatismvvmpattern ( Hook ์ด์ฉ )
์์ ์ฌ์ดํธ๋ค์์ ์ ์ํ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋ฆผ์ผ๋ก ๋ํ๋ด๋ณด์.
View, View Controller, View Model, Model๋ก ๊ตฌ์ฑ๋์ด์์ผ๋ฉฐ, View๋ก ๋ค์ด์จ ์ฌ์ฉ์์ Action์ด View Controller์ ์๋ ๋ก์ง ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ , ํด๋น ๋ฉ์๋๊ฐ View Model์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด์ Model์ state๋ฅผ ๋ณ๊ฒฝํ๋ ํ๋ฆ์ด๋ค.
์ฌ๊ธฐ์ ๋ฌธ์ ๋ Model์ ์์ฑ์ด ๋ณํ๋ ๊ฒ์ View๊ฐ ์ ์ ์๋ค๋ ๊ฒ์ด๋ค.
์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํด์ hi๋ผ๊ณ ์ ์ฅ๋์ด์๋ state๋ฅผ hello๋ผ๊ณ ๋ฐ๊ฟจ๋ค๋ฉด Model์๋ hello๊ฐ ๋ฐ์๋์ด์์ง๋ง, Model์ด ๋ฐ๋์๋์ง View๊ฐ ์ ์ ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์์๊ฒ๋ ์ฌ์ ํ hi๊ฐ ๋ณด์ฌ์ง๋ค.
๋ฌธ์ ํด๊ฒฐ ๊ณผ์
์ด ๋ฌธ์ ๋ Controller๊ฐ Model์ ์์ฑ์ ๊ตฌ๋ ํจ์ผ๋ก์จ(๋ณํ๋ฅผ ์ ๋ฌ๋ฐ์์ผ๋ก์จ) ํด๊ฒฐํ ์ ์๋๋ฐ, ๊ฐ๋ฅํ ๋ฐฉ๋ฒ์๋ ๋๊ฐ์ง๊ฐ ์๋ค.
ํ์ฌ Controller๊ฐ Model์ ์์ฑ์ ๊ตฌ๋ ํ ์ ์๋ ์ด์ ๋ View Controller์ View Model์ ๋ ๋ฒจ์ด ๊ฐ์์์ด๋ฏ๋ก, Model์ ์์ ์ปดํฌ๋ํธ๋ก ์ฌ๋ ค์ฃผ๋ ๊ฒ์ด ์ฒซ๋ฒ์งธ ๋ฐฉ๋ฒ์ด๋ค.
๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ View Controller์ View Model์ ๋ ๋ฒจ์ ์ ์งํ ์ฑ๋ก Model์ด ๊ฐ์ง State๋ง ์ ๋ฌํ๋ ๊ฒ์ด๋ค. ์ด๋ป๊ฒ ํ ์ ์์๊น?
1. Model์ View Controller์ ์์ ์ปดํฌ๋ํธ๋ก ๋ง๋๋ ๋ฐฉ๋ฒ
์ด ๋ฐฉ๋ฒ์ React์ ์๋ฆฌ๋ฅผ ์ด์ฉํ๋ ๊ฐ์ฅ ๊ธฐ์ด์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ์์ ์ปดํฌ๋ํธ๊ฐ props๋ก ํ์ ์ปดํฌ๋ํธ์๊ฒ state๋ฅผ ๋ด๋ ค์ค ์ ์๋ ํน์ฑ์ ์ด์ฉํ๋ ๊ฒ์ด๋ค. Model์์์ ์์ฑ State๊ฐ Controller์ Props๋ก ์ ๋ฌ๋๋ ์๋ฆฌ๋ก ์๋ ๊ตฌ๋ ์ด ๊ฐ๋ฅํ๋ค.
React ์ฌ์ดํธ์์๋ Context API ์ชฝ์ ๋ค์๊ณผ ๊ฐ์ ๋ง์ด ๊ธฐ์ฌ๋์ด์๋ค.
์ด๋ ๊ฒ ํํฅ์ ๋ฐ์ดํฐ ํ๋ฆ์ ๊ธฐ๋๋ ๊ฒ์ ์ถ์ฒํ๊ณ ์๋ค.
ํ์ง๋ง, Model์ ์์ ์ปดํฌ๋ํธ๋ก ๋ณด๋ด๋ ๊ฒ์ ์๋์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๊ตฌํ๋๋ ๊ฒ์ด๋ค.
User์ ๋ง๋ฟ๋ ์ธํฐํ์ด์ค ์ปดํฌ๋ํธ๊ฐ Model์ด๋ผ๋.. ์์ ์ปดํฌ๋ํธ์ธ Model์ State๋ฅผ React์ ์ธ ๋ฐฉ์์ผ๋ก ์ด์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ด์ง๋ง ๋ ผ๋ฆฌ์ ์ด๊ธ๋๋ค.
๋ฐ๋ผ์, ๊ตฌ๋ ๊ธฐ์ ์ ์ฌ์ฉํด๋ณด๋ คํ๋ค.
2. Model State์ ๊ตฌ๋ ํ๋๋ก ๊ตฌํ
๊ตฌ๋ ๊ธฐ์ ์ ๋ฆฌ
๊ฐ์ ๊ตฌ๋ ํ ์ ์๋๋ก ๋ง๋๋ ๊ธฐ์ ์ ์ด๋ค๊ฒ ์์๊น?
https://www.educative.io/blog/react-design-patterns-best-practices ์ฐธ๊ณ
์์ ์ฌ์ดํธ๋ ์ ๋ฐ์ ์ผ๋ก ์ด๋ ๊ฒ ๊ตฌ๋ ์ ํ์ฉํ ๊ธฐ์ ๋ค์ด ์ ๋์ค๊ฒ ๋์ผ๋ฉฐ ๋ฐ์ ํ๋ฉด์ ์ด๋ค ๊ธฐ์ ๋ค์ด ์ฐจ๋ก๋ก ๋์๋์ง๋ฅผ ์ค๋ช ํ๊ณ ์๋ค.
์์ฝํ์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
- MVC ๊ตฌ์กฐ
์ฒ์์๋ MVC ๊ตฌ์กฐ๊ฐ ์์๋ค.
MVC ๊ตฌ์กฐ์์๋ ์ฌ์ฉ์๊ฐ Action์ ๊ฐํ๋ฉด Controller๊ฐ Model์ State๋ฅผ ๋ณ๊ฒฝํ๊ณ , State์ ๋ณ๊ฒฝ์ด View์ ์ ์ฉ๋๋ค. ๋ฌธ์ ๋ ๋จ๋ฐฉํฅ ํ๋ฆ์ด ์๋๋ผ View๋ ์ง์ ์ ์ผ๋ก Model์ State๋ฅผ ์์ ํ ์ ์๋ ์๋ฐฉํฅ ํ๋ฆ์ด๋ผ๋ ๊ฒ์ด๋ค.
์ด๋ฌํ ๊ตฌ์กฐ์์๋ View๊ฐ ๊ฐ Model์ State๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋ ์ํ๊ฐ ๋๋ค. ํ์ง๋ง ๊ตฌ๋ ํ๊ณ ์๋ Model์ state๋ฅผ ์ง์ ์์ ํ ์ ์์ผ๋ฏ๋ก ํ๋ฒ์ Action์ด ์ฐ์์ ์ธ ํธ๋ฆฌ๊ฑฐ๋ฅผ ์ผ์ผ์ผ ์ฝ๋๋ฅผ ์ดํดํ๊ฑฐ๋ ๋๋ฒ๊น ํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ค์๋ค.
- Flux ํจํด
MVC ๊ตฌ์กฐ์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด ํ์ด์ค๋ถ์์ Flux ํจํด์ ๋ง๋ค์๋ค.
Flux ํจํด์ด๋ Action => Dispatcher => Model(Store) => View => Action๋ก ํ action์ผ๋ก๋ถํฐ์ ๋จ๋ฐฉํฅ ํ๋ฆ์ด ์ด์ด์ ธ view๊น์ง ์ ์ฉ๋๋ ํจํด์ ์๋ฏธํ๋ค.
MVC์๋ ๋ค๋ฅด๊ฒ Model์ Dispatcher๋ก๋ง ๊ฐฑ์ ๋ ์ ์์๊ณ , View๋ Model์ State๋ฅผ ๊ตฌ๋ ํ๋ค.
- Redux
์ด๋ฌํ Flux ํจํด์ ๋ ๊ฐ๋จํ๊ฒ ๊ตฌํํ ์ ์์ง ์์๊นํ๋ ๊ณ ๋ฏผ ๋์ ๋์จ ๊ฒ์ด Redux์ ๊ฐ์ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
Redux๋ ํ๋์ Model(Store)๋ก ๊ตฌ์ฑ๋์ด์์ผ๋ฉฐ ์ ์ฒด State๊ฐ ์ด๋ฌํ Store(์ง๋ฆฌ์ ์์ฒ)์ ์งํฉํด์๋ค. ๋์์ ์ด๋ฅผ ์กฐ์ ํ ์ ์๋ Dispatcher๋ก Reducer๋ฅผ ์ฌ์ฉํ๋ค.
Reducer๋ state๋ฅผ ๋ณ๊ฒฝํ๋ ๋ก์ง์ด๋ฉฐ, delay๊ฐ ์๋ ์์ ํจ์๋ก๋ง ๊ตฌ์ฑ๋์ด์ผ ํ๋ค. ์ฆ, api ํต์ ๋ฑ์ Reducer์์ ์ํํ ์ ์๋ค.
๊ตฌํ
๊ฐ๋
Model์์ Context๋ฅผ ๋ง๋ค๊ณ , Context๋ฅผ Provider๋ฅผ ํตํด์ View Model์ ์ ๋ฌํ๋ค.
๊ธฐ์กด๊ณผ ๋น๊ตํด๋ณด๋ฉด View Controller์ ์ญํ ์ View Model์ด ํ๊ณ ์์ผ๋ฉฐ, View Model๊ณผ Model์ ๋ ๋ฒจ์ด ๊ฐ์์๋ ๊ตฌ๋ ๊ธฐ์ ์ ์ด์ฉํ์ฌ Model์ State๋ฅผ View์์ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
์ฌ์ฉ์์ action์ ๋ฐ๋ฅธ ํ๋ฆ์ ์ดํด๋ณด์.
- ์ฌ์ฉ์๊ฐ View์ ์ก์ ์ ๊ฐํจ
- View๋ ์ถ์ํ๋ ๋ฉ์๋ ํธ์ถ
- View Model์ ์ฒ๋ฆฌ ๋ก์ง ๋ฉ์๋๊ฐ ํธ์ถ๋จ
- ์ฒ๋ฆฌ ๋ก์ง ๋ฉ์๋๋ ๊ตฌ๋
ํ๊ณ ์๋ Model์ ๋จ์ ๋ณ๊ฒฝ ๋ฉ์๋๋ฅผ ํธ์ถ
- Model์ ์์ฑ๊ณผ ๋จ์ ๋ณ๊ฒฝ ๋ฉ์๋๋ฅผ ๋ ๋ค ๊ตฌ๋ ํด์ผํจ
- Model์ ๋จ์ ๋ณ๊ฒฝ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด์ Model ์์ฑ ๋ณ๊ฒฝ
- View Model์์ Model์ ์์ฑ์ ๊ตฌ๋ ํ๊ณ ์์ผ๋ฏ๋ก ๋ณ๊ฒฝ ๊ฐ์ง
- View Model์์ ๋ณ๊ฒฝ๋ ์์ฑ์ View์ ๋๊ฒจ์ง๊ณ ์์ผ๋ฏ๋ก View๋ ๋ณ๊ฒฝ ๊ฐ์ง
์ธํฐ๋ท์ MVVM ๊ตฌ์กฐ๋ฅผ ์น๋ฉด ๋์ค๋ ์ด๋ฏธ์ง๋ฅผ ๊ทธ๋๋ก ๊ตฌํํ๊ณ ์์์ ์ ์ ์๋ค.
์ฝ๋
- Model
import { useState, createContext, useContext } from 'react';
// create context to use open
export const OpenStateContext = createContext();
export const OpenDispatchContext = createContext();
// Model์ Context ์ ์ฅ ๋ฐ ์ ๊ณต
export const OpenContextProvider = ({ children }) => {
const [open, setOpen] = useState(true);
return (
<OpenStateContext.Provider value = {open}>
<OpenDispatchContext.Provider value = {setOpen}>
{children}
</OpenDispatchContext.Provider>
</OpenStateContext.Provider>
);
};
export function useOpenState() {
const context = useContext(OpenStateContext);
return context;
}
export function useOpenDispatch() {
const context = useContext(OpenDispatchContext);
return context;
}
Model์์๋ ๊ด๋ฆฌํ๊ณ ์ถ์ ๊ฐ์ ์ ์ํ๊ณ , Context๋ก ๋ฑ๋กํ๋ค.
Context๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๋๋ก ๋ด๋ณด๋ธ๋ค(export).
- Provider
import React from 'react';
import ViewModel from './ViewModel';
import { OpenContextProvider } from './Model/SideBarModel';
// Model๊ณผ View Model์ ์ด์ด์ฃผ๋ ์ญํ
function Provider() {
return (
<OpenContextProvider>
<ViewModel/>
</OpenContextProvider>
);
}
export default Provider;
Provider๋ Model์ด ๋ด๋ณด๋ธ Provider๋ฅผ ๋ฐ์ ViewModel์ ๊ฐ์ธ์ค๋ค.
์ด๋ ๊ฒ ํจ์ผ๋ก์จ ViewModel์ด Model์ Context๋ฅผ ๊ตฌ๋ ํ ์ ์๋ค.
- View Model
import React from 'react';
import { useOpenState, useOpenDispatch } from './Model/SideBarModel';
import SideBarView from './View/SideBarView';
import PageView from './View/PageView';
// View Model์ Model์ Context๋ฅผ ๊ตฌ๋
ํ๊ณ , ๊ฐฑ์ ํ๋ ์ญํ
function ViewModel() {
const open = useOpenState();
const setOpen = useOpenDispatch();
const handleSideBarOpen = () => {
setOpen(true);
};
const handleSideBarClose = () => {
setOpen(false);
};
return (
<React.Fragment>
{open ?
<SideBarView
handleSideBarClose = { handleSideBarClose }
open = { open }
/> : undefined
}
<PageView
handleSideBarOpen = { handleSideBarOpen }
open = { open }
/>
</React.Fragment>
);
}
export default ViewModel;
View Model์ Model์ Context๋ฅผ ์ฌ์ฉํ์ฌ, Model์ State๋ฅผ ๋ณ๊ฒฝํ๊ณ ๊ตฌ๋ ํ๋ค.
View Model์์ ๊ตฌ๋ ํ๊ณ ์๋ State๋ฅผ View์๋ props๋ก ๋ด๋ ค๋ณด๋ด์ฃผ๋ฉด ๋๋ค.
- View
import React from 'react';
import Grid from '@material-ui/core/Grid';
import Tooltip from '@material-ui/core/Tooltip';
import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';
// View๋ Controller์ data ๋ฐ function์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์์ ์ํธ์์ฉ
function SideBarView(props) {
const {
handleSideBarClose,
open
} = props;
return (
<Grid className="sidebar">
<header className="sidebar-header">
<Grid className="sidebar-header-title">
<img src="/logo192.png"/>
<h1>KOS</h1>
</Grid>
<Grid className="sidebar-btn">
<Tooltip title="Close Sidebar" aria-label="close sidebar">
<ArrowBackIosIcon onClick={handleSideBarClose}/>
</Tooltip>
</Grid>
</header>
</Grid>
);
}
export default SideBarView;
View๋ props๋ก ๋ฐ์ ์์ฑ๊ณผ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋๋งํ๋ค.
์ค์ ๊ตฌํ
์๋์ ๊ตฌ์กฐ๊ฐ ์ค์ ๋ก ๊ตฌํ๋ ๊ตฌ์กฐ์ด๋ค.
์ค์ ๋ก ํ๋ก์ ํธ๋ฅผ ๊ตฌํํ๋ค๋ณด๋, Model์ State๋ View์์ ์ง์ ๋ฐ์์ ์ฐ๊ณ ์ฒ๋ฆฌ ๋ก์ง์ View Model์ ๊ฑฐ์น๋ ์์ผ๋ก ๊ตฌ์กฐ๋ฅผ ์ง๊ฒ ๋์๋ค.
์ด๋ ๊ฒ ๋ ์ด์ ๋ React์ Context API์ ํน์ฑ๊ณผ ์ฐ๊ด์ง์ ์ ์๋ค.
Context API ํน์ฑ ์ State๋ ์ง์ ๊ตฌ๋ ์ ํ๊ณ ์๋ ์ปดํฌ๋ํธ์์๋ง ๋ณ๊ฒฝ๋๋ค. ์ฆ, Model์ State๊ฐ ๋ฐ๋ ๊ฒ์ ViewModel์ State๋ ๋ฐ์ํ ์ ์์ง๋ง, props๋ก ์ ๋ฌ๋ View๋ ViewModel์ State๊ฐ ๋ฐ๋ ๊ฒ๋ง์ ๋ฐ์ํ ์ ์๋ค๋ ๋ป์ด๋ค.
Model์ State ๋ณ๊ฒฝ => View Model์ State ๋ณ๊ฒฝ => View์ State ๋ณ๊ฒฝ์ด ์ฐ์์ ์ผ๋ก ์ผ์ด๋์ง ์์๊น ์ถ์ง๋ง ์ํ๊น๊ฒ๋ ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ๊ฐ ์๋ค. React์ ํน์ฑ ์ ๋ณตํฉํ์ ์ธ Array๋ Object๋ ๊ฒ๋ชจ์ต์ด ๋ฐ๋์ง ์์์ผ๋ฉด ์ฌ๋ ๋๋ง๋์ง ์๋๋ค. ๋ฐ๋ผ์ View Model์ State๋ ๋ณ๊ฒฝ๋์์ง๋ง, View๋ก ๋ด๋ ค์ค props๋ ๊ฒ๋ชจ์ต์ด ๋ณ๊ฒฝ๋์ง ์์์ผ๋ฏ๋ก ๋ฐ๋์ง ์๋๋ค.
์๋ฅผ ๋ค์ด Model์ State ์ค [1,2,3,4]์ด๋ผ๊ณ ๋์ด์๋ ์ํ๋ฅผ [1,2,3,3]์ด๋ผ๊ณ ๋ฐ๊พธ๋ฉด, ๊ฐ์ ๋ฐ๋ก ๊ตฌ๋ ํ๊ณ ์๋ View Model์์๋ [1,2,3,3]์ผ๋ก State๊ฐ ๊ฐฑ์ ๋์ง๋ง props๋ก ๊ฐ์ด ์ ๋ฌ๋๋ View์์๋ ๊ฐฑ์ ์ด ์ ์ฉ๋์ง ์๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด forceUpdate๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ์ฌ๋ ๋๋งํ๋ ๋ฑ์ ์์ ๋ฐฉํธ์ ์ฌ์ฉํ ์๋ ์์ง๋ง,
ViewModel์์ props๋ก state๋ฅผ ์ ๋ฌํด์ฃผ๋ ๋์ View์์ ๊ฐ๊ฐ Model์ State๋ฅผ ๊ตฌ๋ ํ๋ ๋ฐฉ์์ผ๋ก ์์ ํด์ฃผ์๋ค.
Comment