🐕 redux
리덕스는 컴포넌트의 상태 업데이트 관련 로직을
다른 파일로 분리 시켜서 효율적으로 관리할 수 있도록 도와주는 라이브러리입니다.
또한 컴포넌트끼리 똑같은 상태를 공유해야 할 때에도
여러 컴포넌트를 거치지 않고 손쉽게 상태값을 전달하거나 업데이트 할 수 있게 도와줍니다.
리덕스 라이브러리는 위와 같은 설명을 보았을때 알 수 있듯이
전역 상태를 관리할 때 매우 효과적인 대안 중 하나입니다.
ContextAPI를 기반으로 만들어진 리덕스는 일반적인 전역 상태 관리를 위한 목적으로만 사용한다면
ContextAPI를 이용하여서도 충분히 비슷하게 구현할 수 있습니다.
ContextAPI에 대한 내용을 정리한 포스트가 있으니
ContextAPI가 궁금하시다면 아래 링크를 참고하세요
https://xionwcfm.tistory.com/238
하지만 리덕스를 사용하면 상태를 더욱 체계적으로 관리할 수 있어지며
코드의 유지 보수성을 높여주고 작업 효율도 극대화 시켜줍니다.
게다가 개발자 도구, 미들웨어 기능도 제공해주어 비동기 작업에도 유용합니다.
따라서 리덕스의 장점을 정리하면 아래와 같이 표로 만들 수 있을 것입니다.
리덕스의 장점 |
전역 상태를 효과적이며 체계적으로 관리 할 수 있다. |
코드의 유지보수성을 높여준다. |
편리한 개발자 도구를 지원해준다. |
미들 웨어 기능을 제공해준다. |
👻알아두면 좋은 keyword
1. action
상태에 어떤 변화가 필요하면 action 이란 것이 발생합니다.
이는 하나의 객체로 표현되며 이 action 객체는 다음과 같은 형식으로 이루어집니다.
{
type : "TOGGLE_VALUE"
}
액션 객체는 type 필드를 반드시 가지고 있어야 하며
이 type 필드에 담긴 값을 액션의 이름이라고 생각할 수 있습니다.
그 외의 값들은 나중에 상태 업데이트를 할 때 필요한 값들이며
개발자의 마음대로 작성할 수 있습니다.
예컨대 이렇게도 작성할 수 있을것입니다.
{
type : "ADD_TODO",
data: {
id:1,
text:"리덕스배우기"
}
}
요렇게요!
뭔가 타입값.. 액션객체.. 이러니까 useReducer 생각이 나네요
2. action 생성 함수
액션 생성함수는 액션 객체를 만들어주는 함수입니다.
function addTodo(data) {
return {
type: 'ADD_TODO',
data,
};
}
요렇게요!
리턴으로 액션객체를 뱉어주니까 액션생성 함수인것
이런 액션생성 함수는 어떠한 변화를 일으켜야 할 때마다 액션 객체를 만들어줘야하는데
그걸 하기 귀찮기도하고 사람이 하다보면 실수를 할 수도 있으니
그런 불상사를 방지하기 위하여 액션객체를 생성해주는 함수를 만들어 관리합니다.
3. 리듀서 reducer
리듀서는 변화를 일으키는 함수 입니다.
액션을 만들어서 발생시키면 리듀서가 현재 상태와 전달받은 액션 객체를 파라미터로 받아와요!
그리고 두 값을 참고해서 새로운 상태를 만들어 반환해줍니다.
완전 유즈리듀서 훅이랑 비슷하게 생겼네요
const initialState = {
counter: 1,
};
function reducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return {
counter: state.counter + 1,
};
default:
return state;
}
}
이건 완전 유즈리듀서랑 판박이네요
4. store 스토어
프로젝트에 리덕스를 적용하기 위해서 스토어를 만들 필요성이 있습니다.
한 개의 프로젝트는 단 하나의 스토어만 가질 수 있습니다.
즉 한 프로젝트에는 여러개의 스토어가 있으면 안된다는 뜻
스토어 안에는 현재 애플리케이션 상태와 리듀서가 들어가 있으며
그 외에도 몇가지 중요한 내장 함수들이 있습니다.
5. dispatch 디스패치
디스패치는 스토어의 내장 함수 중 하나입니다.
디스패치는 "액션을 발생시키는 것"이라고 이해하면 편한데
이 함수는 dispatch(action)과 같은 형태로 액션 객체를 파라미터로 넣어서 호출합니다.
이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜서 새로운 상태를 만들어줍니다.
6. 구독
구독 또한 스토어의 내장 함수 중 하나입니다.
subscribe 함수 안에 리스너 함수를 파라미터로 넣어서 호출해 주면
이 리스너 함수가 액션이 디스패치되어 상태가 업데이트 될 때마다 호출됩니다.
const listener = () => {
console.log('상태가업데이트됨')
}
const unsubscribe = store.subscribe(listener);
unsubscribe()// 구독을 비활성화 할 때 함수를 호출함
요런 식으로 사용하는 거라고 하는데 hmm 써봐야 알 것 같네요
🥶 리덕스는 리액트에 종속되지 않는다.
리덕스는 리액트에서 사용하려고 만들어지긴 했지만 다른 라이브러리/프레임워크와
함께 사용하는 것도 가능합니다.
따라서 리덕스는 바닐라 자바스크립트와도 함께 사용하는게 가능합니다.
🌞그러니까 바닐라자스로 리덕스 써보자
npm install -g parcel-bundler
mkdir vanilla-redux
cd vanilla-redux
yarn init -y
parcel index.html
yarn add redux
파셀번들러를 먼저 설치해주겠습니다
parcel이 뭔데..? 싶어서 물어보니
Parcel은 웹 애플리케이션 구축 및 배포 프로세스를 간소화해주는 오픈 소스 웹 애플리케이션 번들러다.
라고 설명을 해주네요
html,css,js 및 이미지를 포함해서 웹 애플리케이션의 종속성을 자동으로 묶어준다고합니다.
또한 핫모듈 교체를 지원해줘서 개발자가 실시간으로 코드를 변경하면
변경사항이 브라우저에 즉시 반영된다고 합니다.
요 핫모듈 교체 지원해주는 부분이 매력적이어서 깔아보라고 하는 것 같아요
parcel 명령어로 index.html을 실행해보면 개발용 서버가 실행됩니다.
이렇게요!
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="toggle"></div>
<hr>
<h1>0</h1>
<button id="increase">+1</button>
<button id="decrease">-1</button>
<script src="./index.js"></script>
</body>
대충 이런 증가,감소버튼이 있는 html을 작성해줬습니다.
const { createStore } = require('redux');
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');
const TOGGLE_SWITCH = 'TOGGLE_SWITCH';
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';
const toggleSwitch = () => ({ type: TOGGLE_SWITCH });
const increase = (difference) => ({ type: INCREASE, difference });
const decrease = () => ({ type: DECREASE });
const initialState = {
toggle: false,
counter: 0,
};
function reducer(state = initialState, action) {
switch (action.type) {
case TOGGLE_SWITCH:
return {
...state,
toggle: !state.toggle,
};
case INCREASE:
return {
...state,
counter: state.counter + action.difference,
};
default:
return state;
}
}
const store = createStore(reducer);
const render = () => {
const state = store.getState();
if (state.toggle) {
divToggle.classList.add('active');
} else {
divToggle.classList.remove('active');
}
counter.innerText = state.counter;
};
render();
store.subscribe(render);
divToggle.onclick = () => {
store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
store.dispatch(decrease());
};
리듀서 구조로 js를 만들어주었는데
리듀서 함수가 맨 처음에 호출 될 때에는 state값이 undefined 이기 때문에
해당값이 undefined일때엔 initialState를 사용해주도록 했습니다.
initialState의 형태는 자유롭지만 우선 객체로 지정해줬습니다.
리듀서에서는 상태의 불변성을 유지하면서 데이터에 변화를 일으켜야하기때문에
스프레드 연산자 등으로 복사를 한 뒤 데이터에 변화를 주어야합니다.
😋리덕스의 세가지 규칙
1. 단일 스토어
앞서 말한 것처럼 하나의 프로젝트에는 하나의 스토어가 있어야합니다.
여러개의 스토어를 사용하는 것이 완전히 불가능하지는 않지만
상태 관리가 복잡해질 위험이 있으므로 권장되지 않는 방법입니다.
2. 읽기 전용 상태
리덕스의 상태는 읽기 전용입니다. 따라서 리액트에서와 마찬가지로
불변성에 신경을 써주어야합니다.
이렇게 불변성에 신경을 써주어야하는 이유는 내부적으로 데이터가 변경되는 것을
감지하기 위해 얕은 비교 검사를 하기 때문이라고 하네요!
객체의 변화를 감지할 때 객체의 깊숙한 안쪽까지 다 비교하는게 아니라
얕은 깊이까지만 비교해서 좋은 성능을 유지할 수 있다
3. 리듀서는 순수한 함수여야 한다.
리듀서 함수는 순수 함수여야 합니다. 순수함수는 함수형프로그래밍에서도 나왔던 개념으로 기억하는데
🤗리듀서 함수는 이전 상태와 액션 객체를 파라미터로 받는다.
🤗파라미터 외의 값에는 의존하면 안된다.
🤗이전 상태는 절대 건드리지 않고 변화를 준 새로운 상태 객체를 만들어서 반환해야한다.
🤗똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과 값을 반환해야 한다.
위 네가지 주의사항을 지켜줘야 합니다.
예컨대 리듀서 함수 내부에서 랜덤 값을 만든다든지 Date함수를 쓴다든지
네트워크 요청을 한다든지와 같은 작업을 하면
항상 같은 인풋이 같은 결과값을 보장해줄 수 없기 때문에 사용해선 안된다는 것입니다.
하지만 이런 작업들은 우리가 코드를 짜면서 많이 사용하게 되는 작업이긴하니까..
이런 부분들은 리듀서 함수의 바깥에서 처리해 주면 됩니다.
액션객체를 만드는 과정에서 처리를 해줘도 되고
리덕스 미들웨어에서 처리를 해줘도 되는데
네트워크 요청과 같은 비동기 작업은 주로 미들웨어를 통해 관리합니다.
🐶마치며
'redux' 카테고리의 다른 글
RTK Query 데이터에 local Storage를 사용하는 방법 (0) | 2023.05.15 |
---|---|
RTK Query의 Middleware에 대하여 (0) | 2023.05.14 |
updateQueryData RTKquery로 클라이언트에서만 쿼리 데이터를 조작하고 싶다면 (1) | 2023.05.14 |
예제와 함께 TS ReduxToolkit Query Slow start (1) | 2023.05.01 |
Redux와 Redux-toolkit (0) | 2023.04.15 |