Redux는 상태관리 라이브러리이다.
검색해봤을 때 주로 React와 연관되어 나오긴 하지만 React없이도 사용할 수 있는 라이브러리다.
React를 사용할 때 props를 이용해 상태를 자식 컴포넌트로 전달 시켜 줄 수 있는데, 자식 컴포넌트가 많다 보면(깊이가 깊어지면) 코드의 가독성도 떨어지거니와 매우 비효율적으로 구성되게 된다.
컴포넌트가 이렇게 구성 되어 있을 때 컴포넌트 8과 컴포넌트 4에만 사용되는 상태가 있다고 가정해보자.
이를 Redux를 사용하지 않고 props를 내려주는 것만으로 상태를 전달해주려면
컴포넌트 8-> 6-> 2-> 최상위를 거쳐 다시 상태를 최상위 -> 1-> 4로 내려줘야 한다.
이렇게 작성했을 때의 또 다른 단점으로는 컴포넌트 구조가 바뀌게 된다면 코드가 완전 꼬여버릴 수가 있다.
이럴 때 사용하는 것이 Redux 라이브러리로, 전역 상태를 관리할 수 있는 저장소인 Store를 제공하여 별도로 상태를 관리할 수 있게 해준다.
Redux의 구조를 먼저 살펴보자
1. 상태가 변경되어야 하는 이벤트가 발생하면, 변경될 상태에 대한 정보가 담긴 Action객체가 생성된다.
2. 이 Action객체는 Dispatch 함수의 인자로 전달된다.
3. Dispatch 함수는 Action객체를 Reducer 함수로 전달해준다
4. Reducer 함수는 Action객체의 값을 확인하고, 그 값에 따라 전역 상태 저장소 Store의 상태를 변경한다.
5. 상태가 변경되면 React는 화면을 다시 랜더링 한다.
Action -> Dispatch -> Reducer -> Store 순서대로 데이터가 흐르는 방식이다.
Action
Action은 어떤 액션을 취할 것인지 정의해 놓은 객체이다. 즉, 어떤 동작을 하는지 명시해 주는 역할이라고 생각하면 편하다.
- Action을 사용할 때 주의사항
- type은 필수로 지정해 줘야 한다
- 대문자와 Snake Case로 type명을 작성해 줘야 한다(ex. SET_NUMBER)
- 순수 자바스크립트 오브젝트여야 한다
//Action
const setNumber = (num) => {
return {
type: 'SET_NUMBER',
payload: num
//구체적인 값을 작성해야 할 때는 payload를 이용한다(선택사항)
}
}
이러한 형태로 작성 한다
Dispatch
Dispatch는 Reducer로 Action을 전달해주는 함수이다. Dispatch의 전달인자로 Action객체가 전달된다.
import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
// Action 객체를 직접 작성하는 경우
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );
// 액션 생성자(Action Creator)를 사용하는 경우
dispatch( increase() );
dispatch( setNumber(5) );
여기서 Dispatch를 반환하는 메서드가 있다.
그 중 useSeletor(), useDispatch()를 살펴보겠다.
- useDispatch() : 위 예시와 같이 Action 객체를 Reducer로 전달해 주는 Dispatch 함수를 변환하는 메서드
- useSeletor() : 컴포넌트와 state를 연결하여 Redux의 state에 접근할 수 있게 해주는 메서드.
import { useSelector } from 'react-redux'
const counter = useSelector(state => state)
console.log(counter) // 1
Reducer
Reducer는 Dispatch에게서 전달받은 Action객체의 type값에 따라서 상태를 변경시키는 함수이다.
If/else 문이나 switch문을 이용해 작성할 수 있다
이 때, Reducer는 순수함수여야 한다.외부 요인으로 인해 상태가 변경되는 일이 없어야 하기 때문.
const initialState = {
count: 1,
list: []
}
// Reducer를 생성할 때에는 초기 상태를 인자로 요구합니다.
const counterReducer = (state = initialState, action) => {
// Action 객체의 type 값에 따라 분기하는 switch 조건문.
switch (action.type) {
//action === 'INCREASE'일 경우
case 'INCREASE':
return state.count + 1
// action === 'DECREASE'일 경우
case 'DECREASE':
return state.count - 1
// action === 'SET_NUMBER'일 경우
// state라는 본래 객체를 건들이게 되면 불변성에 어긋나기 때문에 spread 사용
case 'SET_NUMBER':
return [...state.list, action.payload]
// 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
default:
return state.count;
}
}
// Reducer가 리턴하는 값이 새로운 상태가 된다.
여러개의 Reducer를 사용하는 경우 Redux의 combineReducers 메서드를 사용해서 하나의 Reducer로 합쳐줄 수 있다
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
counterReducer,
anyReducer,
...
});
Store
Store는 상태가 관리되는 오직 하나뿐인 저장소의 역할을 한다 (Redux 앱의 state가 저장되어 있는 공간)
createStore메서드를 활용해 Reducer를 연결해서 Store를 생성할 수 있다
** 최근 버전에서는 createStore을 지원하지 않아 삭제선과 함께 경고 메세지를 받을 수도 있다
import { createStore } from 'redux';
const store = createStore( );
Redux 의 세가지 원칙
1. Single source of truth
Redux에는 데이터를 저장하는 Store라는 단 하나뿐인 공간이 있다.
즉, 하나의 애플리케이션 안에는 단 하나의 스토어가 있다
2. State is read-only
상태는 읽기 전용이다. 즉, 불변성을 유지해야 한다
React에서 상태 갱신 함수로만 상태를 변경할 수 있었던 것처럼 Redux의 상태도 직접 변경할 수 없다.
(push를 직접하지 않고 spread 연산자(...)나 Object.assign 이용)
3. Changes are made with pure functions
리듀서는 순수함수여야 한다. (동일한 인풋이라면 동일한 아웃풋이 있어야 한다)
현재는 Redux의 단점을 보완하기 위해 Redux Toolkit이라는 툴이 새로 나왔다고 한다.
redux가 익숙해지면 Toolkit도 익혀봐야겠다
참고
https://react.vlpt.us/redux/01-keywords.html
'junior developer :) > React' 카테고리의 다른 글
react_웹 팩(webpack) 사용하여 번들링하기(번들링 과정 및 명령어) (0) | 2022.11.24 |
---|---|
번들링과 웹 팩 (webpack) (0) | 2022.11.23 |
CSS 방법론_SASS, BEM, OOCSS, SMACSS, Styled Components (with. CDD 방법론) (0) | 2022.10.27 |
React_hook library (State hook , Props) (0) | 2022.10.04 |
리엑트(React)는 무엇인가 (0) | 2022.09.29 |