Study/React

[새싹x코딩온] 웹 개발자 부트캠프 과정 15주차 회고 | Redux

다니니니 2024. 8. 19. 17:09
728x90

1. 들어가며

  • React 에서 Redux 를 사용하여 상태 관리하기

2. Redux

Redux는 자바스크립트에서 상태 관리를 하기 위한 라이브러리다.

React에서 상태 관리를 하기 위해 많이 사용하는 라이브러리다.

 

React에서 상태 관리를 위해 사용하는 이유는 프로젝트를 하다보면 컴포넌트가 많이 만들고 관리하게 되는데, 그러다보면 props 지옥에 빠지는 경우가 생긴다!

출처 : https://www.slideshare.net/slideshow/006-react-redux-framework/72730511

이를 해결하기 위해 Redux 는 store라는 개념을 도입하여 전역으로 상태를 관리할 수 있게 하기 위해 사용한다.

 

Redux 용어 정리

출처 : 코딩온 강의 교안

 

1. Store 

상태가 관리되는 공간

현재 애플리케이션 상태(state) 와 reducer 가 들어있음

** 한개의 프로젝트에는 하나의 스토어

** 스토어에 있는 데이터는 컴포넌트에서 직접 조작 X

 

2. Action 

상태에 어떤 변화가 필요할 때 액션(action) 발생

컴포넌트에서 store 에 운반할 데이터

액션은 객체이며, type 속성을 반드시 포함해야함

reducer가 수행할 작업을 설명함

 

3. Dispatch

액션을 발생시키는 것 

dispatch(action) 형태로 액션 객체를 파라미터로 넣어서 호출

 

4. Reducer

액션의 type에 따라 변화를 일으키는 함수

첫번째 매개변수로 현재 상태(state). 두번째 매개변수로 action값 받음

 

Redux의 주요 원칙 3가지

1. 단일 스토어
- 애플리케이션의 전체 상태는 하나의 스토어에 저장 → 모든 상태가 중앙 집중화된 스토어
- 이유 : 한 곳에서 관리 되면 일관성을 유지하기 좋고 추적이 용이

2. 상태(state는 읽기 전용
- 상태는 직접 수정할 수 없으며, 상태를 변경하려면 액션(action) 을 통해서만 가능
- 상태를 변경하려는 의도를 설명하려는 액션을 스토어에 전달하면, 스토어는 리듀서를 통해서 새로운 상태 객체를    생성
- 액션 → 스토어 → 리듀서 → 새로운 상태 생성

3. 변경은 순수 함수로 이루어짐
- 상태를 변경하는 로직은 순수 함수인 리듀서로 정의됨
- 이 과정에서 상태는 직접 수정되지 않고, 새로운 상태 객체가 반환됨

Redux 모듈 설치

npm i redux react-redux @reduxjs/toolkit

 

위의 커맨드로 redux를 설치해준다.

 

 

React 프로젝트에 Redux 적용

기존에 이 포스팅(https://daddda3232.tistory.com/76) 에서 사용한 프로젝트는 react의 hook 중 useReducer를 사용했는데,이를 redux toolkit 을 사용해서 리팩토링 해보고자 한다.

 

useReducer 를 사용한 코드

// useReducer로 리팩토링한 코드

// 주민 정보 배열
const villagers= [
  {id : '1', name : '릴리안', birthday : '5월 9일생'},
  {id : '2', name : '쭈니', birthday : '9월 29일생'},
  {id : '3', name : '아네사', birthday : '6월 23일생'},
  {id : '4', name : '리키', birthday : '6월 3일생'},
  {id : '5', name : '마티', birthday : '4월 16일생'},
  {id : '6', name : '스파크', birthday : '7월 9일생'},
  {id : '7', name : '레베카', birthday : '9월 10일생'},
  {id : '8', name : '미애', birthday : '3월 10일생'},
  {id : '9', name : '스피카', birthday : '9월 11일생'},
  {id : '10', name : '미첼', birthday : '5월 19일생'},
]

// 초기 상태값
const initState = villagers[0]

// reducer 함수
const reducer = (prevState, action) => {
  return prevState = villagers[action - 1]
}

function App() {
  // useReducer 훅 
  const [villager, dispatch] = useReducer(reducer, initState)  

  // 액션 핸들러 함수
  const clickVillger = (e) => {
    let vId = e.currentTarget.getAttribute('data-list')
    dispatch(vId)
  }

  return (
    <div className="App">
      <Villagers villager={villagers} click={clickVillger}/>
      <VillagerInfo name={villager.name} day={villager.birthday}/>
    </div>
  );
}

 

 

Redux를 사용한 코드

 

index.js

-> redux를 사용하기 위해 provider를 사용하고, 여기에 store 데이터를 전달

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store'
import { Provider } from 'react-redux';

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

 

App.js

이전과는 다르게 상태 관련 메서드는 해당되는 컴포넌트에 넣음 (props로 전달 안해도됨!)

import React from 'react';
import { VillagerInfo, Villagers } from './components/VillagersRedux';
import './styles/Villagers.scss'


function App() {
  return (
    <div className="App">
      <Villagers />
      <VillagerInfo />
    </div>
  );
}

export default App;

 

VillagersRedux.js (컴포넌트 부분)

useSelector 를 통해서 Redux Store의 상태를 읽어오고

useDispatch 를 통해서 액션을 디스패치하는 함수를 가져오고, 액션을 디스패치함

import React from 'react'
import villagers from '../data/villagersData'
import { useDispatch, useSelector } from 'react-redux'
import { chgIndex } from '../store/villagersSlice'


export const Villagers = () => {
    const dispatch = useDispatch()

    const villagerClick = (e) => {
        let vId = e.currentTarget.getAttribute('data-list') - 1
        dispatch(chgIndex(vId))
    }

  return (
    <ul className='villager-list'>
        {
            villagers.map((v,i)=>
                <li key={i} onClick={villagerClick} data-list={v.id} className={`villager villager${v.id}`}>
                    <figure className='villager-img'>
                        <img src={'./image/acnh/'+v.name+'1.png'} alt={v.name}/>
                    </figure>
                    <p className='villager-name' data-day={v.birthday}>{v.name}</p>
                </li>
            )
        }
    </ul>
  )
}

export const VillagerInfo = () => {
    const villager =  useSelector(state => state.villager.info)
    const {name, birthday} = villager
    
    return(
        <div className='villager-info'>
            <h2 className='villager-name'>{name}</h2>
            <figure className='villager-img'>
                <img src={'./image/acnh/'+name+'2.png'} alt={name}/>
            </figure>
            <p className='villager-birthday' >{birthday}</p>
        </div>
    )
}

 

villagersSlice.js (store부분)

createSlice 메서드를 통해서 리듀서와 액션 생성자 정의 

import {createSlice} from "@reduxjs/toolkit";
import villagers from "../data/villagersData";

const villagerSlice = createSlice({
    name : 'villager',
    initialState : {info : villagers[0]},
    reducers : {
        chgIndex : (state, action) => { state.info = villagers[action.payload] }
    }
})

export const { chgIndex } = villagerSlice.actions;

export default villagerSlice.reducer;

index.js (store부분)

configureStore 를 통해 Redux 스토어 생성

 

import villagersRuducer from "./villagersSlice";
import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({
    reducer : {
        villager  : villagersRuducer
    }
})

export default store;

Redux를 사용한 코드 결과

 

이전처럼 잘 작동됨!

 

3. 마치며

학습을 하면서 느낀건데 예전에 Vue.js 의 vuex store의 흐름과 redux의 흐름이 비슷하면서도 다른 것 같다.

(그래서 Vuex Store의 흐름에 대해서도 다시 한 번 생각해보며 공부하게됨! 이에 대해서 포스팅 참고 https://daddda3232.tistory.com/83)

지금이야 복잡하게 느껴질 수도 있지만 프로젝트가 커지고, 데이터가 많아지게 되면 정말로 유용하게 쓰일 것 같다.

연습을 많이해서 손에 익숙해지도록 해야겠다!

그나저나.. 프로젝트를 하게되면 라우터도 같이 섞어서 하게 될텐데 그거랑 어떻게 조합해서 써야할지 더 고민하고 공부해야겠다.

 

4. Reference

1. 코딩온 강의 교안 및 실습

 

2. https://www.slideshare.net/slideshow/006-react-redux-framework/72730511

 

006. React - Redux framework

006. React - Redux framework - Download as a PDF or view online for free

www.slideshare.net

 

 

 

728x90