연습장

redux 본문

기타

redux

js0616 2024. 7. 30. 04:20

Redux는 JavaScript 상태 관리 라이브러리 입니다.

 

필요성:

react 에서 state 는 component 안에서 관리됩니다.

리액트 특성상 단방향 데이터 바인딩이기 때문에, 하위 컴포넌트에게 state를 전달 할 수 있습니다.

이를 props 라고 하며, component 구조가 깊어지면 props 를 계속 전달해야 하는 'Props drilling' 이슈가 있습니다.

또한 props 되는 component 가 많아지면 state 관련 오류 발생시 조치하기 어려워지는 문제점도 있습니다.

이때, redux를 사용하여 state를 관리하면 위와 같은 문제를 개선 할 수 있습니다.

 


 

예시코드 .gpt

 

Redux 를 사용해서 버튼 숫자 count 를 state 로 관리하고 '좋아요' 버튼 클릭시 count 가 1 증가하며, '싫어요' 버튼 클릭시 count 가 1 감소하는 코드 만들어줘

// 커널
npm install redux react-redux
 

 

폴더 구조

my-app/
├── public/
│   ├── index.html
│   └── ...
├── src/
│   ├── actions/
│   │   ├── actionTypes.js
│   │   └── actions.js
│   ├── components/
│   │   └── ButtonCounter.js
│   ├── reducers/
│   │   └── reducers.js
│   ├── store/
│   │   └── store.js
│   ├── App.js
│   └── index.js
└── package.json

 

// actionTypes.js
export const INCREMENT_COUNT = 'INCREMENT_COUNT';
export const DECREMENT_COUNT = 'DECREMENT_COUNT';

 

// actions.js
import { INCREMENT_COUNT, DECREMENT_COUNT } from './actionTypes';

export const incrementCount = () => ({
  type: INCREMENT_COUNT
});

export const decrementCount = () => ({
  type: DECREMENT_COUNT
});

 

// reducers.js
import { INCREMENT_COUNT, DECREMENT_COUNT } from '../actions/actionTypes';

const initialState = {
  count: 0
};

const countReducer = (state = initialState, action) => {
  switch (action.type) {
    case INCREMENT_COUNT:
      return {
        ...state,
        count: state.count + 1
      };
    case DECREMENT_COUNT:
      return {
        ...state,
        count: state.count - 1
      };
    default:
      return state;
  }
};

export default countReducer;

 

// store.js
import { createStore } from 'redux';
import countReducer from '../reducers/reducers';

const store = createStore(countReducer);

export default store;

 

// ButtonCounter.js
import React from 'react';
import { connect } from 'react-redux';
import { incrementCount, decrementCount } from '../actions/actions';

const ButtonCounter = ({ count, incrementCount, decrementCount }) => {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementCount}>좋아요</button>
      <button onClick={decrementCount}>싫어요</button>
    </div>
  );
};

const mapStateToProps = state => ({
  count: state.count
});

const mapDispatchToProps = {
  incrementCount,
  decrementCount
};

export default connect(mapStateToProps, mapDispatchToProps)(ButtonCounter);

 

// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store/store';
import ButtonCounter from './components/ButtonCounter';

const App = () => {
  return (
    <Provider store={store}>
      <div className="App">
        <ButtonCounter />
      </div>
    </Provider>
  );
};

export default App;

 

 


 

Redux Toolkit 사용해서 다시 알려줘

 
npm install @reduxjs/toolkit
 

 

my-app/
├── public/
│   ├── index.html
│   └── ...
├── src/
│   ├── components/
│   │   └── ButtonCounter.js
│   ├── reducers/
│   │   └── counterSlice.js
│   ├── store/
│   │   └── store.js
│   ├── App.js
│   └── index.js
└── package.json

 

action 이 없어졌음!

Redux Toolkit을 사용하면 코드를 간결하게 유지할 수 있으며, createSlice가 액션 생성자 함수를 자동으로 생성해주기 때문에 명시적으로 정의하지 않아도 되는 장점이 있습니다.

 

적용 과정

  1. store 설정  
  2. reducers 작성
  3. component 에서 사용
  4. App.js 에  Redux 스토어 적용

 

1. 스토어 설정

Redux Toolkit에서는 configureStore 함수를 사용하여 Redux 스토어를 설정합니다.

이 함수는 기본적으로 Redux DevTools Extension과 함께 동작하며, 필요한 경우 미들웨어를 추가할 수 있습니다.

 

store.js

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../reducers/counterSlice'; // 예시로 counterSlice 리듀서를 사용하겠습니다.

const store = configureStore({
  reducer: {
    counter: counterReducer
  }
});

export default store;

 

 

  • configureStore 함수는 Redux 스토어를 설정하는 역할을 합니다.
  • reducer 객체를 통해 각 슬라이스(리듀서)를 등록합니다.
  • 위 예시에서는 counterReducer를 counter라는 이름으로 등록했습니다.
  • counterReducer는 counterSlice에서 생성한 리듀서입니다.

 

 

 

2. 리듀서 작성

Redux Toolkit에서는 createSlice 함수를 사용하여 리듀서와 액션을 한 번에 정의할 수 있습니다.

이 함수는 Immer 라이브러리를 사용하여 불변성을 유지하면서 상태를 업데이트할 수 있습니다.

 

reducers/counterSlice.js

import { createSlice } from '@reduxjs/toolkit'; // Redux Toolkit에서 createSlice 함수를 import합니다.

const initialState = { // 초기 상태를 정의합니다.
  count: 0 // count라는 필드를 가진 초기 상태 객체입니다. 처음 시작할 때 count는 0입니다.
};

const counterSlice = createSlice({ // createSlice 함수를 사용하여 slice를 생성합니다.
  name: 'counter', // slice의 이름을 'counter'로 설정합니다.
  initialState, // 초기 상태를 사용합니다.
  reducers: { // 리듀서 함수들을 정의합니다.
    increment: state => { // increment 액션의 경우, 현재 상태를 변경하는 함수입니다.
      state.count += 1; // count를 1 증가시킵니다.
    },
    decrement: state => { // decrement 액션의 경우, 현재 상태를 변경하는 함수입니다.
      state.count -= 1; // count를 1 감소시킵니다.
    }
  }
});

export const { increment, decrement } = counterSlice.actions; // 액션 생성자 함수들을 추출하여 export합니다.
export default counterSlice.reducer; // 리듀서 함수를 default로 export합니다.

 

 

  • createSlice 함수를 사용하여 Redux 슬라이스를 생성합니다.
  • name 속성으로 슬라이스의 이름을 지정합니다. 여기서는 'counter'라는 이름을 사용했습니다.
  • initialState로 초기 상태를 정의합니다.
  • reducers 객체 내부에 각 액션에 대한 리듀서 함수를 정의합니다. 이 함수들은 Immer를 사용하여 상태를 직접 수정할 수 있습니다.
  • createSlice는 자동으로 액션 생성자 함수들을 생성하고, counterSlice.actions 객체를 통해 외부에서 액션을 접근할 수 있습니다.

 

 

 

3. 컴포넌트 작성

Redux Toolkit을 사용하면 useSelector useDispatch 훅을 사용하여 컴포넌트에서 Redux 스토어의 상태를 읽고 액션을 디스패치할 수 있습니다.

 

components/ButtonCounter.js

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from '../reducers/counterSlice';

const ButtonCounter = () => {
  const count = useSelector(state => state.counter.count);
  const dispatch = useDispatch();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch(increment())}>좋아요</button>
      <button onClick={() => dispatch(decrement())}>싫어요</button>
    </div>
  );
};

export default ButtonCounter;

 

  • useSelector 훅을 사용하여 Redux 스토어의 상태에서 counter 슬라이스의 count 필드를 읽어옵니다.
  • useDispatch 훅을 사용하여 dispatch 함수를 가져옵니다. 이 함수를 통해 액션을 디스패치할 수 있습니다.
  • 버튼 클릭 시 dispatch(increment()) 또는 dispatch(decrement())를 호출하여 액션을 디스패치합니다.

 

 

 

4. 앱 루트 컴포넌트에서 Redux 스토어 적용하기

애플리케이션의 루트 컴포넌트에서 Redux 스토어를 Provider 컴포넌트를 사용하여 적용합니다.

 

App.js

import React from 'react';
import { Provider } from 'react-redux';
import store from './store/store';
import ButtonCounter from './components/ButtonCounter';

const App = () => {
  return (
    <Provider store={store}>
      <div className="App">
        <ButtonCounter />
      </div>
    </Provider>
  );
};

export default App;

 

 

  • Provider 컴포넌트를 사용하여 애플리케이션의 전체에서 Redux 스토어를 사용할 수 있도록 설정합니다.
  • store prop에는 앞서 생성한 Redux 스토어를 전달합니다.
  • ButtonCounter 컴포넌트는 Provider 컴포넌트 하위에 위치하므로 Redux 스토어의 상태와 액션을 사용할 수 있습니다.

 

 

굉장히 예제를 잘 알려줘서 마음에든다. 이해보다는 하나의 문법처럼 생각하는게 나을듯 싶음


 

용어 정리

  • configureStore 함수는 Redux 스토어를 설정하는 역할을 합니다.
  • createSlice 함수를 사용하여 Redux 슬라이스를 생성
  • useSelector 훅을 사용하여 Redux 스토어의 상태(state) 값 추출 (스토어.슬라이스.값)
  • useDispatch 훅을 사용하여 Redux의 dispatch 함수 사용 (정의해둔 메서드? 같은 기능)
  • Provider 컴포넌트를 사용하여 애플리케이션의 전체에서 Redux 스토어를 사용할 수 있도록 설정

 

Redux 슬라이스(Slice)

createSlice 함수를 사용하여 생성된 하나의 객체로 상태(state)와 그 상태를 변경하는 리듀서(reducer)를 포함한다.

 

 

컴포넌트에서는

import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from '../reducers/counterSlice';

useSelectoruseDispatch  를 react-redux 에서 가져오고

만들어 둔 리듀서를 가져와서 사용하게 된다.

 

 

useState 에서는 setCount 와 같은 함수에 값을 직접 넣는 방식인데,

useDispatch 를 사용하면 사용자가 작성한 리듀서(reducer) 함수를 호출

 

 

 

스토어 생성시 미들웨어를 추가 할 수 있다. ->  추후 보충

 

Q.  dispatch 는 슬라이스로 구분이 되지않는? 것 같은데, 리듀서 이름이 중복이 될수도 있지않나.?? -> 아니래

 

 

 

 

 

https://chatgpt.com/

 

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

'기타' 카테고리의 다른 글

쿠키, 세션  (0) 2024.07.30
MobX 란  (0) 2024.07.30
클래스 컴포넌트의 생명주기  (0) 2024.07.29
Virtual DOM  (0) 2024.07.29
03. 면접대비 react  (0) 2024.07.29