도입 배경
프로젝트 기능 중에
다른 유저들이 본 식물들을 띄우는 기능이 있다.
북마크를 클릭하면 내 도감 목록들 중에 하나를 선택하여 도감에 식물을 넣을 수 있다.

서버에서, 도감에 이미 있는 아이템을 확인해서 구분해 주면 좋겠지만
도감이 여러개이기 때문에 구분하는 것이 여러모로 복잡하기 때문에
기능을 단순화 시키기로 했다.
다른 유저들이 본 식물들을 띄워주고
사용자가 특정 식물을 선택해서 도감에 넣으면
그 식물은 목록에서 필터링된다.
이 과정을 클라이언트에서 맡게 됐고, 그렇기 때문에 상태값은 로그인 ~ 다음 로그인 전까지 유지 된다.
로직이.. 따로 어려울 건 없다.
플로우차트가 익숙하진 않지만 그냥 끄적여 봤다..


1. 추천 식물 정보 목록을 받고
2. 추천 식물 정보를 하나 선택해서 도감에 넣으면
3. 다른 유저들이 본 식물 목록에선 도감에 넣은 식물만 필터링해서 보여준다.
단순하다.
사실 이렇게 단순한 상태관리에 리덕스를 넣을 필요는 없었다.
왜 리덕스를 도입할 필요가 없었는지에 대한 이유를 살펴보자면,
1. 내가 구현하고자 하는 데이터는 복잡하지 않다 - 여러 개의 상태가 서로 의존하거나 상호작용하는경우 리덕스가 중앙에서 관리를 해주기 때문에 상호작용을 예측 할 수 있는데 내가 구현하고자 하는 데이터는 다른 상태값과 상호 의존적이지 않다.
2. 상태의 전역 관리 - 여러 컴포넌트에서 데이터를 공유하고 있지 않다. 굳이 중앙에서 관리할 필요가 없다는 이야기다.
3. 단순한 로직에비해 코드의 양이 방대하다 - 단순히 도감에 식물을 집어넣고, 식물 목록을 조회해오는데에만 많은 코드를 작성한다. 계속 써오던 Recoil 에 비하면 코드양이 방대하게 느껴졌다.
그럼에도 도입을 한 이유는
평소에 리덕스를 쓰는게 어렵다고 느껴져서 배우고 싶었고
쓰면서 어떤 환경에서 사용하면 좋을지 궁금해서 쓴게 크다.
그리고 redux는 디버깅이 용이하다고 하는데 Redux DevTools를 이용해서 상태관리 추적을 어떻게 할 수 있는지 궁금하기도 했다.
결론 : 복잡한 리덕스를 학습하기 위해
index.tsx
import ReactDOM from 'react-dom/client'
import './style/index.css'
import './style/style.css'
import './utils/common/scripts/configure.js'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
import { RecoilRoot } from 'recoil'
import { Provider } from 'react-redux'
import store from './utils/store'
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
window.Kakao.init(`${process.env.REACT_APP_KAKAO_SHARE}`) // 내 웹 키를 할당하면 된다
root.render(
<BrowserRouter>
<RecoilRoot>
<Provider store={store}>
<App />
</Provider>
</RecoilRoot>
</BrowserRouter>,
)
provider 를 통해서 store를 전달
슬라이스 파일
import { createSlice } from '@reduxjs/toolkit'
import { tokenApi } from 'src/utils/common/api/useAxios'
import { castRecPlantListType } from 'src/utils/common/scripts/checkType'
import { RcntVwdItem } from 'src/utils/common/type/type'
import { AppDispatch } from '..'
const initialPlantRecommendedState: { list: RcntVwdItem[]; isPending: boolean; isError: boolean } = {
list: [],
isPending: true,
isError: false,
}
const plantRecommendedSlice = createSlice({
name: 'plantRecommended',
initialState: initialPlantRecommendedState,
reducers: {
// state : 최신 상태
// action : 컴포넌트가 보낸 액션
decrement(state, action) {
if (state.list !== null) {
state.list = state.list.filter((item: RcntVwdItem) => item.plantSpeciesId !== action.payload)
}
},
getData(state, action) {
state.list = action.payload
state.isPending = false
state.isError = false
},
setError(state, action) {
state.isError = action.payload
},
},
})
export const fetchPlantRecommendedData = () => {
return async (dispatch: AppDispatch) => {
try {
const res = await tokenApi.get(
`${process.env.REACT_APP_API_DOMAIN}encyclo-service/stat/recent-plant-detail?페이지=1&사이즈=6`,
)
if (res && res.data) {
const castObj = castRecPlantListType(res.data)
if (castObj) {
const newArr: RcntVwdItem[] = []
for (let i = 0; i < castObj.data.results.length; i++) {
const elem = castObj.data.results[i]
newArr.push({
commonName: elem.plantBriefInfo.commonName,
imageUrl: elem.plantBriefInfo.imageUrl,
scientificName: elem.plantBriefInfo.scientificName,
plantSpeciesId: elem.plantBriefInfo.plantSpeciesId,
})
}
dispatch(plantRecommendedActions.getData(newArr))
}
}
} catch (error) {
dispatch(plantRecommendedActions.setError(true))
}
}
}
// slice에서 설정한 리듀서에 접근 가능
export default plantRecommendedSlice.reducer
export const plantRecommendedActions = plantRecommendedSlice.actions
index.ts
import { configureStore } from '@reduxjs/toolkit'
import plantRecommendedSliceReducer from './plant/plantRecommended'
const store = configureStore({
reducer: {
plantRecommended: plantRecommendedSliceReducer,
},
})
export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>
export default store
컴포넌트에 사용할 커스텀 훅 .ts
// 유저들이 둘러본 목록
export function useGetPlantList() {
const dispatch = useDispatch<AppDispatch>()
const collectionList = useSelector((state: RootState) => state.plantRecommended.list)
const isPending = useSelector((state: RootState) => state.plantRecommended.isPending)
const isError = useSelector((state: RootState) => state.plantRecommended.isError)
useEffect(() => {
if (collectionList?.length === 0 || collectionList === null || !collectionList) {
dispatch(fetchPlantRecommendedData())
}
}, [])
const setCollectionList = (selectId: number | string) => {
dispatch(plantRecommendedActions.decrement(selectId))
}
return {
collectionList,
setCollectionList,
isPending,
isError,
}
}
'사이드 프로젝트' 카테고리의 다른 글
[React Project] 코드 리팩토링 - 메인 페이지 tsx (1) | 2024.09.22 |
---|---|
[React 프로젝트] 로그인 로직 설정.. (UseQuery, axios, typescript) (1) | 2024.09.16 |
[React 프로젝트] 무한 스크롤 도입기.. (react-intersection-observer, typescript) (1) | 2024.09.11 |
[React 프로젝트] 코드 리팩토링 과정, 퍼블리싱 변경 (2) | 2024.09.10 |