[우아한 테크러닝 4기] redux 구현해보기
2021. 7. 2. 19:07ㆍWeb_Programming
안녕하세요. 이번 포스팅에서는 제가 지금 참여하고 있는 우아한 테크러닝 4기에서 들었던 강의 내용을 다뤄보려 합니다!
이번 강의에서는 redux를 javaScript를 이용하여 간단하게 구현하면서, redux에 대한 개념을 깊이 배워볼 수 있었습니다.
💻 리덕스(redux)란?
제가 생각한 리덕스는 간단하게 정의해보면 "외부에서도 전역적으로 상태관리를 가능하게 해주는 라이브러리"라고 볼 수 있을 것 같아요.
아래 그림처럼,
하나의 리덕스 store라는 곳에 상태 값들을 저장해 두고 외부 컴포넌트에서 접근할 수 있도록 하는 시스템이죠.
간단하게 리덕스에 대해 정리를 하고, 구현으로 넘어가 보겠습니다.
먼저 리덕스에서 사용되는 주 용어는 다음과 같습니다.
🔸 스토어 (Store)
상태 값들을 저장해두는 공간
🔸 액션 (Action)
"상태 변화를 위한 수행을 나타내는 객체"로
아래와 같이 필수적으로 요구되는 type값으로 액션들이 구분됩니다.
거기에 추가적으로 액션에 다루고 싶은 data들도 추가할 수 있습니다.
{
type: "ACTION", //필수항목
data: {
id: 0,
text: "리덕스 배우기"
}
}
🔸 리듀서 (Reducer)
"스토어의 액션들을 수행하도록 해주는 함수"로
위에서 말한 action값의 type으로 구분하여 state를 변경하고 이를 반환해주는 함수입니다.
function reducer(state, action) {
switch (action.type) {
case SET_DIFF:
return {
...state,
diff: action.diff
};
case INCREASE:
return {
...state,
number: state.number + state.diff
};
default:
return state;
}
}
🔸 디스패치 (Dispatch)
스토어의 내장 함수로, "수행할 액션을 인자로 받고 리듀서를 실행시켜 액션을 수행"해주는 역할을 합니다.
+ 추가적으로 강의를 들으면서 리덕스에 사용되어 같이 설명이 되었던 것이 closure, 커링 함수인데요.
이에 대한 내용은 아래 포스팅에서 확인해 볼 수 있습니다.
💻리덕스 구현하기
구현 전체 코드는 아래에 작성하겠습니다 :-)
리덕스는 강의해주셨던 민태 님도 이렇게 간단한 코드로 이렇게 히트 친 것이 정말 드문 케이스라고 하시더라고요,
실제로도 구현을 해보니 몇 줄의 코드들로 구현이 가능해서 좀 놀랐습니다.
👉 createStore
store를 생성하는 createStore 함수를 살펴볼게요.
function createStore(reducer) {
let state;
let handler = [];
reducer(state, {
type: "@@__init__@@"
});
return {
dispach: (action) => {
state = reducer(state, action);
handler.forEach((h) => {
h();
});
},
subscribe: (listener) => {
handler.push(listener);
},
getState: () => state
};
}
createStore의 지역 변수로
- "action으로 사용될 state"와
- "subcribe를 수행하기 위한 handler"를 가집니다.
이 스코프(변수)들을 반환 함수에서 사용 가능하도록 앞서 말했던 클로저 함수가 사용되었는데요.
반환 함수로 store를 통해 사용 가능한 함수 dispatch, subscribe, getStore를 만들어줍니다.
🔸 dispatch
인자로 받은 action으로 reducer를 수행하여 변경된 state값(복사본)을 반환받고, 이를 state(원본)에 담아줍니다.
또한, dispatch로 action이 수행될 때마다
handler 리스트에 들어있는 subscribe handler들을 forEach문을 통해 수행해줍니다.
🔸 subscribe
subscribe 호출 시 받은 인자 listener를 handler에 넣어 dispatch에서 수행가능하도록 push 해줍니다.
🔸 getStore는 간단하게 state값을 반환시켜주는 함수입니다.
👉reducer
action을 수행해주는 함수 reducer를 살펴보겠습니다.
const InitState = {
type: "",
counter: 0,
porfile: {
id: "",
imageUrl: ""
}
};
function reducer(state = InitState, action) {
switch (action.type) {
case "counter":
return { ...state, counter: action.counter };
case "action":
return { ...state, type: action.action };
default:
return { ...state };
}
}
reducer는 인자로 state, action을 받습니다.
초기 state값으로 InitState로 선언하여 reducer의 state로 선언해줍니다.
인자로 받은 aciton의 type을 switch case를 이용하여 action을 구분하고, action에 따라 변경된 state값을 반환해줍니다.
🔸 여기서 중요한 것은 state 원본 값의 변경은 제어 가능하도록 createStore내에서만 변경하도록 해야 하기 때문에,
외부에서는(reducer) state를 복사하여 값을 변경하고 그 값을 반환해 createStore에서 원본 값을 변경하도록 하는 것입니다.
👉 actionCreator
actionCreator는 type과 data를 인자로 받아 action을 반환하여 dispatch에 넘겨주도록 해주는 함수입니다.
function actionCreator(type, data) {
return {
type: type,
...data
};
}
type, data를 선언하여 직접 dispatch로 넣어줘도 되지만, 간략화하고 가독성을 높여주기 위해 별도의 함수로 작성합니다.
마지막으로 구현한 redux들을 사용한 코드입니다.
const store = createStore(reducer);
store.subscribe(() => {
console.log("알림", store.getState());
});
function foo() {
store.dispach(actionCreator("counter", { counter: 1 }));
}
function zoo() {
store.dispach(actionCreator("action", { action: "fetch" }));
}
foo();
zoo();
🔹 store를 createStore와 구현한 reducer를 이용하여 생성해줍니다.
🔹 store의 subscribe에 수행할 handler를 인자로 넣어줍니다.
+getStore를 통해 변경된 state값을 반환해주는 handler
🔹 임의의 foo, zoo함수에서
- store.dispach(actionCreator("counter", { counter: 1 })); 와 같이
- actionCreator로 { type: "counter", counter : 1 }의 action을 생성하여 dispatch에 넣어줍니다.
- reducer를 통해 counter값을 0->1로 변경한 state를 반환받아 createStore에서 state값을 변경해줍니다.
🔹 이렇게 foo함수와 zoo함수를 호출하게 되면,
state가 변경되고, 콘솔에 다음과 같이 찍히며 subscribe가 수행됩니다.
알림 ▶{type: "", counter: 1, porfile: Object}
알림 ▶{type: "fetch", counter: 1, porfile: Object}
이렇게 해서 redux를 구현할 수 있었습니다.
전체 코드는 아래와 같습니다.
function createStore(reducer) {
let state;
let handler = [];
reducer(state, {
type: "@@__init__@@"
});
return {
dispach: (action) => {
state = reducer(state, action);
handler.forEach((h) => {
h();
});
},
subscribe: (listener) => {
handler.push(listener);
},
getState: () => state
};
}
const InitState = {
type: "",
counter: 0,
porfile: {
id: "",
imageUrl: ""
}
};
function reducer(state = InitState, action) {
switch (action.type) {
case "counter":
return { ...state, counter: action.counter };
case "action":
return { ...state, type: action.action };
default:
return { ...state };
}
}
const store = createStore(reducer);
function actionCreator(type, data) {
return {
type: type,
...data
};
}
store.subscribe(() => {
console.log("알림", store.getState());
});
function foo() {
store.dispach(actionCreator("counter", { counter: 1 }));
}
function zoo() {
store.dispach(actionCreator("action", { action: "fetch" }));
}
foo();
zoo();
리덕스는 많은 분들이 러닝 커브가 높다고들 말합니다. 저 또한 개념은 간단하지만 처음 사용할 때 꽤 버벅 거렸습니다..
물론 지금도 리덕스 마스터는 못했지만, 이렇게 직접 코드를 구현해보니 함수를 통해 데이터가 변경되고 넘어가는 흐름이 깊게 이해가 된 것 같습니다. 들으면서도 재밌다는 느낌이 들었습니다 🧡💛
이렇게 이번 redux구현 포스팅을 마치겠습니다.
'Web_Programming' 카테고리의 다른 글
[디자인 패턴] 플라이웨이트 패턴_ Flyweight pattern (JavaScript) (0) | 2024.11.17 |
---|---|
[디자인 패턴] 관찰자 패턴_Observer pattern (JavaScript) (0) | 2024.11.17 |
[디자인 패턴] 어댑터 패턴_Adapter pattern (JavaScript) (0) | 2024.11.17 |
[자바스크립트] 클로저 (Closure) 함수&커링 (0) | 2021.06.28 |
d3.js csv파일로 table만들기 (0) | 2021.03.30 |