티스토리 뷰
Redux로 리팩토링하려는 이유
작성했던 일기장은 useContext, useReducer를 이용하여 전역적으로 상태 관리를 해주었다.
useContext를 사용하면, 컨텍스트를 상태값 / 액션으로 나누어서 리렌더링 문제를 해결해야하고,
상태값, 액션을 따로 Provider로 적용해야하기 때문에 코드가 지저분해 보이기도 하였다.
또한 useReducer 의 경우 새로운 상태 값을 생성 할 때 해당 Context 내부에 포함된 컴포넌트들이 상태값의 일부에만 관심이 있더라도 강제로 re-render 되기 때문에 성능 문제가 발생 할 수 있었다.
Redux 를 사용하면 저장소 상태의 특정 부분만 사용하고 해당 값이 변경 될 때만 re-render 할 수 있다.
따라서 상태관리 방식을 useContext에서 Redux로 리팩토링을 해주려고 한다.
구현
useContext와 Redux의 사용방식이 비슷해서 1시간 정도면 끝낼줄 알았는데, 1시간은 무슨 거의 4시간 정도를 시간을 들여서 마무리를 지었다.
아마, Redux를 겉핥기 식으로만 이해하고, 진행해서가 가장 큰 것 같다.
코딩애플과 생활코딩의 강의를 계속 돌려보면서 전체적인 작동 방식을 이해한 뒤 진행하였다.
우선 store, reducers, action 디렉토리를 만들어 주어서,
관련 기능들을 분리 시켜주었다.
Redux는 다음과 같은 순서로 상태를 관리한다
- 상태가 변경되어야 하는 이벤트가 발생하면, 변경될 상태에 대한 정보가 담긴 Action 객체가 생성
- 이 Action 객체는 Dispatch 함수의 인자로 전달
- Dispatch 함수는 Action 객체를 Reducer 함수로 전달.
- Reducer 함수는 Action 객체의 값을 확인하고, 그 값에 따라 전역 상태 저장소 Store의 상태를 변경.
- 상태가 변경되면, React는 화면을 다시 렌더링
구현을 하기 위해서 아래와 같은 순서를 정하였다.
1. Store
2. Reducer
3. Action
4. useDispatch
5. useSelector
reducers
기존에 작성했던 Reucer를 diaryReducer라는 파일로 분리한뒤,
rootReducer로 만들어 주었다.
Reducers/index.js
import { combineReducers } from "redux";
import diaryReducer from "./diaryReducer";
// 여러 reducer를 사용하는 경우 reducer를 하나로 묶어주는 메소드입니다.
// store에 저장되는 리듀서는 오직 1개입니다.
// const rootReducer = combineReducers({ diaryReducer });
const rootReducer = diaryReducer;
export default rootReducer;
store
Store/store.js
import { legacy_createStore as createStore } from "redux";
import rootReducer from "../reducers";
//store 생성
//store에 reducer 넣기
const store = createStore(rootReducer);
export default store;
reducers에서 생성한 rootReducer를 store라는 전역 상태 저장소에 넣어주었다.
src/index.js
그 후 Provider를 이용하여 App 전체에서 store에 접근이 가능하게 감싸주었다.
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store/store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
actions
가장 많은 시간을 보낸 곳이었다.
기존의 코드를 그대로 사용하지 못하고 조금 바꿔 주어야 했는데,
src/App.js
const dataId = useRef(6);
Actions
CREATE
const onCreate = (date, content, emotion) => {
dispatch({
type: "CREATE",
data: {
id: dataId.current,
date: new Date(date).getTime(),
content,
emotion,
},
});
dataId.current += 1;
};
//REMOVE
const onRemove = (targetId) => {
dispatch({ type: "REMOVE", targetId });
};
//EDIT
const onEdit = (targetId, date, content, emotion) => {
dispatch({
type: "EDIT",
data: {
id: targetId,
date: new Date(date).getTime(),
content,
emotion,
},
});
};
기존 코드에서는 App.js 내에서 useRef를 사용하여 Diary의 id를 관리해주었다.
Action 객체가 같은 파일내에서 있었가 때문에, useRef를 이용한 코드들이 존재하였다.
하지만, 나는 따로 actions 디렉토리를 만들어서 Action객체들을 분리하고 싶었고,
App.js의 useRef를 어떻게 적용시켜야지 되는지 한참을 고민하였다.
고민끝에 useRef를 이용하여 Diary의 id를 관리하는 대신에,
일기 리스트내 첫번째 일기의 Id에 +1을 더하는 식으로 다음 일기의 Id를 설정해 주었고,
일기를 작성할 때 사용하는 Action 객체도 useRef를 사용하여 Id를 설정하는 방식에서
위의 인자를 전달받는 식으로 변경해주었다.
Actions/index.js
export const onInit = (data) => {
return {
type: "INIT",
data,
};
};
export const onCreate = (dataId, date, content, emotion) => ({
type: "CREATE",
data: {
id: dataId,
date: new Date(date).getTime(),
content,
emotion,
},
});
export const onRemove = (targetId) => ({
type: "REMOVE",
targetId,
});
export const onEdit = (targetId, date, content, emotion) => ({
type: "EDIT",
data: {
id: targetId,
date: new Date(date).getTime(),
content,
emotion,
},
});
useDispatch & useSelector
useContext에서 상태값 / 액션으로 따로 Provider를 주고 접근하는 것에 반해서,
Redux에서는 useDispatch와 useSelector를 이용해
Action 객체를 실행하거나, 상태에 바로 접근할 수 있었던 점이 편했다.
변경 내용
state의 접근 방식을 변경
//변경 전
const diaryList = useContext(DiaryStateContext);
//변경 후
const diaryList = useSelector((state) => state);
components/DiaryEditor
새로운 다이어리 id에 접근 방법을 변경
//diaryId 설정
const diaryId = useSelector((state) => (!state[0] ? 0 : state[0].id + 1));
const dispatch = useDispatch();
dipatch로 Actions 객체들을 실행
//변경 전
onCreate(date, content, emotion);
onEdit(originData.id, date, content, emotion);
onRemove(originData.id);
//변경 후
dispatch(onCreate(diaryId, date, content, emotion));
dispatch(onEdit(originData.id, date, content, emotion));
dispatch(onRemove(originData.id));
모든 과정을 끝내고 나니, 일기장이 정상적으로 작동하는 것을 확인하였다.
단순히 리팩토링만 하면 된다고 생각했는데, Redux의 작동원리를 자세히 공부하는 경험이 되었고,
예상치 못한 경우들도 만나서 머리가 아팠지만, 관문하나를 헤쳐나간것 같아서 뿌듯하다.
다음에는 styled-component를 이용하여 리팩토링을 진행해봐야겠다.
'프로젝트 > 일기장' 카테고리의 다른 글
감정일기장 - styled-components 적용 (0) | 2023.03.20 |
---|---|
감정일기장 - 새로운 디자인 목업 (0) | 2023.03.16 |
일기장 빌드 및 배포 (0) | 2023.03.01 |
감정일기장 - Diary 페이지 (0) | 2023.02.27 |
감정 일기장 -Edit 페이지 (0) | 2023.02.27 |
- Total
- Today
- Yesterday
- dfs
- dictionary
- 브루드포스
- SEB43
- til
- useContext
- 프리프로젝트
- React quill
- SEB 43
- 프로그래머스
- BFS
- 스택오버플로우
- 프로젝트
- Redux
- 감정일기장
- 코테
- seb
- 감정 일기장
- 그리디 알고리즘
- SEB 43기
- 코드스테이츠
- 인적성
- 다이나믹 프로그래밍
- 기술면접
- 회고
- 프론트엔드
- Python
- 백준
- 개인 프로젝트
- SEB43기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |