[React] 리덕스 (Redux)란 ?
리덕스(Redux) 란?
공식문서를 참조한 정의는 다음과 같다.
자바스크립트로 구동되는 어플리케이션에서 예측 가능한 상태관리를 도와주는 상태관리
라이브러리
그런데, React의 setState, Hooks의 useState로만 사용해서 구현해도 충분히 상태관리에 이상이 없는데
굳이 리덕스를 사용하는 이유는 무엇일까?
우리가 알고 있는 리액트에 상태관리는 각 컴포넌트 안에서 이루어지는데, 각 컴포넌트에 필요한 상태에 대해서는
각각의 컴포넌트 내에서 선언되고 수정된다.
Redux는 (Flux 패턴을 활용) 전역상태를 관리하기 위한 라이브러리이다.(React,Vue,Angular에서 모두 사용 가능)
예를 들어 위 그림과 같이 컴포넌트 구성이 있다고 가정하고, 자식 컴포넌트6에서 자식 컴포넌트8로 상태를 전달해야 한다면 다음과 같은 순서로 상태가 전달되야 한다.
자식 컴포넌트2 -> 부모 컴포넌트 -> 자식 컴포넌트4 -> 자식 컴포넌트8
따라서 상대가 여러 컴포넌트에서 사용될 경우 부모컴포넌트를 통해 전달이 된다. 이러한 방식을 규모가 작은 프로젝트나 상태 개수가 많지 않을 때 별 문제가 없지만. 규모가 커짐에 따라서는 상태를 관리하기 어렵다.
이러한 문제를 해결하기 위해서 상태 관리 라이브러리인 리덕스(Redux)를 사용한다.
여기서 잠깐!!
Flux란?
- 기존의 많이 사용했던 MVC패턴의 문제점을 느끼고 페이스북에서 개발한 하나의 개발 패턴이다.
- 단순한 데이터 흐름으로 예측할 수 있는 상태의 코드를 만들기 위해 만들어진 패턴이다.
기존의 MVC모델의 양방향 데이터 방식은 모델의 변화에 따라서 바로바로 뷰가 변하기 떄문에 시스템이 복잡해질 수록
예측 불가능한 상황들이 생기면서 데이터들이 꼬여버리는 단점을 가지고 있었다.
업데이트할 Modal들이 다수고, 서로 의존성이 얽혀있을 경우에 자주 발생하게 된다.
Flux패턴은 MVC의 양방향 데이터 바인딩의 단점을 해결하고자 페이스부겡서 고안한 단방향 데이터 바인딩
방식의 디자인 패턴이다. 구조는 다음과 같다.
- Dispatcher: Flux의 모든 데이터 흐름을 관리하는 허브 역할을 하는 부분
- Store: 데이터(상태)를 저장하는 부분
- View: Store의 변화를 감지하고 View를 업데이트해주는 부분, Controller View라고도 부른다.
출처:https://taegon.kim/archives/5288
즉, "Action이 dispatch>dispatcher는 action에 맞게 store를 업데이트하고>View는 store의 변화가 감지되면 view 업데이트"의 방식으로 진행된다. 대표적으로 React와 Redux가 flux패턴을 사용하고, Vuex라이브러리에서도 flux패턴에
영감을 받아 만들어 졌다고 한다.
Redux와 Flux의 차이
- Flux와 대비되는 Redux의 주요 특징은 하나의 스토어와 리듀서 그리고 불변이라는 개념이다.
Redux를 쓰는 이유
- 프로젝트의 규모가 커질때 local state의 전달이 어렵다.
- global state의 유지가 어렵다. 예) 로그인 후 유저의 인증정보를 유지할 경우
- 상태를 예측 가능하게 만들어줌.
- 유지보수에 좋음
- 디버깅에 유리함 (Action과 state log 기록시)
- 테스트를 붙이기 쉽다
Redux의 3가지 특징
1. Single source of truth
- 모든 상태는 하나의 저장소(store)안에 하나의 객체 트리 구조로 저장된다.
- 하나의 어플리케이션 안에는 하나의 스토어가 있다.
- 스토어를 여러개 생성시 상태를 관리해서는 안된다. 그 대신 리듀소를 여러 개 만들어 관리 가능
- 어플리케이션의 모든 상태는 하나의 저장소 안에서 하나의 객체 트리 구조로 저장
2. State is read-only
상태는 읽기전용(불변) 데이터이며, 오직 액션만 상태를 변경할 수 있다.(예측 가능)
3.변화를 일으키는 함수, 리듀서는 순수 함수여야 한다.
순수함수: 반환(reture)값이 전달 인자(argument) 값에만 의존하는 함수.
전달받은 매게변수 state,action을 변형하지 않아야 하고, 반드시 새로운 상태를 반환 해야한다.
Redux의 기본 개념
Action
✏️ 상태 변화를 일으킬 때 참조하는 객체(애플리케이션에서 저장소로 보내는 데이터 묶음)
자바스크립트 객체
- 액션은 반드시 어떤 형태의 액션이 실행될지 나타내는 type 속성을 가져야 한다.
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO, // 반드시 type이 존재 해야한다. 이 외는 자유
text: 'Build my first Redux app'
}
// Action(액션) 생성자 => 액션을 만드는 함수 (액션과 액션 생성자는 다름)
function addTodo(text) {
return {
type: ADD_TODO,
text: '리덕스 배우기'
}
}
Reducer
✏️ 상태를 변화시키는 로직이 담긴 함수. 현재 상태와 Action을 이용해 다음 상태를 만들어 낸다.
- reducer는 액션(객체)를 받아 새로운 state(객체)를 반환하는 역할을 한다.
- reducer는 인자를 두개 받는다 (이전 상태(previous state), 두번째 인자는 액션(action))
- 액션이 어떻게 변경을 시키는지 Switch & case로 정의된다.
<주의할 점>
- 인수들을 변경해서는 안됨.
- API호출이나 라우팅처럼 사이드 이펙트를 야기하는것들을 사용해선 안된다.
- Date.now()나 Math.random() 같이 순수하지 않은 함수를 호출해서는 안된다.
- spread syntax나 assagin을 사용한 새로운 객체를 리턴해야한다.
function reducer(state, action) {
// 상태 업데이트 로직
return alteredState;
}
// counter를 위한 함수
function counter(state, action) {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
}
Store
✏️ 애플리케이션의 상태 값들을 내장하고 있다 (저장소). 상태가 관리되는 오직 하나의 공간
<store가 하는 일>
- 애플리케이션의 상태를 저장한다.
- getState()를 통해 상태에 접근할 수 있게 해준다.
- dispatch(action)을 통해 상태를 수정할 수 있게 해준다.
- subscribe(listner)를 통해 리스너를 등록한다.
import { createStore } from 'redux'
import todoApp from './reducers'
//
let store = createStore(todoApp)
Dispatch
✏️ Store의 내장함수 중 하나이며, 액션을 발생시키는 것 (액션을 파라미터로 전달 => dispatch(Action))
- 액션 객체를 넘겨 상태를 업데이트하는 유일한 방법이다.
- 함수에서 액션을 파라미터로 전달하는데 그것을 디스패치 액션 이라한다.
Subscribe
✏️ Store의 내장함수 중 하나이며, 함수 형태의 값을 파라미터로 받아옴
- subscribe 함수에 특정 함수를 전달하면, 액션이 디스패치 되었을때 마다 전달해준 함수가 호출된다.
추가적으로 비동기를 지원하는 Redux Thunk 와 Redux Saga 그리고 다양한 미들웨어가 존재한다.
참고 자료