Redux = 전역 상태 관리 라이브러리
: 소프트웨어 개발 도중 개발자들이 겪을 수 있는 여러 어려움들에 대한 일종의 모범 답안
바퀴를 재발명하지 마라
→ 대부분의 어려움은 이미 누가 겪었으니 그들이 패턴화 해둔 해결 방식을 적용해라
Model - View - Controller
: 프로젝트의 구성요소를 역할에 따라 model, view, controller로 나눠 작성하는 패턴
→ 관심사의 분리(SoC; Seperation of Concern)
ex) MVC ≒ state, jsx, setState
👎 Controller가 Model과 View 모두를 책임짐
controller가 model을 바꿈 → model이 바뀌었으니 view도 바꿔야함 → 그러다보니 다시 model도 바꿔야함 → 계속 반복...
양방향 바인딩 Two-Way Binding 으로는 controller가 어느 요소에 변화를 일으켰는지 알기 어려움
➡️ 그러다가 버그가 생긴다면 보수가 너무 어려움...!
MVC 구조로 인해 생긴 눈사태... 📘 Facebook도 못고치겠다!
프론트엔드 기술의 발달로 웹 애플리케이션이 고도화 됨
→ 규모에 맞는 안정적인 상태 관리 패턴이 필요
➡️ Flux는 애플리케이션의 예측 불가능성을 해결하고자 등장
단방향 바인딩 : 모든 상태 변화가 Action → Dispatcher → Store → View로 흐른다
≈ React
리덕스란❓
자바스크립트 앱을 위한 예측 가능한 상태 컨테이너
리덕스는 위와 같은 목적을 수행하려는 라이브러리입니다
- Flux : 디자인 패턴 = **개념**
- Redux : Flux 패턴을 이용해 자바스크립트에서 이용할 수 있도록 구현해 놓은 것 = **라이브러리**
데이터의 흐름이 복잡하기 때문에 데이터의 흐름을 제약해야함
: 상태 변화가 일어나는 시점과 형태에 제약 ➡️ 상태 변화가 예측 가능해짐
언제 Redux를 사용하면 좋을까?
: 써야하나 쓰지 말아야 하나? 긴가민가할 때는 필요 없을 때. 진짜 써야할 때면 이미 알고있다.
: 바닐라 리액트로 하다가 문제가 생기기 전까진 리덕스를 쓰지 마라.
애플리케이션의 모든 상태는 하나의 저장소 안에 하나의 객체 트리 구조로 저장된다.
리덕스의 상태값을 수정하는 유일한 방법은 액션 객체와 함께 dispatch를 호출하는 것
state는 직접적으로 수정하지 않는다 ≈ setState 함수
dispatch가 호출된 순서대로 상태를 변경 → 상태 변경 순서/내역을 쉽게 관리할 수 있음
Action 객체는 평범한 자바스크립트 객체, Store에 입력된 순서와 내용을 저장하면 나중에 그 과정을 재현할 수 있음
Reducer
이전 상태값과 액션 객체를 입력으로 받아 새로운 상태값을 만드는 순수함수
(state, action) => nextState
순수 함수
: 똑같은 입력이면, 똑같은 출력을 언제나 내는 함수
👍 테스트 코드를 작성하기 쉬움, 예측 가능함
➡️ 리덕스의 예측 가능함을 지키기 위해 모든 상태 변화는 순수 함수로 작성해야함
// 순수 함수
const addOne = (num) => num + 1;
// 불순한 함수!! 😡
const addRandom = (num) => num + Math.random();
불순한 함수 예시
fetch
등의 비동기 로직new Date()
Math.random()
실제 유저가 보는 화면, UI 컴포넌트
Action
상태 변화에 대한 의도를 표현하는 단순한 자바스크립트 객체
어떤 행위를 할 것인지, 어떻게 바꿀 것인지 정의
Action Creator
액션 만드는 애 = 정해진 틀에 맞게 Action을 리턴하는 함수
액션은 직접 만들지 말고, 크리에이터를 통해서만 만들어라!
→ 객체 형식으로 type과 payload가 있음
export const addCart = (item) => { // 액션 "생성 함수"
return {
type: "ADD_ITEM", // 액션 "객체"
payload: item,
};
};
Action은 Store에 담긴 데이터를 변형시킬 수 있는 유일한 방법이다!
store.dispatch
함수의 인자로 담겨 reducer에 어떻게 store를 변경시킬 것인지에 대한 정보를 제공하는 역할
Action
을 Reducer
까지 보내는 역할을 하는 함수
→ 반드시 동기적으로 처리됨
∵ Action이 들어온 순서대로 reducer에게 보내져야함 : 예측 가능성을 위해
(previousState, action) => newState
의 형식으로 새로운 값을 만듦
const INITIAL_STATE = [];
// redux/cart.js
export default function cartReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case "ADD_ITEM":
return [...state, action.payload]; // 이전 상태에 새로운 item을 추가
case "DELETE_ITEM":
return state.filter((product) => product.id !== payload.id);
default:
return state; // 해당 사항 없으면 이전 상태를 그대로 리턴
}
}
// redux/index.js
// 나눠서 쓰고 combine을 해준다 -> root reducer!
import { combineReducers } from "redux";
import cartReducer from "./cartReducer";
export default combineReducers({ cartReducer });