본문 바로가기
프론트엔드/React

리액트 전역 상태관리를 사용해보자 (Redux, Recoil)

 

안녕하십니까 

야생에서 등장한 하프 입니다 😊

 

리액트에서 전역적으로 상태관리가 주어진다고 한다면, 아무래도 필요한것이.. 전역 상태관리 일거시옵니다~

간단하고 핵심만 짚어서 해드릴테니 잘 따라오시면 되겠습니다 👌

 

 

📝 목차

Redux, Recoil 이란?

전역 상태관리를 해주는 라이브러리로 많이들 알고 계실 겁니다 ~

주로 Redux는 대부분 알고 계신거 같더라구요.

 

이것을 왜? 사용하는가 한다면 바로 props 때문입니다.

props는 부모에서 자식에게 넘겨줄 수 있지만, 

자식에서 부모로 props를 넘겨줄 수는 없습니다.

 

그래서 자식에게 useState 변수를 넘겨준다고 한다면, props는 너무나도 복잡해지게 됩니다.

즉, 유지보수가 힘들어지게 되겠지요.

 

그래서 우리는 이것을 `어딘가`에 변수를 저장해두고 내가 원할때마다 가져다 쓰자! 하는것이 전역상태관리 입니다.

 

 

1. Redux

Redux(리덕스)의 경우 익숙한 구조에 신뢰성이 높다고 볼 수 있습니다.

대신에! 하나를 변경하려고 해도 actions, reducer, store등 등 등.. 다 변경 해줘야 하는 번거러움 있지요.

 

하지만 번거로움에는 큰 이점도 따른 다는것!.. 명심해주세요.

 

 

2. Recoil

Recoil(리코일)의 경우는 리덕스보다 좀 더 간단하게 사용할 수 있는 라이브러리 라고 보면 됩니다.

리코일에도 atom, selet 등 다양한 기능들이 있으니.. 한 가지에 익숙해진다면, 러닝 커브가 살짝 일어날 수 있습니다.

 

그치만!!.. 편리한것에 있어서 매우 좋다고 할 수 있지요.

 

 

3. Redux? Recoil? 어떤걸 사용해야 할까

Redux와 Recoil의 상태관리 라이브러리를 선택하기 전에 있어서 프로젝트의 규모를 살펴봐야 할 것 같습니다.

 

주인장의 경우는 ..

프로젝트의 규모가 크고 디버깅이 많이 필요하다 싶으면 Redux

프로젝트의 규모가 작고 간단하게 진행을 하고 싶다 하면 Recoil 을 추천드립니다.

 

전 개인적으로 리코일을 선호하는 편 입니다.

이것저것 맞춰주기 귀찮아서 그냥.. 사용합니다 😅

 

 

 

서론이 길어졌네요 바로 사용법에 대해 알아보도록 합시다 ~

 

다른 글들을 보면 여기는 저렇게, 저기는 저렇게 방식이 전부 다르기도 해서, 헷갈리기도 합니다.

그래서 한번에 보기 쉽게 딱!!! 정리 해드리겠습니다.

 

물론 정리해놓고 나중에 제가 보기 위함 이지만요 😊


 

 

Redux 사용법

1. 라이브러리 설치

필요한 라이브러리는 다음과 같습니다.

redux 와 react-redux를 설치해주시면 됩니다.

npm i redux react-redux

 

 

2. 파일 구조

상태관리를 해주기 위해서 파일 구조를 다음과 같이 잡았습니다.

📁 src
└─ 📁 redux
   └─ 📁 actions
      └─ 📜 coffeeAction.js 
   └─ 📁 reducers
      └─ 📜 coffeeReducer.js
   └─ 📜 store.js

 

크게 Actions, Reducers, Store 3개로 분류가 됩니다.

아래의 코드들은 그대로 따라 붙여넣어도 됩니다.

 

3. 리덕스 설정

액션(Actions)

액션은 웹으로부터 온 데이터를 Store에 전송할 수 있도록 하는 Payload 입니다.

/src/redux/actions/coffeeAction.js

export const addCoffee = (coffeeData) => ({
  type: "ADD",
  payload: coffeeData
})

 

  • type : 웹으로부터 전달받은 수행할 Type 입니다.
  • payload : 웹으로부터 전달받은 데이터 입니다. 

 

 

리듀서(Reducers)

액션(Action)에서 전달 받은 Type을 실행하는 구문 입니다.

Switch를 통해서 전달받은 Type에 따라 다르게 구문을 수행할 수 있습니다 !!..

/src/redex/reducers/coffeeReducer.js

const coffeeReducer = (state = [], action) => {
    switch (action.type) {
      case 'ADD':
        state.push(
          action.payload
        )
        return state;
      default:
        return state;
    }
  };
  
  export default coffeeReducer;

 

 

스토어(store)

'스토어는 리듀서(Reducers)들을 관리해주는 것' 이라고 생각하시면 됩니다!

/src/redux/store.js

import { combineReducers, createStore } from 'redux';
import coffeeReducer from './reducers/coffeeReducer';

const rootReducer = combineReducers({
    coffee: coffeeReducer
})

let store = createStore(rootReducer);

export default store;

 

 

이제 마지막으로 index.js만 수정해주면 됩니다.!!!

/src/index.js

import { Provider } from 'react-redux';
import store from "./redux/store";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( // Provider 추가..!
  <Provider store={store}>
    <App />
  </Provider>
);

 

 

4. 데이터 불러오기

리덕스에서는 useSelectoruseDispatch가 있습니다!

 

useSelector의 경우는 조회할 때 사용을 하고

useDispatch의 경우는 업데이트할 때 사용을 합니다!

 

 

우선 app.js를 간단하게 꾸며보도록 할게요.

/src/app.js

import { useState } from 'react';

function App() {
  let [coffee, setCoffee] = useState({
    title: "",
    price: 0
  })
  
  const onClick = () => {
    
  }

  return (
    <div className="App">
      커피 이름 : <input value={coffee.title} onChange={(e) => {
        setCoffee({
          ...coffee,
          title: e.target.value
        })
      }}></input> <br/>
      커피 가격 : <input type={"number"} value={coffee.price} onChange={(e) => {
        setCoffee({
          ...coffee,
          price: e.target.value
        })
      }}></input> <br/>
      <button onClick={onClick}>추가!</button>
    </div>
  );
}

export default App;

추가를 클릭하면..~ Console 로그에 추가 되는게 보여지도록 하겠습니다.

 

 

아래의 코드를 살펴보면

const coffeeSelect = useSelector((state) => state.coffee);

 

이러한 코드를 마주치게 되는데!!.. 이건 무슨 뜻이냐 하면

/src/redux/store.js 에서 이 부분을 가리키게 됩니다!!

그래서 coffeeReducer를 지정해서 불러오겠다~.. 라고 생각하시면 되겠습니다. 

 

 

전체 코드는 다음과 같습니다

/src/App.js

import { useSelector, useDispatch } from 'react-redux';
import { addCoffee } from './redux/actions/coffeeAction';
import { useState } from 'react';

function App() {
  const coffeeSelect = useSelector((state) => state.coffee);
  const dispatch = useDispatch();
  let [coffee, setCoffee] = useState({
    title: "",
    price: 0
  })
  
  const onClick = () => {
    dispatch(addCoffee(coffee));
    alert("추가되었습니다!")
    console.log(coffeeSelect);
  }
  
...[이하 생략]

 

데이터를 입력해보고 추가!! 버튼을 눌러주면!!!

한개 한개 씩 추가되는것을 볼수가 있습니다!!

 


 

리코일 사용법

리코일의 경우, 리덕스보다 사용하기는 편리 합니다.

 

1. 라이브러리 설치

필요한 라이브러리 설치를 합시다.

npm install recoil

 

 

Recoil의 경우는 부모드리 어딘가에 `RecoilRoot` 가 필요합니다!

공식 홈페이지 에서는 Root 컴포넌트가 RecoilRoot를 넣기 가장 좋은 장소라고 하네요

 

저의 경우는 `src/index.js`에 넣어주었습니다.

import { RecoilRoot } from 'recoil';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <RecoilRoot>
    <App />
  </RecoilRoot>
);

 

 

2. 파일구조

리코일을 설정한 파일 구조는 다음과 같습니다.

📁 src
└─ 📁 recoil
   └─ 📜 atom.js 
   └─ 📜 selector.js

 

 

3. Atom

Atom 사용법을 먼저 알아보도록 하겠습니다.

Atom은 상태(State)의 일부를 나타 냅니다. 어떤 컴포넌트에서나 읽고 쓸 수 있습니다.

하지만!.. Atom을 사용한 컴포넌트들은 구독 상태가 되어, State가 변화하면 구독된 컴포넌트들은 전부 렌더링이 되어버리는 상황이 발생합니다. 

 

그러니.. 꼭 필요한 부분에만 하는것이 좋겠습니다!

Atom은 useState와 사용법이 유사하니, 금방 적응하실 수 있으실 겁니다.

 

// src/recoil/atom.js

import { atom } from "recoil";

export const upDownState = atom({
    key: 'upDownState',
    default: 0, 
});

 

 

useRecoilState를 사용하면, useState처럼 사용할 수 있습니다.

// src/app.js

import { useRecoilState, useRecoilValue } from "recoil";
import { upDownState } from "./recoil/atom";

const App = () => {
  let [updown, setUpDown] = useRecoilState(upDownState);
  return (
    <div>
        <span>{updown}</span>
        <br/>
        <button onClick={() => {setUpDown(updown - 1)}}>down</button>
        <button onClick={() => {setUpDown(updown + 1)}}>up</button>
    </div>
  )
}

export default App;

 

 

결과를 확인해봅시다 😊

See the Pen Recoil_atom by Kimjinwon (@hdev1004) on CodePen.

 

 

 

 

 

 

4. Selector

Selector의 경우는 Redux에서 Reducer와 같은 역할이라고 보시면 됩니다.

State의 변화되는 값을 통하여, 새로운 값의 형태로 만들어주는 좋은 기능 입니다.

 

많이는 사용할것 같지는 않지만 알아두는건 좋지요!

// src/recoil/selector.js

import { selector } from "recoil";
import { upDownState } from "./atom";

export const upDownSelector = selector({
    key: 'upDownSelector', // unique ID (with respect to other atoms/selectors)
    get: ({get}) => {
      const updown = get(upDownState);
      if(updown < 0) {
        return `0 이하로는 내려갈 수 없습니다!`
      }
      return `값 : ${updown}`
    },
});

 

import { useRecoilState, useRecoilValue } from "recoil";
import { upDownState } from "./recoil/atom";
import { upDownSelector } from "./recoil/selector";

const App = () => {
  let [updown, setUpDown] = useRecoilState(upDownState);
  let selector = useRecoilValue(upDownSelector);
  return (
    <div>
        <span>{selector}</span>
        <br/>
        <button onClick={() => {setUpDown(updown - 1)}}>down</button>
        <button onClick={() => {setUpDown(updown + 1)}}>up</button>
    </div>
  )
}

export default App;

 

 

이것도 결과를 확인해봅시다!

See the Pen Untitled by Kimjinwon (@hdev1004) on CodePen.

 

 

 

 

 

 

모두 즐거운 코딩 되세요 😊