- ์๊ณ ๋ฆฌ์ฆ๊ณผ ๊ณต๋ถํ ๊ฒ ์ ๋ฆฌ์ฉ๋
start Date : 2021/11/11
- 2021/11/11 : Redux-action
- 2021/11/12 : immer.js
- 2021/11/15 : Redux-middleware & redux-thunk & redux-saga
- 2021/11/17 : ๋ฆฌ์กํธ ํ๋ก์ ํธ ํ๊ฒฝ์ค์
- redux-action์ ์ก์ ์์ฑ ํจ์์ ๋ฆฌ๋์๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํ ์ ์๊ฒ ํด์ค๋ค.
- createAction, handleAction, combineAction ์ด ์๋ค.(createAction๊ณผ handleAction ์ ์ฃผ๋ก ์ด์ฉํ๋๊ฑฐ ๊ฐ๋ค.)
* ์ก์
์์ฑ ํจ์๋ฅผ ๋ง๋ค์ด์ฃผ๋ ํจ์ ์ด๋ค.
* ์ง์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค ํ์๊ฐ ์์
* ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ ๋ฐ์ ๊ฐ์ ์ก์
์ payload ๊ฐ์ผ๋ก ์ค์ ํด์ค๋ค.
// ๊ธฐ์กด
export const increment = (index) => ({
type: "INCREMENT",
index,
});
//===========================================
// createAction๋ฅผ ์ฌ์ฉ
export const increment = createAction("INCREMENT");
//์ด๋ ๊ฒ createAction๋ก ์์ฑ ํ increment(ํ๋ผ๋ฏธํฐ) ๋ฅผ ๋ฃ์ด์ ์ฌ์ฉํ๋ค.
increment(1);
createAction์ผ๋ก ์์ฑํ increment๋ฅผ ํธ์ถ ํ๊ฒ ๋๋ฉด ์ด๋ฐ์์ผ๋ก ์ก์ ์ด ๋ง๋ค์ด์ง๋ค.
{
type: 'INCREMENT',
payload: 1
}
* ๋ฆฌ๋์ ํจ์๋ฅผ ๋ง๋ค์ด์ฃผ๋ ํจ์์ด๋ค.
* ๋ฆฌ๋์๋ฅผ ๊ฐ๋จํ๊ณ ๊ฐ๋
์ฑ์๊ฒ ์์ฑ ํ ์ ์๋ค.
* ์ฒซ๋ฒ์งธ ์ธ์๋ ์ก์
์ ์คํํ ํจ์๋ฅผ ๊ฐ์ง ๊ฐ์ฒด, ๋๋ฒ์งธ ์ธ์๋ ๊ธฐ๋ณธ๊ฐ์ ๋ฃ์ด์ค๋ค.
const reducer = (state = initState, action) => {
switch (action.type) {
case "INCREMENT":
return {
...state,
number: state.number + 1,
};
case "DECREMENT":
return {
...state,
number: state.number - 1,
};
default:
return state;
}
};
const reducer = handleActions(
{
INCREMENT: (state, action) => ({
counter: state.counter + action.payload,
}),
DECREMENT: (state, action) => ({
counter: state.counter - action.payload,
}),
},
{ counter: 0 }
);
- React์์ state์ ๋ถ๋ณ์ฑ์ ์ ์งํ๋ ์ฝ๋๋ฅผ ์ฝ๊ฒ ์์ฑํ๊ฒ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋ณํ์ง ์๋ ์ฑ์ง์ ๋ถ๋ณ์ฑ์ด๋ผ๊ณ ํ๋ค.
React์์๋ State๋ฅผ ์ง์ ์ ์ผ๋ก ๋ณํ์ํค๋ฉด ์๋๊ธฐ ๋๋ฌธ์ ๋ถ๋ณ์ฑ์ ์ ์งํ๋๊ฒ์ด ์ค์ํ๋ค.
- ๋ฐ์ดํฐ์ ๊ด๋ฆฌ์๋ผ๊ณ ๋ณด๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
- State์ ์์๋น๊ต๋ฅผ ํตํด์ ์๋ก์ด ๊ฐ์ธ์ง ํ๋จํ๊ฒ ๋๋ค.
- ์๋ก์ด ๊ฐ์ผ ๊ฒฝ์ฐ ๋ฆฌ๋ ๋๋ง
- state๋ฅผ ๋ณํ์ํค๊ณ DOM์ ๋ค์๋ง๋ค๊ธฐ ์ํด์๋ ์๋ก์ด ๊ฐ์ฒด or ์๋ก์ด ๋ฐฐ์ด์ ๋ง๋ค์ด ์๋ก์ด ์ฐธ์กฐ๊ฐ์ด ์์ฑ๋์ด์ผ ํ๋ค.
//์ด๋ ๊ฒ ์ง์ ์ ์ผ๋ก ์์ ํ๋ฉด ๋ฆฌ๋ ๋๋ง ๋์ง ์๋๋ค.
state.push("value"); //์๋ก์ด ์ฐธ์กฐ๊ฐ์ด ์๋
//state ๋ณํํ๋ ๊ฒฝ์ฐ
//Class component ์ผ๊ฒฝ์ฐ
setState((prevState) => {
return newState;
});
//ํน์
setState(newState);
//functional component ์ผ ๊ฒฝ์ฐ
const [value, setValue] = useState(์ด๊ธฐ๊ฐ);
setValue(newState);
// state : ๋ฐ๊พธ๊ณ ์ถ์ ๊ฐ์ฒด or ๋ฐฐ์ด
// draft : ์ด๋ค์์ผ๋ก ๋ณ๊ฒฝํ ์ง์ ๋ํ ํจ์
const nextState = produce(state, (draft) => {
draft.value = value;
});
์ด๋ฐ์์ผ๋ก ๋ณ๊ฒฝํ๊ฒ ๋๋ฉด ์์๋ณต์ฌ๋ฅผ ํตํด์ ๋ฆฌ๋ ๋๋ง์ด ๋๋ค.
- ์ฃผ๋ก ๋ฆฌ๋์์์ ์ฌ์ฉ๋๋ค.
//๊ธฐ์กด ๋ฆฌ๋์ ์์ฑํ ๋
const reducer = (state = initState, action) => {
switch (action.type) {
case "INCREMENT":
return {
...state,
number: state.number + 1,
};
case "DECREMENT":
return {
...state,
number: state.number - 1,
};
default:
return state;
}
};
// immer๋ฅผ ์ ์ฉํ ๋ฆฌ๋์
const reducer = (state = initState, action) => {
switch (action.type) {
case "INCREMENT":
return produce(state, (draft) => {
draft.number + 1;
});
case "DECREMENT":
return produce(state, (draft) => {
draft.number - 1;
});
default:
return state;
}
};
์คํ๋ ๋ ๋ฌธ๋ฒ์ ์ฌ์ฉํ์ง ์๊ณ ๊น๋ํ๊ฒ ์ฝ๋๊ฐ ๋ณ๊ฒฝ๋์๋ค.
const [value, setValue] = useState({
number: 1,
done: false,
});
//๊ธฐ์กด
const onClick = useCallback(() => {
setValue((value) => ({
...value,
done: !value.done,
}));
}, []);
//์ ์ฉํ๊ฒ
const onClick = useCallback(() => {
setValue(
produce((draft) => {
draft.done = !draft.done;
})
);
}, []);
์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ธ ์ํ๊ฐ์ ์๋ตํ๊ณ ๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ์ธ ์ ๋ฐ์ดํธ ํจ์๋ง ๋ฃ์ด์ฃผ๋ฉด ์ ๋ฐ์ดํธ๋ฅผ ํด์ฃผ๋ ํจ์๊ฐ ๋๋ค.
- ์ฃผ๋ก ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌ ํ ๋ ์ฌ์ฉ
- action์ dispatch ํ์ ๋ reducer๋ฅผ ์คํํ๊ธฐ ์ ์ ์ถ๊ฐ ์์ ์ ์คํ ํ ์ ์๊ฒ ํด์ค๋ค.
- redux-thunk์ redux-saga ๋๊ฐ์ง๊ฐ ๊ฐ์ฅ ์์ฃผ ์ฌ์ฉ๋๋ค.
- ๋ฏธ๋ค์จ์ด๋ฅผ ์ ์ฉ ํ ๋๋ applyMiddleware(๋ฏธ๋ค์จ์ด) ํจ์๋ฅผ ์ฌ์ฉํ๋ค.
(logger๋ฅผ ์ฌ์ฉ ํ ๋๋ ๋งจ๋ค์ logger๋ฅผ ์ ์ฉ์์ผ์ผํจ example : applayMiddleware(thunk,logger) ) - ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋ฆฌ๋์ค์ ํ๋ฆ : ์ก์ -> ๋ฏธ๋ค์จ์ด -> ๋ฆฌ๋์ -> ์คํ ์ด
- ์ก์ ์ด ๋ฌด์๋๊ฒ ๋ง๋ค ์ ์๋ค.
- ์ก์ ์ ์ฝ์์ ์ถ๋ ฅํ๊ฑฐ๋ ์๋ฒ์ ๋ก๊น ํ ์ ์๋ค.
- ์ก์ ์ด ๋์คํจ์น ๋์ ๋ ์์ ํด์ ๋ฆฌ๋์์ ์ ๋ฌ ํ ์ ์๋ค.
- ํน์ ์ก์ ์ด ๋ฐ์ํ์ ๋ ๋ค๋ฅธ ์ก์ ์ด ๋ฐ์ ํ ์ ์๋ค.
- ํน์ ์ก์ ์ด ๋ฐ์ํ์ ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ฅผ ์คํ์ํฌ ์ ์๋ค.
const middleware = store => next => action =>{
//์ํํ ์์
}
// ์์ ๋ฉ์๋๋ฅผ ํ์ด์ ์ด๊ฒ
function middleware(store){
return function(next){
return function(action){
์ํํ ์์
}
}
}
- store๋ ๋ฆฌ๋์ค ์คํ ์ด ์ธ์คํด์ค( dispatch, getState, subscribe ๋ด์ฅํจ์๊ฐ ์กด์ฌํจ)์ด๋ค.
- action์ ํ์ฌ ์ฒ๋ฆฌํ๊ณ ์๋ ์ก์ ๊ฐ์ฒด
- next๋ ์ก์ ์ ๋ค์ ๋ฏธ๋ค์จ์ด์ ์ ๋ฌํ๋ ํจ์ next(action) ๊ฐ์ ์์ผ๋ก ์ฌ์ฉ
(next๋ฅผ ํธ์ถํ์ง ์๋๋ค๋ฉด ์ก์ ์ด ๋ฌด์์ฒ๋ฆฌ๋์ด ๋ฆฌ๋์๋ก ์ ๋ฌ๋์ง ์์)
- ๋น๋๊ธฐ ์์ ์ ์ฒ๋ฆฌ ํ ๋ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ๋ฏธ๋ค์จ์ด
- ์ก์ ๊ฐ์ฒด๊ฐ ์๋ ํจ์๋ฅผ ๋์คํจ์น ํ ์ ์๋ค.
- ํจ์๋ฅผ ๋์คํจ์น ํ ๋๋ dispatch์ getState๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์์ผ ํ๋ค.
(getState๋ฅผ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ ๋ฐ์์ค์ง ์์๋ ๋ฌด๊ดํจ)
export const increase = () => (dispatch, getState) => {
setTimeOut(() => dispatch(increase()), 1000); //1์ด ๋๋ ์ดํ dispatch์ํด
};
// getState๋ ์ฌ์ฉํ์ง ์์์ ๋ฃ์ง ์์๋ ๊ด์ฐฎ์
export const increase = () => (dispatch) => {
setTimeOut(() => dispatch(increase()), 1000);
};
// Async/await๋ ์ ์ฉ๊ฐ๋ฅํ๋ค
export const callApi = () => async (dispatch) => {
try {
const data = await API.getData();
dispatch({ type: GET_DATA_SUCCESS, data });
} catch (e) {
dispatch({ type: GET_DATA_ERROR, error: e });
}
};
- ์ก์ ์ ๋ชจ๋ํฐ๋งํ๋ค ํน์ ์ก์ ์ด ๋ฐ์ํ๋ฉด ํน์ ์์ ์ ํ๋ ๋ฐฉ์(ํน์ ์ก์ : ์๋ฐ์คํฌ๋ฆฝํธ ์คํ, ๋ค๋ฅธ์ก์ ๋์คํจ์น, ํ์ฌ ์ํ ๋ถ๋ฌ์ค๊ธฐ)
- Generator ๋ฌธ๋ฒ์ ์ฌ์ฉํ๋ค.
- ๋น๋๊ธฐ ์์ ์ ํ ๋ ๊ธฐ์กด ์์ฒญ์ ์ทจ์ ์ฒ๋ฆฌ ํ ์ ์๋ค.
- ํน์ ์ก์ ์ด ๋ฐ์ํ์ ๋ ๋ค๋ฅธ ์ก์ ์ด ๋์คํจ์น๋๊ฒ ํ๊ฑฐ๋, js์ฝ๋๋ฅผ ์คํ ํ ์ ์๋ค.
- ์น์์ผ ์ฌ์ฉ ์ Channel์ด๋ผ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํด ํจ์จ์ ์ผ๋ก ์ฝ๋ ๊ด๋ฆฌ ๊ฐ๋ฅ
- API ์์ฒญ์ด ์คํจํ์ ๋ ์ฌ์์ฒญํ๋ ์์ ๊ฐ๋ฅ
- ํจ์๋ฅผ ์์ฑ ํ ๋ ํจ์๋ฅผ ํน์ ๊ตฌ๊ฐ์ ๋ฉ์ถ๊ฑฐ๋ ๋๋์๊ฐ๊ฒ ํ ์ ์๋ค.
- function * ์ด๋ผ๋ ํค์๋๋ฅผ ์ฌ์ฉํด ๋ฉ์๋๋ฅผ ์์ฑํ๋ค
- yield ๋ผ๋ ํค์๋๋ฅผ ์ฌ์ฉํด ์ผ์์ ์ผ๋ก ์ ์ง๋๊ณ ๋ฉ์๋.next() ํจ์๋ฅผ ์ฌ์ฉํด ๋ค์ ์์ ๋ ์ ์๋ค.
function* generatorFunction() {
console.log("1");
yield 1;
console.log("2");
yield 2;
console.log("3");
yield 3;
return 4;
}
const generator = generatorFunction();
//next๋ฅผ ํธ์ถํ๊ฒ ๋๋ฉด yield ๊ฐ์ ๋ฐํํ๊ณ ์ฝ๋์ ์งํ์ ๋ฉ์ถ๋ค.
generator.next(); // value : 1, done : false
generator.next(); // value : 2, done : false
generator.next(); // value : 3, done : false
//yield๊ฐ ๋๋๊ณ return์ด ๋๋ฉด done ๊ฐ์ด true๋ก ๋ฐํ๋๋ค.
generator.next(); // value : 4, done : true
function* generatorFunction2() {
let a = yield;
let b = yield;
yield a + b;
}
const generator2 = generatorFunction2();
//nextํธ์ถ์ ์ธ์๋ฅผ ์ ๋ฌํ์ฌ ํจ์ ๋ด๋ถ์์๋ ์ด์ฉ ๊ฐ๋ฅํ๋ค.
generator2.next(1); // value : undefined, done : false
generator2.next(2); // value : undefined, done : false
generator2.next(); // value : 3, done : true
- delay(ms) => ์ค์ ๋ ์๊ฐ ์ดํ์ resolveํ๋ ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๋ค
- put({type,payload}) => ํน์ ์ก์ ์ dispatch ํ๋๋ก ํ๋ค
- takeEvery(type, function) => ๋ค์ด์ค๋ ๋ชจ๋ ์ก์ ์ ๋ํด ํน์ ์์ ์ ์ฒ๋ฆฌํด์ค๋ค.
- takeLatest(type,function) => ๊ธฐ์กด ์งํ ์ค์ด๋ ์์ ์ ์ทจ์ํ๊ณ ๊ฐ์ฅ ๋ง์ง๋ง ์์ ๋ง ์ํ
- call(function,parameter) => ํน์ ํจ์๋ฅผ ํธ์ถํ๊ณ ๊ฒฐ๊ณผ๋ฌผ์ด ๋ฐํ ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆผ
- all([ ]) => ์ ๋ค๋ ์ดํฐ ํจ์๋ฅผ ๋ฐฐ์ด๋ก ๋ฃ์ด์ฃผ๋ฉด ๋ณํ์ ์ผ๋ก ๋์์ ์ํ Promise.all๊ณผ ๋น์ทํจ
/* saga ๋ฉ์๋ ๊ตฌํ */
const GET_DATA = "GET_DATA";
function* getDataSaga() {
try {
const count = yield call(DataAPI.getData);
//์ฑ๊ณต์
yield put({
type: GET_DATA_SUCCESS,
payload: data,
});
} catch (e) {
//์คํจ์
yield put({
type: GET_DATA_ERROR,
error: true,
payload: e,
});
}
}
export function* dataSaga() {
yield takeEvery(GET_DATA, getDataSaga);
}
/* ๋ฃจํธ ๋ฉ์๋ index */
//๋ฏธ๋ค์จ์ด์ ์ ์ฉํ rootSaga ๋ง๋ค๊ธฐ
const rootReducer = combineReducers(๋ฆฌ๋์๋ค);
export function* rootSage() {
//์์์ ๋ง๋ dataSaga๋ฅผ ๋ฃ์ด์ค
yield all([dataSaga()]); //all์ ๋ฐฐ์ด ์์ ์ฌ๋ฌ ์ฌ๊ฐ๋ฅผ ๋์์ ์คํ์์ผ ์ค๋ค.
}
/* ์คํ ์ด */
//๋ฏธ๋ค์จ์ด์ ์ ์ฉ
const sagaMiddleware = createSagaMiddleware(); // ์ฌ๊ฐ ๋ฏธ๋ค์จ์ด๋ฅผ ๋ง๋ฌ
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
//์คํ ์ด ์์ฑ ํ ์คํ ์์ผ์ค์ผํจ
sagaMiddleware.run(rootSaga);
- node.js์ ์์กด์ฑ๊ณผ ํจํค์ง ๊ด๋ฆฌ๋ฅผ ์ํ ํจํค์ง ๋งค๋์
- npm install์ผ๋ก package.josn ํ์ผ์ ์์กด์ฑ์ ์ง์ ํ ์ ์์
- ๋ฒ์ ๊ด๋ฆฌ ์ง์
ํจํค์ง๋ฅผ ๋ค์ด๋ก๋ ํ๊ณ ์ถ์ ๋
npm i ํจํค์ง๋ช ํน์ npm install ํจํค์ง๋ช ์ผ๋ก ๋ค์ด ๊ฐ๋ฅ
์ธ์คํจ ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ ๋ -D๋ --save-dev -P๋ --save-prod -g๋ ๊ธ๋ก๋ฒ์ ์ธ์คํจ
- node ํจํค์ง๋ฅผ ์คํ์ํค๋ ๋๊ตฌ(npm5.2.0 ์ด์ ์ง์)
npx ํจํค์ง๋ช
์ผ๋ก ์ฌ์ฉ
npx์ ์ญํ
- ์คํํ ํจํค์ง๊ฐ ๊ฒฝ๋ก์ ์๋์ง ํ์ธ
- ๊ฒฝ๋ก์ ์์ผ๋ฉด ์คํ
- ๊ฒฝ๋ก์ ์์ ๊ฒฝ์ฐ npx๊ฐ ์ต์ ๋ฒ์ ํจํค์ง๋ฅผ ์ค์น ํ ํ ์คํ
- npm i create-react-app
- ๋ฆฌ์กํธ์ ๊ธฐ์ดํ๊ฒฝ์ ์ค์ ํด์ฃผ๋ ๋ณด์ผ๋ฌ ํ๋ ์ดํธ์ด๋ค.
- ๋ฐ๋ฒจ, ์นํฉ๋ฑ ๋ค์ํ ํจํค์ง๊ฐ ํฌํจ๋์ด์๋ค (ES6+ ๋ฌธ๋ฒ, CSS ํ์ฒ๋ฆฌ๋ ํฌํจ)
์์ค์ฝ๋๋ฅผ ๋ถ์ํด ๋ฌธ๋ฒ์ ์ค๋ฅ, ์คํ์ผ์ ์ค๋ฅ, ๊ตฌ์กฐ ๋ฑ์ ํ์๋ฅผ ๋ฌ์์ฃผ๋ ๋๊ตฌ
ํ์ ์ ์คํ์ผ์ ํต์ผ ์ํฌ ์ ์๋ค์ฝ๋๋ฅผ ์ ๋ ฌํด ์ฃผ๋ Code Formatter ์ ํด์ง ๊ท์น์ ๋ฐ๋ผ Prettier๊ฐ ์๋์ผ๋ก formatting์ ํด์ค
root ํด๋์ .prettierrc ์์ฑ ๋ฐ ์ค์
{
"singleQuote": true, // '' ๋ก ์ฌ์ฉํ ๊ฒ์ธ์ง
"semi": true, //์ธ๋ฏธ์ฝ๋ก ์ฌ์ฉํ ์ง
"tabWidth": 2, //๋ค์ฌ์ฐ๊ธฐ ์นธ์
"trailingComma": "all", //์ผํ๋ฅผ ๋ถ์ผ์ง "none", "es5", "all" ๊ฐ๋ฅ
"printWidth": 80 //ํ์ค์ ๋ช์นธ๊น์ง ์์ฑํ ์ง
}
//์ถ๊ฐ์ต์
์ https://prettier.io/docs/en/options.ahtml ์์ ํ์ธ
VSCode ํ์ฅ์์ Prettier ์ค์น ํ ํ๊ฒฝ์ค์ ์์ Format On Save ์ฒดํฌ
ESLint ์ค์น ํ ํ๊ฒฝ์ค์ ์์ Auto Fix on Save ์ฒดํฌ
npm install eslint-config-prettier ํ pakage.json์ ์๋์ฝ๋ ์ถ๊ฐ
"eslintConfig" :{
"extends":[
"prettier"
],
"rules": {
"react/jsx-filename-extension": 0 //ํ์ผ์ .jsx ํ์ฅ์ ๊ท์น ๋ฌด์
}
}
auto Import ๋ vscode์ ํ์ฅ์์ ๋ค์ด ๋ฐ์ผ๋ฉด ํธํ ๊ฐ๋ฐ์ ํ ์ ์๋ค.