state와 props로 데이터의 상태 관리를 할 경우 props drilling현상이 발생하게 되어
많은 컴포넌트 사이에서 props찾기로 인해 불필요한 리소스가 발생한다는 문제점이 있었다.
하지만 react로 데이터의 상태 관리를 하게 될 경우 props drilling의 문제 뿐만 아니라 더 많은 단점이 있다. 그 단점은 다음과 같다
props drilling과 관련된 내용으로 해당 상태를 직접 사용하지 않는 최상위 컴포넌트와 그아래의 컴포넌트들도 상태 데이터를 갖는다.
props drilling이 갖는 문제로 프로퍼티 내려꽂기가 여러번 반복적으로 진행됨
애플리케이션이 복잡해질수록 데이터 흐름도 매우 복잡해진다.
컴포넌트 구조가 바뀔 경우 데이터의 흐름이 꼬이게 되어 데이터 흐름을 다시 대거 수정해야 한다.
이러한 문제점들을 보완하기 위해 상태 관리 라이브러리를 사용하는데
상태 관리 라이브러리의 가장 기본이라고 할 수 있는 Redux를 알아보기로 한다!
redux는 기존 react의 데이터 흐름과 같지 않고 스토어가 만들어지고 그 스토어 에서 바로 필요한 컴포넌트로 데이터가 전달 될 수 있도록 구성되어있다.
store에서 데이터를 관리하여 필요한 component에 바로 보내 줄 수 있는 시스템을 갖고 있다!
그렇다면 어떤 순서와 구조로 동적으로 변하는 데이터가 전달 되는지 확인 해 보도록 한다.
상태가 변경 되어야 하는 이벤트가 발생하면 변경 될 상태에 대한 정보가 담긴 기존 데이터의 Action 객체가 생성된다.
Action 객체는 Dispatch 함수의 인자로 전달된다. Dispatch함수는 Action 의 객체를 Reducer 함수로 전달한다.
Reducer함수는 Action객체의 값을 확인한 후 그 값에 따라 전역 상태 저장소 Store의 상태를 변경한다.
상태가 변경되면 변경된 데이터를 화면에 렌더링 한다.
즉, Action → Dispatch → Reducer → Store의 순서로 데이터가 단방향으로 흐르게 된다.
그렇다면 도식이 아닌 어떤 방식으로 코드가 구성되는지 알아보자!
Store는 상태가 관리되는 오직 하나뿐인 저장소이다.
createStore
메서드를 활용해서 Reducer를 연결하여 생성 할 수 있다.import { createStore } from 'redux';
const store = createStore(rootReducer);
Reducer는 Dispatch에게서 전달받은 Action 객체의 type
값에 따라서 상태를 변경시키는 함수
const count = 1
// Reducer를 생성할 때에는 초기 상태를 인자로 요구합니다.
const counterReducer = (state = count, action) => {
// Action 객체의 type 값에 따라 분기하는 switch 조건문입니다.
switch (action.type) {
//action === 'INCREASE'일 경우
case 'INCREASE':
return state + 1
// action === 'DECREASE'일 경우
case 'DECREASE':
return state - 1
// action === 'SET_NUMBER'일 경우
case 'SET_NUMBER':
return action.payload
// 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
default:
return state;
}
}
// Reducer가 리턴하는 값이 새로운 상태가 됩니다.
이 때, Reducer는 순수함수여야한다. 외부 요인으로 인해 기대한 값이 아닌 엉뚱한 값으로 상태가 변경되는 일이 없어야 하기 때문! 만약 여러 개의 Reducer를 사용 할 경우, Redux의 combineReducers
메서드를 사용해서 하나의 Reducer로 합쳐줄 수 있다.import { combineReducers } from 'redux';
const rootReducer = combineReducers({
counterReducer,
anyReducer,
...
});
Action어떤 액션을 취할 것인지 정의해 놓은 객체이다.
// payload가 필요 없는 경우
{ type: 'INCREASE' }
// payload가 필요한 경우
{ type: 'SET_NUMBER', payload: 5 }
type지정 : 해당 Action 객체가 어떤 동작을 하는지 명시해주는 역할을 하기 때문에
대문자와 Snake Case로 작성
필요에 따라 payload를 작성하여 구체적인 값을 전달
// payload가 필요 없는 경우
const increase = () => {
return {
type: 'INCREASE'
}
}
// payload가 필요한 경우
const setNumber = (num) => {
return {
type: 'SET_NUMBER',
payload: num
}
}
Dispatch는 Reducer로 Action을 전달해주는 함수
Dispatch의 전달 인자로 Action 객체가 전달 된다.
// Action 객체를 직접 작성하는 경우
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );
// 액션 생성자(Action Creator)를 사용하는 경우
dispatch( increase() );
dispatch( setNumber(5) );
Redux Hooks는 React-Redux에서 Redux를 사용할 때 활용할 수 있는 Hooks 메서드를 제공한다.
그 중에서 크게 useSelector()
, useDispatch()
이 두 가지의 메서드를 기억하도록 한다.
Action 객체를 Reducer로 전달해 주는 Dispatch 함수를 반환하는 메서드
import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
dispatch( increase() )
console.log(counter) // 2
dispatch( setNumber(5) )
console.log(counter) // 5
컴포넌트와 state를 연결하여 Redux의 state에 접근할 수 있게 해주는 메서드
// Redux Hooks 메서드는 'redux'가 아니라 'react-redux'에서 불러옵니다.
import { useSelector } from 'react-redux'
const counter = useSelector(state => state)
console.log(counter) // 1