#1. Redux
1. Redux의 기본 개념
1) Redux란?
- 리액트의 전역 상태 관리 라이브러리 이다.
- 리액트로 일반적인 컴포넌트 개발시에는 상태값(변수)을 관리하기 위해 라이프사이클이나 hook을 사용한다.
- 이 경우 각각의 컴포넌트가 관리하는 변수값들이 소스파일 여기저기에 흩어져 있기 때문에 코드 유지보수에 좋지 않다.
- 하지만 Redux를 사용하면 컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리시켜서 더욱 효율적으로 관리할 수 있다.
- 즉, 여러개의 컴포넌트가 개별적으로 관리하는 상태값들을 하나의 소스에 모아 놓고 통합 관리하는 것이 목적이다.
- 컴포넌트 끼리 상태를 고유해야 할 때도 여러 컴포넌트를 거치지 않고 손쉽게 상태 값을 전달하거나 업데이트 할 수 있다.
2) Redux의 기본 요소
- 상태값
- 액션 - 문자열
- 액션함수 - 리듀서 호출 함수
- 리듀서 - 상태값 갱신 함수
- 스토어 - 상태값 저장소
- 구독 - 상태값이 변경되었음을 감지하는 기능
- 디스패치 - 액션함수 호출
3) 패키지 설치
- react-redux
yarn add react-redux
- 리액트에서 Redux를 사용할 수 있도록 해주는 컨테이너
- Redux에 의존한다.
- @reduxjs/toolkit
yarn add @reduxjs/toolkit
- 리액트에서 리덕스를 좀 더 간결하게 사용할 수 있도록 하는 패키지
- redux-logger
yarn add redux-logger
- redux가 관리하는 상태값을 자동으로 로그에 표시하는 미들웨어
- redux-devtools-extension
yarn add redux-devtools-extension
- redux의 상태를 크롬브라우저 개발자도구에 설치된 확장 기능과 연동할 수 있게 해주는 미들웨어
※ yarn 에서 패키지 여러개 설치할 때는 띄어쓰기로 구분한다.
yarn add react-redux @reduxjs/toolkit redux-logger redux-devtools-extension
2. 리덕스 미들웨어
- 액션을 디스패치 했을 때 (slice의 저장되어 있는 함수를 호출, 흔히 CRUD) 리듀서에서 이를 처리하기에 앞어 실행되는 사전에 지정된 작업들을 말한다.
- ajax를 호출했을 때, ajax가 동작하기 전에 혹은 동작한 후에 자동으로 수행되게 만들어 놓은 작업들
- 미들웨어는 index.js에서 스토어를 생성하는 과정에서 적용한다.
1) 미들웨어로 수행하는 처리들
- 전달 받은 액션을 단순히 콘솔에 기록
- 전달받은 액션 정보를 기반으로 액션을 취소
- 다른 종류의 액션을 추가로 패치
2) 동작 순서
[사용자 이벤트] → [액션] → [미들웨어] → [리듀서(ajax를 실질적을 동작시킴) → [스토어](상태값 저장소)
3) 미들웨어 종류
- redux-logger: 브라우저 콘솔에 브라우저의 흐름을 자동으로 기록해준다.
- ajax가 실행될 때 어떤 함수가 실행되는지 자동으로 출력, ajax가 받아오는 응답결과를 콘솔에 자동으로 찍어준다.
- redux-devtools-extension
3. React에 Redux 적용하기
- 순정 Redux를 React에 적용하는 것은 매우 복잡한 처리를 요구하기 때문에 최신 버전에서는 Redux-Toolkit 이라는 라이브러리를 통하여 Redux 구조를 단순화 하고 있다.
1) Redux Store 준비하기
- 상태값, 액션, 액션함수, 리듀서, 스토어가 통합된 형태.
- 상태값을 관리한다.
- src/store.jsx
// store의 기본 구조
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({ // configureStore 호출
// 개발자가 직접 작성한 Slice 오브젝트들이 명시되어야 한다.
// reducer에 이름과 객체를 나열한다.
reducer: {
name: object,
name: object,
...
}
});
export default store;
- reduxjs/toolkit에서 configureStore(스토어를 설정하는 함수) 가져온다.
- configureStore 안에 상태값들을 나열하고 store라는 객체를 export한다.
2) React Store를 React에 구독시키기
- src / index.js
// 리덕스 구성을 위한 참조
import { Provider } from 'react-redux';
import store from './store';
- import한 store에는 store.jsx의 reducer 안에 상태값들이 들어있다.
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
- 렌더링 처리를 <Provider store={store}> 태그로 감싸준다.
- store의 상태값이 <App />을 통해서 모든 하위 컴포넌트에 전달된다. (하나의 상태값을 화면이 바뀌어도 사용가능!)
3) Slice 모듈 작성
- 액션값을 갱신하기 위한 실질적인 함수를 만든다.
- url 하나당 CRUD(입력,수정,삭제,조회)를 담당하는데, url 하나당 slice 하나 라고 보면 된다.
- src/slices/MySlice.jsx
import { createSlice } from '@reduxjs/toolkit';
const slice이름 = createSlice({
name: 'slice별칭',
initialState: {
// 이 모듈이 관리하고자 하는 상태값들을 명시
변수1: 값,
변수2: 값
},
reducers: {
// 상태값을 갱신하기 위한 함수들을 구현
// 컴포넌트에서 이 함수들을 호출할 때 전달되는 파라미터는 action.payload로 전달된다.
// initialState와 동일한 구조의 JSON을 리턴한다.
액션함수1: (state, action) => {...state},
액션함수2: (state, action) => {...state}
},
});
// 액션함수들 내보내기
export const { 액션함수1, 액션함수2 } = slice이름.actions;
// 리듀서 객체 내보내기
export default slice이름.reducer;
- initialState에 관리하고자 하는 상태값들을 명시한다.
- reducers는 상태값을 useState가 아닌 useReducer로 관리한다.
- 안에 함수들을 구현, state에는 initialState 값이 들어오고, 액션값으로 상태를 리턴 받아 다시 initialState로 보낸다.
- slice이름. actions로 내보내기하면 reducers의 액션 함수들이 export 된다.
※ 상태값을 리턴 받을 때는 action을 사용하고, export할 때는 actions를 사용함에 주의! (뒤에 s가 있고 없고 차이)
※ 상태값을 갱신할 때는 reducers 를 사용하고, export할 때는 reducer를 사용함에 주의! (뒤에 s가 있고 없고 차이)
- src/store.jsx
- 정의한 Slice 모듈 명시
import { configureStore } from '@reduxjs/toolkit';
import slice이름 from './slice/MySlice';
const store = configureStore({ // configureStore 호출
// 개발자가 직접 작성한 Slice 오브젝트들이 명시되어야 한다.
// reducer에 이름과 객체를 나열한다.
reducer: {
slice별칭: slice이름,
}
});
export default store;
- MySlice.jsx에서 작성한 별칭을 KEY로 사용하고, 객체 이름을 값으로 지정한다.
4) 컴포넌트 사용하기
- 필요한 기능 참조
// 상태값을 로드하기 위한 hook과 action함수를 dispatch할 hook 참조
import { useSelector, useDispatch } from 'react-redux';
// Slice에 정의된 액션함수들 참조
import { 액션함수1, 액션함수2 } from '../slice.MySlice';
- 컴포넌트 내부에서 hook을 통해 필요한 Object 생성
// hook을 통해 slice가 관리하는 상태값 가져오기
const { 변수1, 변수2 } = useSelector((state) => state.sclice별칭);
// dispatch 함수 행성
const dispatch = useDispatch();
- useSelector를 사용해서 state를 콜백으로 받고, MySlice.jsx의 slice별칭을 지정하면, initialState에 지정한 변수값을 가져올 수 있다.
- dispatch는 slice 안에 있는 액션함수들을 호출 할 수 있다.
- 필요한 이벤트 핸들러 안에서 액션함수 디스패치하기
- Slice에서 정의한 액션함수의 action.payload 파라미터로 전달된다.
- 다수의 파라미터가 필요한 경우 JSON 객체로 묶어서 전달한다.
dispatch(액션함수1(파라미터));
dispatch(액션함수2(파라미터));
4. 실습
- index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
// 리덕스 구성을 위한 참조
import { Provider } from 'react-redux';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
- App.jsx
import React from 'react';
// Counter.jsx 참조
import Counter from './pages/Counter';
function App() {
return (
<div>
<Counter />
</div>
);
}
export default App;
- CounterSlice.jsx
// createSlice 함수 참조
import { createSlice } from '@reduxjs/toolkit';
// TODO: Slice 정의 (Action함수 + Reducer의 개념)
const counterSlice = createSlice({
name: 'counter',
// 이 모듈이 관리하고자 하는 상태값들을 명시
initialState: {
// 여기서는 2개의 상태값을 관리한다.
number: 0,
color: '#000',
},
// 내부 action 및 동기 action
// 상태값을 갱신하기 위한 함수들을 구현
// 컴포넌트에서 이 함수들을 호출할 때는 전달되는 파라미터는 action.payload로 전달된다.
// initialState와 동일한 구조의 JSON을 리턴한다.
reducers: {
plus: (state, action) => {
const numberValue = state.number + action.payload;
let colorValue = '#000';
numberValue > 0 ? (colorValue = '#2f77eb') : (colorValue = '#f60');
return { number: numberValue, color: colorValue };
},
minus: (state, action) => {
const numberValue = state.number + action.payload;
let colorValue = '#000';
numberValue > 0 ? (colorValue = '#2f77eb') : (colorValue = '#f60');
return { number: numberValue, color: colorValue };
},
},
});
export const { plus, minus } = counterSlice.actions;
// 리듀서 객체 내보내기
export default counterSlice.reducer;
- Counter.jsx
import React, { memo, useEffect } from 'react';
// 상태값을 로드하기 위한 hook과 action 함수를 dispatch 할 hook 참조
import { useSelector, useDispatch } from 'react-redux';
// Slice에 정의된 액셤함수들 참조
import { plus, minus } from '../slices/CounterSlice';
const Counter = () => {
// 컴포넌트가 마운트 될 때 콘솔의 모든 내용 삭제함(출력 결과가 복잡해 지는 것을 방지)
useEffect(() => console.clear(), []);
// hook을 통해 slice가 관리하는 상태값 가져오기
const { number, color } = useSelector((state) => state.counter); // store에 있는 counter를 가져옴
// dispatch 함수 생성
const dispatch = useDispatch();
return (
<div>
<button onClick={(e) => { dispatch(plus(5)); }}>
+5
</button>
<button onClick={(e) => { dispatch(minus(-3)); }}>
-3
</button>
<h2 style={{ color: color }}>
{number}
</h2>
</div>
);
};
export default memo(Counter);
- store.jsx
// configureStore함수 참조
import { configureStore } from '@reduxjs/toolkit';
// CounterSlice 컴포넌트 참조
import counterSlice from './slices/CounterSlice';
const store = configureStore({
reducer: {
// 개발자가 직접 작성한 reducer들이 명시되어야 한다.
counter: counterSlice,
},
});
export default store;
※ 출력결과
'국비수업 > React' 카테고리의 다른 글
[React/Redux] 비동기 처리 (0) | 2022.05.23 |
---|---|
[React] Axios-hooks (0) | 2022.05.17 |
[React] Axios 사용하기 (0) | 2022.05.15 |
[React] Hooks (0) | 2022.04.29 |
[React] CSS 사용하기 (0) | 2022.04.26 |