-
Notifications
You must be signed in to change notification settings - Fork 1
๐ฏ Hyundux: ์์ฒด ์ํ๊ด๋ฆฌ
hyundux๋ ๊ธฐ์กด์ redux๊ฐ ๊ฐ์ง๊ณ ์๋ ์ ํต์ ์ธ ํ์ ๊ฐ์ง๊ณ ์์ง๋ง, ๊ฐ ์ปดํฌ๋ํธ๊ฐ state๋ฅผ ์์ ํ์ง ์๊ณ page ๋จ์์์ props๋ก ์ปดํฌ๋ํธ์ ๋ด๋ ค์ฃผ๊ณ ์์๋ค. ํ์ง๋ง component์ ์์ฒด state์ ๋ณํ๋ฅผ ์ค ์ ์๋ค๋๊ฑด ๋ฐ์ชฝ์ง๋ฆฌ ์ปดํฌ๋ํธ ๋ถ๋ฆฌ๋ผ๊ณ ์๊ฐํด์ ์ปดํฌ๋ํธ๊ฐ ์์ฒด์ ์ผ๋ก state๋ฅผ ๊ฐ์ง๊ณ ์๊ณ ์ํ๋ณํ๋ component๊ฐ ๊ฐ์ง๋๋ก ํ์๋ค.
๊ทธ๋ฆผ ์์
- ์ข์ ์ํ๊ด๋ฆฌ : ๊ธฐ์กด์ state๋ Page ๋จ์๋ก ๊ด๋ฆฌํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋๋ถ๋ถ Page๋ผ๋ ๋ถ๋ชจ์์ ์ํ๋ฅผ ๋ชจ์ ๊ด๋ฆฌํ๋ค. ํ์ง๋ง component๋ผ๋ ๋ฆฌ์กํธ์ ์ฅ์ ์ ๋ฐ๋ง ์ด๋ฆฐ๊ฒ ๊ฐ๋ค๋ ๋๋์ด ๋ค์๋ค. ์ปดํฌ๋ํธ๋ผ๋ ๊ฒ์ ๊ฒฐ๊ตญ ์ฌ์ฌ์ฉ์ด ๋์ผํ๋๋ฐ ๊ทธ๋ด๋๋ง๋ค ๊ทธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ง ํ์ด์ง์ ์๋ก์ด ๋ก์ง์ ์ถ๊ฐํ๋๊ฒ์ ์ข์๋ณด์ด์ง ์์๋ค. ๊ทธ๋์ ์ปดํฌ๋ํธ ๋จ์์ ์ํ๊ด๋ฆฌ๋ฅผ ํ๊ธฐ๋ก ํ์๋ค.
- ๋จ๋ฐฉํฅ ์ํ๊ด๋ฆฌ : ๋ฌด์กฐ๊ฑด์ ์ผ๋ก ๋จ๋ฐฉํ์ด์ฌ์ผํ๋ค. ์ธ๋ถ fetch๋ local storage๊ฐ์ด ํด๋ผ์ด์ธํธ์ ์กฐ์๋ฒ์๋ฅผ ๋ฒ์ด๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ์ง ์์์ผํ์ผ๋ฉฐ ๊ณ ๋ คํ์ง ์์๋ค. ๊ทธ๋ฌํ ํด๋ผ์ด์ธํธ ์ธ๋ถ์์ state๋ฅผ ๋ฐ๊พธ๊ฒ๋๋ฉด ์ด๋ค side effect๊ฐ ๋ฐ์ํ ์ง ๋ชจ๋ฅด๊ณ ์ด๋ค ์์ ์ state ๊ฐฑ์ ๋ ์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ hyundux์์ฒด๋ ๋๊ธฐ์ ์ผ๋ก ๋จ๋ฐฉํฅ์ ์ผ๋ก ์ํ๋ฅผ ๋ณ๊ฒฝํ๊ธฐ์ํด ์ ๊ฒฝ์ผ๋ค.
interface Store {
states: State<unknown>[];
reducers: Reducer<unknown>[];
subscribe: <PayLoad>(
initState: State<PayLoad>,
reducer: Reducer<PayLoad> | null,
cb: (state: State<PayLoad>) => void
) => void;
dispatch: <T>(action: Action | DoAction<T>) => void;
publish: <PayLoad>(oldState: State<PayLoad>, state: State<PayLoad>) => void;
subscribeList: Map<string, <PayLoad>(state: State<PayLoad>) => void>;
}
- state: store์ ๋ฑ๋ก๋ ์ปดํฌ๋ํธ๋ค์ ์ํ๋ฅผ ์ ์ฅํ๋ ๋ฐฐ์ด
- reducer: action๋ค์ ๊ฐ ์ปดํฌ๋ํธ๊ฐ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ ์ฅํ reducer
- subscribe: hook์์๋ง ๋ถ๋ฅด๋ ํจ์๋ก ์ด๊ธฐ์ state์ reducer, publish์ ๋ฑ๋ก๋ ๊ฒ๋ค์ ์ด๊ธฐํํ๋ค.
- dispatch: ์ปดํฌ๋ํธ๊ฐ action์ ์ด์ฃผ๊ณ ๋งตํ๋ ์ ๋ณด๋ค์ ํตํด state๋ฅผ ๋ณ๊ฒฝํ๋ค.
- publish: state๊ฐ ๋ณ๊ฒฝ๋์์์ ์๋ฆฌ๋ ํจ์์ด๋ค.
- subscribeList: ๊ฐ ์ปดํฌ๋ํธ์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ ๋ฐฐ์ด์ด๋ค.
ํ์ฌ dispatch๋ฅผ ์ ์ธํ ๋ชจ๋ function, property๋ค์ ์ธ๋ถ์์ ์ ๊ทผํ์ง ์๋๋ค.
dispatch๋ ์ธ๋ถ์์ ์ง์ ์ ์ผ๋ก ์ ๊ทผํ์ง ๋ชปํ๋๋ก ์์ ํ ์์
interface State<T> {
type: string;
payload: T;
}
interface Action {
type: string;
actionName: string;
payload?: object;
}
interface Reducer<PayLoad> {
type: string;
reducer: (state: State<PayLoad>, action: Action) => State<PayLoad>;
}
interface DoAction<T> {
type: string;
doing: (state: State<T>) => State<T>;
}
- State
- type: ํด๋น state์ mapping์ ์ํ key
- payload: state์ ๋ํ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ payload
- Action
- type: state์ mappingํ๊ธฐ ์ํ key
- actionName: reducer์ mappingํ๊ธฐ ์ํ key
- payload: ๊ฐ action์์ ๋ค๊ณ ์์ด์ผํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ payload
- Reducer
- type: state์ mappingํ๊ธฐ ์ํ key
- reducer: state์ action์ ๋ฐ์์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํจ์
- DoAction
- type: state์ mappingํ๊ธฐ ์ํ key
- reducer์ switch ๋ถ๋ถ์ ๋์์ action์์ ์ฒ๋ฆฌํจ
Reducer์ ๋ฆฌํดํ์ ์ด Promise๊ฐ ์๋์ด์
๋ฆฌ๋์๋ store๋ฅผ ๋ฐ๋์ ๋๊ธฐ์ ์ผ๋ก ๋ฐ๊พธ๊ฒํ๊ธฐ ์ํจ์ด๋ค. ๋น๋๊ธฐ๋ฅผ reducer์์ ์ฒ๋ฆฌํ๊ฒ ๋๋ฉด ๋จ๋ฐฉํฅ๋ณด๋จ ๋ค๋ฐฉํฅ์ด ๋ ๊ฒ ๊ฐ์์ reducer์์๋ ๋ชจ๋ ์ฒ๋ฆฌ๋ฅผ ๋๊ธฐ์ ์ผ๋กํ๋ค. (๐ Hyundux-saga: ์์ฒด ๋ฏธ๋ค์จ์ด ๋น๋๊ธฐ์ฒ๋ฆฌ ์ฌ๊ธฐ์์ ๋ง๋ ๋ฏธ๋ค์จ์ด๊ฐ action์ ์ก์์ ์ฒ๋ฆฌํ๋ค.)
DoAction์ ๋ง๋ค๊ฒ ๋ ์ด์
์ฌ์ค ์ํ๊ด๋ฆฌ๋ ๋ฌด๊ฒ๋ค. ๋ค์ํ state๊ฐ ์๋๊ฒ๋ ์๋๊ณ ํ๋์ state์ด๊ธด ํ์ง๋ง UI๋ฅผ ๋ณ๊ฒฝํ๋๊ฒ ์๋ ๋น์ฆ๋์ค๋ก์ง์ธ ๊ฒฝ์ฐ๊ฐ ์์ ์ ์๋ค. ์ด๋ฐ ๊ฒฝ์ฐ DoAction์ ์ข action์ ์ค์ ์คํ๋๋๋กํ๋ Do๋ฅผ ๋ง๋ค์ด์ ์ฝ๋์์ง์ฑ์ ์ข์ง ์์ง๋ง ๋ณด์ผ๋ฌํ๋ ์ดํธ์ ์๋ ๋ง์ ๊ฒ๋ค์ด ์ค์ด๋๋ Do๋ฅผ ๋ง๋ค๊ฒ ๋์๋ค. (flutter์ bloc๊ณผ cubit์ ์ฐธ๊ณ )
function useWork<PayLoad>(
initialState: HState<PayLoad>,
reducer: Reducer<PayLoad>
) {
const [state, setState] = useState<HState<PayLoad>>(initialState);
store.subscribe(state, reducer, (newState) => {
setState(newState);
});
return state.payload
}
๊ฐ ์ํ๊ด๋ฆฌ๊ฐ ํ์ํ ์ปดํฌ๋ํธ์์ ๋ถ๋ฅด๋ ํด๋น custom hook์ ๋ถ๋ฅด๋๋ฉด ์ปดํฌ๋ํธ๊ฐ subscribe๋ฅผ ํจ
- parameters
- initState: ์ปดํฌ๋ํธ๊ฐ ์ด๊ธฐ์ ๊ฐ์ง๊ณ ์๋ ์ํ๊ฐ๋ค
- reducer: ๊ฐ ์ปดํฌ๋ํธ์ ์ฒ๋ฆฌํ๋ reducer
์์๋ ์ซ์๋ฅผ ์ธ์ด์ฃผ๋ counter์ ์์ ์ด๋ค.
const WORKFLOW_NAME = "Count";
// state type
interface CountPayLoad {
count: number;
text: string;
}
const initCountState = createState<CountPayLoad>(WORKFLOW_NAME, {
count: 0,
text: "helloWorld",
});
// define reducer
const countReducer: Reducer<CountPayLoad> = {
type: WORKFLOW_NAME,
reducer: async function reducer(state, action) {
const payLoad = state.payload;
switch (action.actionName) {
case "countUp":
return makePayLoad(state, { count: payLoad.count + 1 });
case "countDown":
return makePayLoad(state, { count: payLoad.count - 1 });
case "getText": {
const actionPayLoad = (action.payload || {}) as { text: string };
return makePayLoad(state, { text: actionPayLoad.text });
}
default:
return state;
}
},
};
// actions
const action = {
countUp: (): Action => {
return {
type: WORKFLOW_NAME,
actionName: "countUp",
};
},
countDown: (): Action => {
return {
type: WORKFLOW_NAME,
actionName: "countDown",
};
},
getText: (text: string): Action => {
return {
type: WORKFLOW_NAME,
actionName: "getText",
payload: {
text: text,
},
};
},
};
// do๋ฅผ ์ฌ์ฉํ ๋ Reducer๊ฐ ๋ฐ๋ก ํ์์๋ค.
const doAction = {
countUp: (): DoAction<CountPayLoad> => {
return {
type: Do_NAME,
doing: (state: State<CountPayLoad>): State<CountPayLoad> => {
return makePayLoad(state, { count: state.payload.count + 1 });
},
};
},
};
export { action, initCountState, countReducer };
์์ฒ๋ผ state, reducer, action, doAction ๋ฑ์ ์ ์ธํ๊ณ ์ฌ์ฉํ๋ค. ์์ ๊ฐ์ ํํ๋ ์๋๋๋ผ๋ Action, Reducer, State์ ํ์ ์ ํต์ผ์ํค๊ณ ํค๊ฐ๋๋ WROKFLOW_NAME๋ง ๋ง์ถ๊ฒ ๋๋ฉด ์ ์๋ํ๋ค.
- ํ์ฌ๋ dispatch๋ฅผ store๋ฅผ ์ง์ ์ ๊ทผํ๋๋ฐ ์ด๋ฅผ custom hook์ ๋ฃ์ด์ ๋ณด๋ด๊ณ ์ง์ ์ ์ผ๋ก store์ ์ ๊ทผํ๋ ๊ฒ์ ๋ง์์ผํ ๊ฒ ๊ฐ๋ค.
- ์ค์ผ๋งํผ ์ค์์ง๋ง ๋ณด์ผ๋ฌ ํ๋ ์ดํธ๊ฐ ๋๋ฌด ๊ธด๊ฑฐ ๊ฐ๋ค..
- ์ปดํฌ๋ํธ ๋จ์๋ก state๋ฅผ ๊ด๋ฆฌํ๋ค๋ณด๋ ํด๋น state์ ๋ํ ์์กด์ฑ๋ฌธ์ ๊ฐ ์๊ธด๋ค... ์ด ์ ์ ๊ณ ์น๊ธฐ ์ํด ๋ง์ ์๊ฐ์ด ๋ค๊ฒ๊ฐ๋ค.
- store์ ๋ผ์ดํ์ฌ์ดํด์ ์ข ๋ ์ ๊ด๋ฆฌํ ์ ์๋๋ก ๋ง๋ค์ด์ผํ ๊ฒ ๊ฐ๋ค.