본문 바로가기

React

React + Redux 초기 셋팅 및 테스트

반응형

처음에 리액트를 접했을 때, 컴포넌트별로 분리해서 개발을 진행하는 과정에서 다른 컴포넌트 간의 state 값을 전달하기위해 부모컴포넌트로 보내고, 자식컴포넌트의 프로퍼티로 넘겨주는 작업들로 소스코드가 상당히 복잡한 관계가 되었다. 

이를 해결 하기 위해 찾아보던 중 Redux 를 발견하였고, 나는 곧 바로 적용하였다.

리덕스는 중앙 저장소에서 상태관리가 일어나며, 저장소에서 구독하고 있는 상태값이 변경되면 해당 컴포넌트가 리렌더링 된다. 부모, 자식간의 프로퍼티 지옥이 끝났지만, 리덕스는 생각보다 러닝커브가 높다. 리덕스 단독으로 사용하기 보다는 redux-toolkit, redux-saga 와 같이 쓰이는 것 같은데, 여기서는 리덕스 셋팅 및 테스트 실습만 할 것이다.

 

일단 먼저 CRA 를 통해 리액트 프로젝트를 만든다.

npx create-react-app react-redux-base
cd react-redux-base

 

리덕스 관련 패키지를 설치한다.

yarn add redux react-redux redux-devtools-extension
  • redux : 리덕스(중앙 저장소 셋팅) 패키지
  • react-redux : 리액트와 리덕스를 바인딩 해주는 패키지
  • redux-devtools-extension : 크롬 플러그인 을 통해 개발자도구에서 현재 리덕스 스토어의 상태 및 디스패치 된 액션들을 확인할 수 있도록 도와주는 패키지

리덕스 모듈(./src/modules/counter.js)을 생성한다.

const PLUS = 'counter/PLUS';
const MINUS = 'counter/MINUS';

export const plusCounter = (count) => ({ type: PLUS, count });
export const minusCounter = (count) => ({ type: MINUS, count });

const initialState = 0;

const counter = (state = initialState, action) => {
    switch (action.type) {
        case PLUS:
            return action.count ? state + action.count : state + 1;
        case MINUS:
            return action.count ? state - action.count : state - 1;
        default:
            return state;
    }
};

export default counter;
  • Line 1~2: 타입을 상수로 선언
  • Line 4~5: action 함수 ( 반드시 type 을 필요로 함 )
  • Line 7: 해당 리덕스 모듈의 초기 값
  • Line 9~18: 리덕스 모듈로 action 을 통해 새로운 반응이 생겼을 때 type 에 따라 값을 리턴.

 

루트 리듀서를 ./src/modules/index.js 에 생성한다.

import { combineReducers } from 'redux';
import counter from './counter';

const rootReducer = combineReducers({
    counter
});

export default rootReducer;
  • combineReducers 를 통해 내가 만든 리덕스 모듈들을 합친다. ( 리덕스 모듈을 추가해서 위 소스처럼 추가해주면 된다.)

 

./index.js 에서 store를 생성하고 Provider를 통해 리덕스를 적용한다.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import { Provider } from 'react-redux';
import rootReducer from "./modules";

// store 생성
const store = createStore(rootReducer, composeWithDevTools());

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>,
    document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals

※ Provider 로 감싸는 컴포넌트(여기서는 <App/>) 부터 리덕스가 적용됨을 유의한다.

 

리덕스 초기 셋팅에 대한 준비작업이 끝났다. 

 

이제 값을 변경할 수 있는 컴포넌트를 만든다. (./src/components/Counter.jsx)

import React, { useState } from 'react';

const Counter = ({ onPlus, onMinus }) => {
    const [state, setState] = useState({});

    const handleChange = (evt) => {
        setState({
            ...state,
            [evt.target.name]: Number(evt.target.value)
        })
    };

    return (
        <div>
            <input type="text" name="plus" value={state?.plus} onChange={handleChange} />
            <button onClick={onPlus(state?.plus)}>+</button>
            <input type="text" name="minus" value={state?.minus} onChange={handleChange} />
            <button onClick={onMinus(state?.minus)}>-</button>
        </div>
    );
}

export default Counter;
  • 컨테이너 컴포넌트로 부터 이벤트(action)를 전달 받아서 버튼을 통해 값을 변경하는 컴포넌트이다.
  • 내부적으로 input 값을 전달하기 위한 state 가 존재한다.
  • input 값을 입력 후 버튼을 누르면, 입력한 값만큼 증감되고, 값을 입력하지 않으면 1 씩 증감한다.

컨테이너 컴포넌트를 작성한다. (./src/containers/CounterContainer.jsx)

import React from 'react';
import { useSelector, useDispatch, shallowEqual } from "react-redux";
import { plusCounter, minusCounter } from '../modules/counter'; // 리덕스 모듈 액션 함수
import Counter from '../components/Counter';

const CounterContainer = () => {
    const dispatch = useDispatch();
    const count = useSelector(state => state.counter, shallowEqual);

    const handlePlus = (plus) => () => {
        dispatch(plusCounter(plus));
    };
    const handleMinus = (minus) => () => {
        dispatch(minusCounter(minus));
    };

    return (
        <div>
            <Counter onPlus={handlePlus} onMinus={handleMinus} />
            현재 값은 {count} 입니다.
        </div>
    );
}

export default CounterContainer;
  • Line 7 : 리덕스 모듈에서 액션 함수를 사용하기 위해서 필요하다.
  • Line 8 : 리덕스 모듈의 값을 가져오기 위해 useSelector를 사용하였고,  useSelector 의 첫번째 인자는 리덕스 스토어에 접근해서 리듀서 명을 리턴해주는 콜백함수를 넣어주면 되고, 두번째 인자(shallowEqual)는 shallow compare 로 상황에 맞게 deps 를 넣어주면 될 것이다. 여러개의 리듀서를 등록했다면, useSelector의 첫번째 인자에서 여러개를 리턴해주면 된다.
const { test1, test2 } = useSelector(state => ({ test1: state.test1, test2: state.test2 }), []);​
  • Line 10~15 : dispatch 를 통해 리듀서의 액션 함수를 호출할 수 있도록 해준다.

 

아래와 같은 테스트 컴포넌트를 통해 값이 변경됨을 확인할 수 있다.

 

크롬 플러그인 을 설치 했다면, 크롬 개발자 도구의 Redux 탭에서 현재 리덕스의 모든 상태를 확인할 수 있다.

 

 

위 예제에 대한 샘플소스는 깃허브에 올려놓았습니다.

직접 동작해보고, 액션 함수를 만들어보면서 실습해보면 이해가 쉬울 것 입니다.

https://github.com/sbjang123456/react-redux-base

 

sbjang123456/react-redux-base

리액트+리덕스 초기 셋팅 및 연습. Contribute to sbjang123456/react-redux-base development by creating an account on GitHub.

github.com

 

반응형