IT/React

Redux

iamhyeon 2024. 12. 27. 21:21

 

State  상태

- 컴포넌트 안에서 관리되는 것

- 자식 컴포넌트들 간의 다이렉트 데이터 전달은 불가능하다

- 자식 컴포넌트들 간의 데이터를 주고 받을 때는 상태를 관리하는 부모 컴포넌트를 통해서 주고 받는다

- 상태를 관리하는 상위 컴포넌트에서 계속 내려 받아야 한다.

 

Redux

- 여러 컴포넌트가 공유하는 상태를 관리하기 위한 라이브러리

- 리액트 컨텍스트에 기반을 둔 라이브러리로 Provider 컴포넌트가 항상 취상위로 동작해야 한다.

- 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너이다.

- 서로 다른 환경(서버, 클라이언트, 네이티브)에서 작동하고, 테스트하기 쉬운 앱을 작성하도록 도와준다.

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

- 전역 상태 저장소 제공

- Props Drilling 이슈 해결

 

Store  상태 저장소

- 모든 상태를 하나의 중앙 저장소인 store에 저장한다

- 상태가 관리되는 오직 하나의 공간

- 스토어 안에서 App에서 필요한 상태를 담는다

- 컴포넌트에서 상태정보가 필요할 때 스토어에 접근한다

 

Action

- 상태 변경을 설명하는 객체

- App에서 Store에 운반할 데이터를 말한다

- JSON 객체 형식으로 돼있다

- 상태를 변경하려면 액션을 dispatch해야 한다

- 주로 타입과 payload(변경할 데이터)를 포함한다

 

Reducer

- 액션을 받아서 새로운 상태를 반환하는 함수

- 이전 상태와 액션을 인자로 받아 새로운 상태를 계산해 반환한다

- Action을 Reducer에 전달한다

- Reducer가 주문을 보고 Store의 상태를 업데이트 하는 것

- Action을 Reducer에 전달하기위해서는 dispatch() 메서드를 사용해야 한다

=>  컴포넌트 dispatch()  ➖  Reducer  =>  action  ➖  Store     ➡️     컴포넌트 리렌더링

 

Dispatch

- 액션을 리듀서에 전달하여 상태를 변경하는 방법

- 액션을 dispatch하면 Reducer가 새로운 상태를 계산하고, Redux는 새로운 상태를 Store에 반영한다.

 

Subscription

- 리액트 컴포넌트와 같은 UI는 상태변경을 반영하기 위해 redux store에 구독할 수 있다

- 상태가 변경되면 구독된 컴포넌트가 자동으로 업데이트된다.


- Redux는 상태변수를 관리하는 모듈이다
- Redux는 컴포넌트들의 배치와 상관없는 store 라는 공간을 만든다
- store 안에 용도별로 slice라는 모듈들을 박아넣는다
- slice 마다 관리하고자하는 상태변수를 만든다 ( 상태변수 여러개 가능 )
- slice마다 setter함수들을 넣는다 <= (reducer)
- App.js 하위 컴포넌트에서 필요한 상태변수를 바로 받을 수 있다. 그 변수를 여러개의 컴포넌트가 받을 수 있다.
- 어떤 컴포넌트가 reducer 함수를 호출했다면 해당 슬라이스의 상태변수를 업데이트하고, App.js를 타고 모든 컴포넌트에 전파된다.
- useState 상태변수를 전역화시키는 모듈이다
- 다른 상태변수는 다른페이지 갔다오면 리셋되는데 Redux는 계속 유지된다


 

설치

# NPM
npm install redux
npm install react-redux

# Yarn
yarn add redux react-redux

 


redux 예제


 slice라고 불리는 모듈을 먼저 작업한다

 

📁 src / slices / CounterSlice.js 파일

 

⬇️  reduxjs/toolkit 패키지로부터 createSlice함수 가져오기         (📁src>slices>CounterSlice.js)

import {createSlice} from '@reduxjs/toolkit';

 

⬇️  createSlice 함수 호출할 때 json 객체로 몇가지 설정을 해준다.          (📁src>slices>CounterSlice.js)

const CounterSlice = createSlice( {
    // 1) Slice 객체 이름 => Slice 객체명과 동일하게 설정 (헷갈리지 않게)
    name: 'CounterSlice',

    //  2) 이 모듈이 관리하고자하는 상태값들을 명시 (전역변수 같은) => 이 안의 변수들은 자유롭게 나열
    initialState: {
        number: 0,
        color: '#000'
    },
    .....

initialState  :  모든 페이지에서 인식 가능한 전역변수

(📁src>slices>CounterSlice.js)

const CounterSlice = createSlice( {
    
    ........

    /** 3) 상태값을 갱신하기 위한 함수들 구현
     * => 파라미터는 state, action 으로 고정되어 있다
     * => state : 현재 상태값을 가리킨다
     * => 컴포넌트에서 이 함수들을 호출할 때 전달되는 파라미터는 `action.payload`로 전달된다
     * => initialState 와 동일한 구조의 JSON을 리턴해야 한다
     */
    reducers: {
        plus: (state, action) => {
            const numberValue = state.number + action.payload;
            let colorValue = '#000';

            if (numberValue > 0) {
                colorValue = '#2f77eb';
            } else if (numberValue < 0) {
                colorValue = '#f60';
            }

            return {
                number: numberValue,
                color: colorValue
            }
        },

        minus: (state, action) => {
        		...........
        }
    }
} );

reducers  =>  json

initialState 변수들의   setter 함수들을 나열한다고 보면 된다

 

상태값은 state, action으로 고정

 

⬇️  plus, minus  =>  action 함수                                      (📁src>slices>CounterSlice.js)

reducers: {
	plus: (state, action) => { ... },
	minus: (state, action) => { ... }
}

 

⬇️  action 함수 export                                                       (📁src>slices>CounterSlice.js)

// 액션함수들 내보내기
export const {plus, minus} = CounterSlice.actions;

컴포넌트에서 액션함수 사용 가능

( 컴포넌트의 이벤트 안에서 호출 한다거나 )

⬆️ 리듀서를 사용할 컴포넌트 예제

컴포넌트에서 +,- 버튼 클릭 이벤트시 리듀서의 plus, minus 호출 하는 것

 

 

버튼을 누르면서 개발자가 보내고 싶은 몇가지 파라미터를 보낸다.

파라미터가 action으로 들어가고,   ( 컴포넌트 => slice - reducer - action )

initialState가 state로 들어온다.      

action 값에 따라 state를 조작한다. ( action => state => initialState )

return값은 initialState를 갱신한다. 

상태값은 store를 통해 컴포넌트에 반영된다.  ( initialState의 변수들 => store )

 

 

⬇️  컴포넌트에서 5라는 파라미터 값은 action의 paylod로 들어온다.

// 컴포넌트 일부
<button onClick={e => dispatch(plus(5))}> +5 </button>

(📁src>slices>CounterSlice.js)

plus: (state, action) => {
    const numberValue = state.number + action.payload;

 

⬇️  action 함수의 return 값은 initialState와 동일한 구조의 JSON 이어야 한다.                    (📁src>slices>CounterSlice.js)

return {
    number: numberValue,
    color: colorValue
}

 

 

⬇️  기본으로 reducer export     (reducers 아니고 reducer)                                                  (📁src>slices>CounterSlice.js)

// 리듀서 객체 내보내기
export default CounterSlice.reducer;

 

 

 

Slice 모듈을  store.js에 탑재시킨다.

⬇️  📁src>store.js

import CounterSlice from './slices/CounterSlice';
reducer: {
    CounterSlice
}

⬆️  ( JSON )  

 

❓ ( ⬇️ JavaScript 문법  =>  key, value 같을 때 key 생략 가능하다 )

const x = 100;

const data = {
	"x" : x
}

// key, value 같을 때 => key 생략 가능
const data = {
	x
}

 

 

 

⬇️  📁src>index.js

react-redux 패키지로부터 Provider 컴포넌트를 가져온다

// 리덕스 구성을 위한 참조
import { Provider } from 'react-redux';
import store from './store';
import { Provider } from 'react-redux';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    // 리덕스 스토어 연결
    <Provider store={store}>
        <BrowserRouter basename={process.env.PUBLIC_URL} future={{v7_startTransition: true, v7_relativeSplatPath: true}}>
            <App />
        </BrowserRouter>
    </Provider>
);

⬆️  Provider는 store가 갖고있는 모든 slice를 App의 모든 컴포넌트에 전파한다.

 

 

 

📁src>pages>counter>index.js

⬇️  Slice로부터 액션함수 가져오기

// Slice에 정의된 액션함수들 참조
import {plus, minus} from '../../slices/CounterSlice';

 

⬇️  상태값을 넘겨받을 useSelector, useDispatch 가져오기 

// 상태값을 로드하기 위한 hook 과 action 함수를 dispatch 할 hook 참조
import {useSelector, useDispatch} from 'react-redux';

useDispatch 함수를 통해 action함수를 호출한다.

 

const Counter = memo(() => {

    // hook을 통해 slice가 관리하는 상태값 가져오기
    const {number, color} = useSelector(state => state.CounterSlice);

⬆️  state  =>  store가 관리하는 모든 상태값                                                                    📁src>pages>counter>index.js

 

 

CounterSice의 상태변수가 리턴되고, 구조분해로 number, color에 값이 들어간다.

 

⬆️ 계산된 initialState 값이 useSelector 함수를 통해 컴포넌트에서 사용된다.

 

 

⬇️ dispatch 함수를 통해 action함수 plus, minus 를 호출한다.                                            📁src>pages>counter>index.js

const dispatch = useDispatch();

return (
    <CounterContainer>

        <h2>Counter</h2>

        <div className='counter-box'>
            <button onClick={e => dispatch(plus(5))}> +5 </button>
            <h2 className="number" style={{ color: color }}> {number} </h2>
            <button onClick={e => dispatch(minus(3))}> -3 </button>
        </div>

    </CounterContainer>
);

[ 📁src>pages>counter>index.js ] ➖ (컴포넌트) dispatch(plus(5))   ➡️

[ 📁src>slices>CounterSlice.js ]  ➖ reducers.plus ➖ action.payload  ➡️  initialState.number, color  갱신  ➡️   

[ 📁src>store.js ]  ➡️  [ 📁src>pages>counter>index.js ] ➖ useSelector => number, color  ➡️  (컴포넌트)안의 {number}

 

 

 

 

 

 

 


refer to

메가IT아카데미 이광호쌤

https://velog.io/@s_soo100/Redux-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%8D%95%EC%8A%A4-%EC%8A%A4%ED%84%B0%EB%94%941

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

 

 

'IT > React' 카테고리의 다른 글

vite  (0) 2025.02.12
Sass  (2) 2025.02.11
React 개발환경 구성  (2) 2024.11.15