[React] Redux

mynoseis3·2024년 3월 30일
0

react

목록 보기
6/9
post-thumbnail

Redux

리액트 애플리케이션의 상태 관리 라이브러리

상태란?

state(상태) : 컴포넌트 내부에서 관리되는 데이터

  • 리액트는 자식 컴포넌트들 간의 다이렉트 데이터 전달은 불가능 하다.
  • 자식 컴포넌트들 간의 데이터를 주고 받을 때는 상태를 관리하는 부모 컴포넌트를 통해서 주고 받는다.
  • 자식이 많아지면 상태 관리가 매우 복잡해진다.
  • 상태를 관리하는 상위 컴포넌트에서 계속 내려 받아야한다. Props drilling 이슈

props drilling(프로퍼티 내리꽂기)
하위 컴포넌트가 깊어지고 전달하는 컴포넌트가 많아질 수록 props 를 추적하거나 유지보수하기 어려워진다.
React-Redux 는 이런 props drilling 을 막고 멀리 떨어진 컴포넌트도 중앙 데이터 저장소, Store 에서 직접 전달할 수 있다.

리덕스를 쓰는 이유

리덕스를 사용하면 state를 컴포넌트에 종속시키지 않고, 상태 관리를 컴포넌트의 바깥에서 관리 할 수 있게 된다.

  1. 전역 상태 저장소 제공

  2. Props drilling 이슈 해결

리덕스 원칙

  • 애플리케이션 상태는 모두 한 곳에서 집중 관리된다. (동기화 필요 ✘)
  • 상태는 불변(읽기 전용) 데이터이고
    오직 액션 만이 상태 교체를 요청 할 수 있다. (예측 가능)
  • 리듀서(함수)를 통해 상태의 최종 값만 설정한다. (단순화)

작동 흐름

컴포넌트는 store의 값을 직접적으로 바꾸거나 요청하지 못한다.
Action은 앱에서 스토어에 운반할 데이터를 말한다. (주문서)
Action을 Reducer에 전달하기 위해서는 dispatch() 메소드를 사용해야한다.
(액션은 자바스크립트 객체 형식으로 되어있다.)
Reducer가 주문을 보고 Store의 상태를 바꿔준다.

ex)
로그인 컴포넌트에서 "로그인 하기" 라는 행동을 리듀서에게 던져주고
리듀서가 케이스(행동지침)에 따라 Store를 업데이트 한다.

Store값이 바뀌면 자동으로 component가 바뀌면서 리렌더링 된다.

  • useDispatch : 액션을 전달하는 훅
  • useSelector : Store에 있는 값을 가져다 쓸 때 사용하는 훅

리덕스 사용해보기

  • Redux Core 설치
    npm install redux

리덕스는 리액트에서만 쓰는 게 아니라 자바스크립트에서나 node.js 등 다양하게 사용되므로 리액트에서 리덕스를 사용할 땐 리액트-리덕스를 추가적으로 설치해주어야 한다.

  • React Redux 설치
    npm i react-redux

https://react-redux.js.org/ 참고

  • Provider 컴포넌트로 앱 감싸기
    index.js에서 Provider import / App 컴포넌트를 Provider로 감싸고 이때
    props로 store를 넘겨준다.
 <Provider store={store}>
    <App />
  </Provider>

아직 store를 정의하지 않아서 난 오류

https://ko.redux.js.org/introduction/getting-started 리덕스 문서 참고

  • Store 생성

store.js 파일 작성

import { createStore } from 'redux';
import reducer from './reducer/reducer';

// 앱의 상태를 보관하는 Redux 저장소 만들기
let store = createStore(reducer);

export default store;
  • 리듀서 함수 생성

reducer.js 파일 작성

let initialState = {
  count: 0,
};

function reducer(state = initialState, action) {}

export default reducer;
  • 다시 index.js 돌아가서 생성한 store import 하기
    import store from './redux/store';

  • useDispatch 훅 사용해서 reducer에 action 보내기

action을 보낼 땐 type은 필수적으로 작성하기
type: action의 이름

import logo from './logo.svg';
import './App.css';
import { useState } from 'react';
import { useDispatch } from 'react-redux';

function App() {
  const [count, setCount] = useState(0);
  const dispatch = useDispatch();

  const increase = () => {
    dispatch({ type: 'INCREMENT' });
    setCount(count + 1);
  };
  return (
    <div className="container">
      <div className="wrap">
        <h2>{count}</h2>
        <button onClick={increase}>증가!</button>
      </div>
    </div>
  );
}

export default App;
  • reducer.js에서 콘솔 찍어보기
let initialState = {
  count: 0,
};

function reducer(state = initialState, action) {
  console.log('action은 뭐야?', action);
}

export default reducer;

결과 - > action은 객체이다.

이제 reducer에서 이 INCREMENT라는 액션이 올 때마다 store를 업데이트해주면 되는데
그 역할을 하는 부분이 return 이다.
그래서 항상 reducer는 store에게 return을 해주어야 한다.

reducer.js

function reducer(state = initialState, action) {
  console.log('action은 뭐야?', action);
  if (action.type === 'INCREMENT') {
    return { ...state, count: state.count + 1 };
  }
    return { ...state }; // store는 return이 무조건 있어야 하므로
  						// reducer 함수에는 무조건 기본 리턴이 필요하다.

}

...state 은 만약 state가 여러개일 때 다른 state 값은 유지하되
count만 바꾸겠다는 뜻

....spread 문법을 통해 기존 객체 내용을 복사하여 새로운 객체에 전달 가능

store는 새로운 객체 주소를 전달받아야 자신이 업데이트 됐다는 걸 인식하게 된다.
헷갈리면 ...을 기본적으로 붙여주자요

이제 app.js에서 기존에 사용하던 state 부분을 없애고
Store에 있는 값을 가져다 적용하기 !!

useSelector import 하기

useSelector 훅에서 reducer 에 있는 state객체 안 count라는 값을 가져온다.
const count = useSelector((state) => state.count);

import './App.css';
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';

function App() {
  // const [count, setCount] = useState(0);
  const count = useSelector((state) => state.count);
  const dispatch = useDispatch();

  const increase = () => {
    dispatch({ type: 'INCREMENT' });
    // setCount(count + 1);
  };
  return (
    <div className="container">
      <div className="wrap">
        <h2>{count}</h2>
        <button onClick={increase}>증가!</button>
      </div>
    </div>
  );
}

export default App;

결과

count가 잘 업데이트되어 반영되는 걸 확인할 수 있다.

컴포넌트를 추가적으로 만들고 테스트해보면
이젠 자식 컴포넌트에서 원하는 값을 받을 때 부모 컴포넌트에게서 props를 넘겨 받아 사용하는 게 아니라 원하는 값을 useSelector 훅을 통해 직접 값을 가져와 사용할 수 있다.

payload 사용해보기

payload는 액션(Action) 객체의 속성 중 하나로
type과는 다르게 선택적인 요소이다.
데이터를 실어나르는 역할을 한다.

주로 리덕스에서 "payload"는 액션(Action) 객체의 속성 중 하나로 사용된다.
액션 객체는 해당 액션이 발생했음을 나타내는 유형(Type)과 함께 추가적인 데이터를 전달할 수 있는데, 이러한 추가 데이터가 "payload"이다.
예를 들어, 사용자가 특정 항목을 추가하는 액션을 생성할 때, 해당 항목의 데이터를 "payload"로서 액션 객체에 포함할 수 있다.

//app.js
  const increase = () => {
    dispatch({ type: 'INCREMENT', payload: { num: 5 } });
    // setCount(count + 1);
  };
//reducer.js
function reducer(state = initialState, action) {
  console.log('action은 뭐야?', action);
  if (action.type === 'INCREMENT') {
    return { ...state, count: state.count + action.payload.num };
  }
  return { ...state };
}

결과 5씩 증가쓰

로그인 버튼 생성, 로그인 함수에서 dispatch 사용해서 LOGIN이라는 액션과 payload에 id,pw 데이터를 넘겨주고 reducer에서 받아 store를 업데이트 해주면
로그인 버튼 클릭 시 해당 id,pw 값이 렌더링 된다.

나중에 마이페이지 같은 부분에서
로그인 정보같은 것을 보여줄 때도 활용해보면 좋을 것 같다.


https://velog.io/@seungsang00/React-Redux-%EA%B8%B0%EC%B4%88
https://ko.redux.js.org/introduction/getting-started/
https://junvelee.tistory.com/97
https://medium.com/@heoh06/%EB%A6%AC%EC%95%A1%ED%8A%B8-redux%EC%99%80-%EC%82%AC%EC%9A%A9%EB%B2%95-731853fc3cd4

profile
웹개발자 꿈나무 꾸준함의 힘을 믿습니다 🚶

0개의 댓글