
Section 19 리덕스에 뛰어들기
TIL1) 리덕스 vs 리액트 컨텍스트
리덕스는 전역 상태 관리를 위한 라이브러리로, 복잡한 애플리케이션에서 상태를 예측 가능하고 일관되게 관리할 수 있도록 도와준다. 주로 복잡한 상태 관리가 필요한 경우 사용한다.
리액트 컨텍스트는 컴포넌트 트리 전반에 걸쳐 데이터를 전달할 수 있는 방법을 제공하지만, 상태 업데이트가 빈번하거나 많은 상태를 관리해야 하는 경우에는 성능 문제가 발생할 수 있다.
리덕스는 미들웨어와 같은 강력한 기능을 제공하며, 리액트 컨텍스트는 비교적 단순한 상태 공유에 적합하다.
//그니깐. 리덕스 사용하라는 말이다!
TIL2) 리덕스 createStore()
createStore()는 리덕스에서 스토어를 생성하는 함수입니다. 스토어는 애플리케이션의 전역 상태를 관리하며, 리듀서와 미들웨어를 포함할 수 있습니다.
리덕스 툴킷에서는 configureStore()를 사용하여 스토어를 생성하며, 이는 미들웨어와 개발자 도구 설정을 자동으로 포함하여 더 쉽게 설정할 수 있습니다.
TIL3) Dispatch(1월 1일 아니얌)
dispatch는 리덕스에서 액션을 스토어에 전달하는 함수이다.
액션은 상태를 변경하기 위한 정보를 담고 있으며, 스토어는 리듀서를 통해 상태를 업데이트한다.
리덕스 툴킷에서는 dispatch를 사용하여 액션을 보낼 수 있으며, 비동기 작업을 위해 createAsyncThunk와 같은 유틸리티를 제공한다.
TIL4) 클래스 기반 컴포넌트가 있는 리덕스
클래스 기반 컴포넌트에서도 리덕스를 사용할 수 있다. connect 함수와 mapStateToProps, mapDispatchToProps를 사용하여 스토어의 상태와 액션을 컴포넌트에 연결한다.
하지만 리액트의 최신 버전에서는 함수형 컴포넌트와 훅을 사용하는 것이 일반적이며, useSelector와 useDispatch 훅을 사용하여 상태와 액션을 연결한다.
TIL5) State 슬라이스 추가하기
리덕스 툴킷에서는 상태를 슬라이스 단위로 관리한다. 슬라이스는 관련된 상태와 리듀서를 그룹화하여 관리할 수 있게 해준다.
createSlice() 함수를 사용하여 슬라이스를 생성하며, 이 함수는 자동으로 액션 크리에이터와 리듀서를 생성한다.
TIL6) 리덕스 툴킷 State 연결하기
슬라이스 생성: createSlice()를 사용하여 슬라이스를 정의한다. 이 함수는 초기 상태, 리듀서, 그리고 자동으로 생성된 액션 크리에이터를 반환.
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
},
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
스토어 구성: configureStore()를 사용하여 스토어를 설정하고, 슬라이스 리듀서를 포함
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
컴포넌트에서 상태 사용: useSelector 훅을 사용하여 상태를 가져오고, useDispatch 훅을 사용하여 액션을 디스패치한다
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<button onClick={() => dispatch(decrement())}>-</button>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
</div>
);
}
export default Counter;
Learned
리덕스 툴킷(RTK)으로 상태 관리
리덕스 툴킷은 리덕스의 복잡한 설정을 단순화하고 효율적인 상태 관리를 가능하게 해주는 강력한 도구이다. 이를 통해 애플리케이션의 상태를 더 쉽게 관리할 수 있음을 배웠다. 다음은 리덕스 툴킷을 활용하여 상태를 연결하는 과정에서 배운 주요 내용이다.
리덕스 툴킷에 대해 공부하면서 상태 관리의 직관성과 생산성이 매우 높아졌다고 느꼈다. 특히 보일러플레이트 코드가 줄어들면서 설정이 간단해지고, 슬라이스 단위로 상태를 나눠 관리할 수 있어 유지보수가 훨씬 쉬워졌다.
useSelector와 useDispatch를 통해 쉽게 상태를 가져오고 변경할 수 있어서, 여러 컴포넌트 간 상태 공유도 원활했다.
질문거리 (사실 리덕스가 아닌 리액트쿼리랍니다)
고래 에선 리액트쿼리를 쓰고, 현재 저도 리액트 쿼리를 써서 여쭤봅니다!
리액트 쿼리를 선택한 이유가 무엇인가요? 다른 데이터 페칭 라이브러리와 비교했을 때 어떤 점이 가장 큰 장점인가요?
리덕스와 리액트 쿼리를 함께 사용하는 경우가 있나요? 있다면 어떤 상황에서 두 가지를 병행해서 사용하는 것이 적절한지 알고 싶습니다.
리액트 쿼리에서 캐싱 전략을 어떻게 설정하는 것이 좋은가요? 캐시를 관리하는 데 있어 유용한 팁이 있으면 알려주세요.
데이터가 자주 변경되는 경우 캐시 무효화(invalidation)를 어떻게 처리하는지 궁금합니다.
리액트 쿼리를 사용하면서 서버 상태와 클라이언트 상태를 어떻게 구분하여 관리하는지 알고 싶습니다.
클라이언트 상태 관리를 위해 리덕스나 리액트 컨텍스트를 함께 사용하는 경우가 있는지, 있다면 어떤 상황에서 사용하는지 궁금합니다.
오늘은 10월 13일. 13일의 주인공이 군대에 갔다. 슬프네.. 내년 6월 11일만 기다릴겝,,,,,,,
서버 상태(데이터)를 클라이언트의 다른 state들과 명확하게 구분해서 관리하는 컨셉을 제대로 정착시킨 라이브러리가 react-query라고 봐도 될 거 같아요. 비동기 데이터를 관리할 때 꼭 필요한 로딩, 에러 상태나 캐싱, 데이터 수명, 갱신 등의 기능이 잘 설계되어 있어서 좋은 거 같습니다. 제가 느낄 때 가장 큰 장점은 개발자 경험과 꾸준한 개선/유지보수인 거 같아요. 다른 라이브러리랑 비교했을 때의 장점은 이 글에 잘 소개되어있습니다. https://tech.kakaopay.com/post/react-query-1
서버에서 받아온 데이터가 전역적으로 이용되어야 하는 경우 Redux와 react-query를 함께 이용할 수도 있습니다.
자주 예시로 들었던 유저의 로그인 정보 등을 관리할 때인데요. 사용자가 로그인을 하고, 그 응답으로 사용자 정보를 받아오는 과정은 react-query가 담당하고, 해당 정보를 전역에서 이용하기 위해서 상태 관리는 redux가 담당할 수 있습니다. staleTime을 적절히 설정하면 redux없이도 react-query를 전역 상태 관리 용도로 활용할 수 있기도 합니다.
staleTime이나 cacheTime 같은 경우에는 페이지의 목적이나 사용자에 따라서 적절히 설정하는 것이 필요합니다. 사용자가 오래 페이지에 머무르면서 작업하는 페이지이거나, 자동으로 데이터가 다시 fetching되면 곤란한 페이지들의 경우에는 캐시를 초기화하지 않는 게 필요하고, 그렇지 않은 경우에는 적절한 시간으로 설정해주어야 합니다.
서버에서 빈번하게 변경되는 데이터의 현재 상태를 클라이언트에서 알아야 하는 경우에는 여러 가지 고려할 만한 방식이 있는데, websocket 등 실시간 통신 방식을 이용하거나 refetchInterval 같은 parameter를 활용해서 자동으로 자주 refetch되도록 하는 방법도 있습니다.
특정 페이지나 컴포넌트에서 변경한 데이터가 클라이언트의 다른 위치에도 잘 반영되도록 해야 하는 경우(e.g. 프로필 정보를 수정했을 때 헤더에 노출되는 내 닉네임이 변경되어야 하는 등), 다시 말하면 mutation(POST, PATCH, DELETE 등)을 요청했을 때 해당 요청에 영향을 받는 다른 query(GET) 요청을 invalidate해야 하는 경우에는 useMutation의 onSuccess 함수와 invalidateQuery 등을 이용해서 작업합니다.
전역 상태 관리를 위해서는 보통 한 가지 방식을 이용하는 경우가 더 많습니다.
이 부분과 서버 상태와 클라이언트 상태의 구분에 대해서는 토요일에 다시 얘기해봐요!
react-query에 대해서는 maintainer 중 한 명인 tkdodo 블로그(https://tkdodo.eu/blog/tags/react-query)를 보시면 좋을 거 같아요! 번역본(https://highjoon-dev.vercel.app/tags/react-query/2)