Redux
: 상태 관리 라이브러리
기존의 리액트 방식
- 대로라면 Props를 통해서 부모 컴포넌트에서 자식 컴포넌트로만 정보를 전달할 수 있음
- 조부모 컴포넌트에서 손자 컴포넌트로 값을 보내고자 할때도 반드시 부모 컴포넌트를 거쳐야만 한다. 즉, 부모컴포넌트에서는 그 값이 필요가 없어도, 단순히 손자 컴포넌트로 전달하기 위해 불필요하게 거쳐야만 함! (조부모 → 부모 → 손자)
(이런 경우를 Props Drilling이라고 함)
- 자식 컴포넌트에서 부모 컴포넌트로 값을 보낼 수 없음
리덕스
: 어떤 컴포넌트든 쉽게 접근 가능
- State를 공유할 때 부-모 관계가 아니여도 되고, 중간에 의미없이 컴포넌트를 거치지 않아도 됨
- 자식 컴포넌트에서 만든 State를 부모 컴포넌트에서도 사용할 수 있게 됨

전역 상태 관리
지역상태 Local state
:컴포넌트에서 useState를 이용해서 생성한 state
전역상태 Global state
: Global state는 컴포넌트에서 생성되지 않음!!!

중앙 State관리소에서 State를 생성하고, 만약 어떤 컴포넌트에서 State가 필요하다면 컴포넌트가 어디에 위치하고 있든 상관없이 State를 불러와서 사용 할 수 있게 됨
이렇게 특정 컴포넌트에 종속되어 있는 것이 아니라 “중앙 state 관리소”에서 생성된 State
+ 이런 값들을 관리하는 것을 전역 상태 관리 라고 한다
Redux 설정
리덕스 설치)
yarn add redux react-redux
// redux와 react-redux를 같이 설치하겠다는 의미
// yarn add redux, yarn add react-redux 따로 설치 가능
// react-redux 패키지는 redux를 React에서 사용할 수 있게 해 주는 패키지!
폴더 구조 생성)

1. src 폴더 안에 redux 폴더를 생성
2. redux 폴더 안에 config, modules 폴더를 생성
3. config 폴더 안에 configStore.js파일을 생성합니다.
- redux : 리덕스와 관련된 코드를 모두 모아 놓을 폴더
- config : 리덕스 설정과 관련된 파일들을 놓을 폴더
- configStore.js : “중앙 state 관리소" 인 Store를 만드는 설정 코드들이 있는 파일
- modules : 만들게 될 State들의 그룹
* 설정코드는 지금 당장 이해할 필요가 없음. 코드 분석 x
설정 코드를 작성하는 이유는 리덕스를 만든 리덕스팀에서 이렇게 설정을 하라고 안내하고 있기 때문..!
* 카운터 프로그램
모듈 만들기 (모듈: State 그룹)
1. modules 폴더에 counter.js 파일을 생성
2. 코드 작성
// src/modules/counter.js
// 초기 상태값
const initialState = { // useState 초기값과 동일한 역할 + 객체, 배열, 원시데이터 모두 가능
number: 0,
};
// useState에서는 const [number, setNumber] = useState(0) 로 작성
// 리듀서 : 변화를 일으키는 함수
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
}; // const onClickHandler = () => { setNumber(number+1) }
// useState에서는 setNumber, 즉 setState로 state를 변경했다면
// redux는 reducer가 이런 역할을 대신한다
// 모듈파일에서는 리듀서를 export default
export default counter;
redux는 reducer를 포함한 Store라고 할 수 있음! (store: 상태를 저장)
어떤 action을 일으킨다 = dispatch
action을 일으켰을 때 reducer가 자동실행되고, 그 action에 맞게 데이터를 수정함
=> 리듀서reducer는 store에 있는 데이터를 바꿔주는 역할
3. 카운터 module을 store에 연결
import { createStore } from "redux"; // 원래 있던 코드
import { combineReducers } from "redux"; // 원래 있던 코드
import counter from "../modules/counter"; // 새롭게 추가
const rootReducer = combineReducers({
//이 부분에 추가한 counter module 넣기: store와 module 연결
counter: counter, // 새롭게 추가
});
const store = createStore(rootReducer);
export default store;
createStore() : 리덕스의 가장 핵심이 되는 스토어를 만드는 메소드(함수)
리덕스는 단일 스토어로 모든 상태 트리를 관리함
combineReducers() : 리덕스는 action —> dispatch —> reducer 순으로 동작한다
애플리케이션이 복잡해지게 되면 reducer 부분을 여러 개로 나눠야 하는 경우가 발생함
combineReducers은 여러 개의 독립적인 reducer의 반환 값을 하나의 상태 객체로 만들어줌
4. 스토어와 모듈 연결 확인
컴포넌트에서 스토어를 직접 조회하기! useSelector라는 ‘react-redux’의 훅을 사용
// src/App.js
import React from "react";
import { useSelector } from "react-redux"; // useSelector import하기
const App = () => {
const counterStore = useSelector((state) => state);
// 1. useSelector를 받아와서
// 2. 화살표함수 사용하고 state를 넣어줌
// const counterStore = useSelector(function(state){ return state; }); 와 똑같은 의미
// 화살표함수에서 꺼낸 state 인자:
// 프로젝트에서 존재하는 모든 redux module의 state를 가져오라는 뜻!
// const number = useSelector((state) => state.counter.number);
// state 안의 counter 안의 number만 조회
return <div></div>;
};
export default App;
+) useSelector의 사용법
// 1. store에서 꺼낸 값을 할당 할 변수를 선언
const number =
// 2. useSelector()를 변수에 할당
const number = useSelector()
// 3. useSelector의 인자에 화살표함수 넣어주기
const number = useSelector( ()=>{} )
// 4. 화살표 함수의 인자에서 값을 꺼내 return
const number = useSelector((state) => {
console.log(state)
return state
});
※ 정리)
보통 모듈은 기능의 이름을 따서 파일을 생성
모듈의 구성요소: initialState, Reducer
모듈을 만들면 스토어에 연결을 해야 하고, 연결은 configStore.js에서
컴포넌트에서 Store를 조회할 때는 useSelector를 사용
useSelector((state)⇒state) 에서 state는 모든 모듈의 state 를 조회할 수 있는 값!!
counter.js 모듈의 state 수정 기능 (+ 1 기능 구현하려면)
기존 리액트에서는 useState()를 사용해서 number에 +1을 할 때는 setNumber을 이용해서 +1을 해줌
// 예시 코드
// local state
const [number, setNumber] = useState(0)
// click handler
const onClickHandler = () => {
setNumber(number + 1)
}
리덕스에서의 값의 수정
: 리듀서reducer에서 일어남
1. 리듀서에게 보낼 number를 +1 하라는 “명령”을 만든다
리듀서에게 변경하라는 명령을 내리는데, 이런 명령을 redux에서는 action이라고 한다
리듀서에게 내가 어떤 Action을 하길 원한다, 라고 표현하면 리듀서가 그걸 실행시켜 주고, 이 행동을 코드로 나타내면 객체
// 예시 코드
//number에 +1 을 하는 액션 객체
{ type : "PLUS_ONE" };
그리고 이것을 액션 객체라고 함
(( 액션 객체는 반드시 type이라는 key를 가져야 한다. 왜냐하면 이 액션 객체를 리듀서에게 보냈을 때 리듀서는 객체 안에서 type이라는 key를 보기 때문 ))
2. 명령을 보낸다
액션객체를 보내기 리듀서로 보내기 위해서는 useDispatch 라는 훅hook을 사용
react-redux에서 import 해서 사용할 수 있으며, 우리가 만든 액션 객체를 리듀서로 보내주는 역할을 함
useDispatch를 사용하기 위해서는 컴포넌트 안에서 아래와 같이 먼저 코드를 작성해서 dispatch라는 변수를 생성해줘야 함. 이렇게 생성한 dispatch는 함수!!
그래서 dispatch를 사용할 때 () 를 붙여서 함수를 실행하는데
// src/App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux"; // useSelector import하기
const App = () => {
const dispatch = useDispatch(); // dispatch 생성
// 함수이기 때문에 실행할 때 괄호()를 붙임 : dispatch()
return (
<div>
<button>+1</button>
</div>
);
};
export default App;
() 안에 액션객체를 넣어주면 됨
// src/App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux"; // useSelector import하기
const App = () => {
const dispatch = useDispatch(); // dispatch 생성
// 함수이기 때문에 실행할 때 괄호()를 붙임 : dispatch()
return (
<div>
<button
onClick={() => {
dispatch({ type: "PLUS_ONE" });
}}> +1 </button></div>
); // dispatch() 괄호 안에 action객체 넣어서 사용
// dispatch를 통해 action객체를 reducer로 보내기!
// 버튼을 클릭했을 때 dispatch를 통해서 +1이라는 action을 실행한다는 뜻
};
export default App;
// src/modules/counter.js
// 초기 상태값
const initialState = {
number: 0,
};
// App.js - dispatch를 통해 action객체를 reducer로 보냄
// App.js에서 보낸 action 객체를 받을 수 있도록 구현하기
// 리듀서
const counter = (state = initialState, action) => {
console.log(action);
switch (action.type) {
default:
return state;
}
};
// 모듈파일에서는 리듀서를 export default 한다.
export default counter;
console.log(action)로 리듀서있는 action은 App.js에서 dispatch로 보낸 그 액션객체(PLUS_ONE)임을 확인 가능
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
3. 리듀서에서 명령을 받아 number +1을 한다
state에 있는 number를 실제로 변경하는 로직 추가
로직코드는 switch문으로 작성(reducer가 action객체를 받아 상태를 바꾸는 원리)
// src/modules/counter.js
// 초기 상태값
const initialState = {
number: 0,
};
// 리듀서
const counter = (state = initialState, action) => {
console.log(action);
switch (action.type) {
case "PLUS_ONE": // PLUS_ONE이라는 type을 가진 action을 실행
return { number: state.number + 1 };
// action 안에 있는 type을 switch문으로 하나씩 검사해서
// 일치하는 케이스를 찾음
// 이것 때문에 type이라는 key는 반드시 action객체를 포함해야 함!!
default:
return state;
} // type과 case가 일치하는 경우에 해당 코드가 실행되고,
// 실행돼서 나온 새로운 state를 반환 -> 새로운 module의 state가 됨
};
// 모듈파일에서는 리듀서를 export default 한다.
export default counter;
이후 ↓이 state ↓를 구독하고 있는 component에서 업데이트된 값을 받음
// src/modules/counter.js
// 초기 상태값
const initialState = {
number: 0,
};
userSelector를 사용해서 store 안에 있는 값이 제대로 변경됐는지, App.js에서 number를 확인하는 코드 추가
// src/App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
const App = () => {
const dispatch = useDispatch();
const number = useSelector((state) => state.counter.number);
console.log(number); // 콘솔로 number 확인 + dispatch한 action이 찍힘
return (
{/* 화면에 number 출력 */}
<div>
{number}
<button onClick={() => { dispatch({ type: "PLUS_ONE" }); }}> +1 </button>
</div>
);
};
export default App;
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
useState에서 만든 state가 변경되면 화면이 리렌더링되는 것과 마찬가지로, 리덕스에 존재하는 state도 값이 변경되면 useSelector를 하고 있는 컴포넌트들도 모두 다시 리렌더링된다 -> 화면상에서도 증가하는 것이 보임
+) - 버튼 구현하기
// src/App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
const App = () => {
const dispatch = useDispatch();
const number = useSelector((state) => state.counter.number);
console.log(number); // 콘솔로 number 확인 + dispatch한 action이 찍힘
return (
<div>
{number}
<button onClick={() => {dispatch({ type: "PLUS_ONE" })}}> +1 </button>
<button onClick={() => {dispatch({ type: "MINUS_ONE" })}}> -1 </button>
</div>
); {/* -1 버튼 추가 */}
{/* onClick할때 dispatch됨 */}
};
export default App;
// src/modules/counter.js
// 초기 상태값
const initialState = {
number: 0,
};
// 리듀서
const counter = (state = initialState, action) => {
console.log(action);
switch (action.type) {
case "PLUS_ONE":
return { number: state.number + 1 };
case "MINUS_ONE":
return { number: state.number - 1 };
// minus 버튼의 reducer 로직 구현
// switch문에 case 추가
default:
return state;
}
};
// 모듈파일에서는 리듀서를 export default 한다.
export default counter;
정리)
action 객체 : 반드시 type이란 key를 가져야 함 + reducer로 보낼 명령 + type의 value는 대문자로!!
dispatch : action객체를 reducer로 보내는 전달자 함수 + dispatch를 이용하기 위해선 useDispatch란 hook을 사용
useDispatch : store의 내장함수, action을 발생
reducer : dispatch를 통해 전달 받은 action 객체를 검사하고 조건이 일치했을 때 새로운 상태값을 만들어내는 변화를 만드는 함수
'스파르타 개발일지' 카테고리의 다른 글
| 20230116 타입스크립트 01 (0) | 2023.01.16 |
|---|---|
| 20221229 리액트 네이티브 시작! (0) | 2022.12.29 |
| 개발일지 20221212 리액트 숙련 강의 1 (0) | 2022.12.12 |
| 개발일지 20221210~11 todo-list 만들기 (0) | 2022.12.11 |
| 개발일지 20221208 리액트 입문 3 실습 (0) | 2022.12.08 |