개발새발 로그

React - Redux개요 본문

React

React - Redux개요

이즈흐 2023. 12. 17. 15:14

현재 Redux 공식사이트에서는 Redux Toolkit 사용을 권장하고 있다.

RTK는 Redux 앱을 만들기에 필수적으로 여기는 패키지와 함수들을 포함합니다.
대부분의 Redux 작업을 단순화하고, 흔한 실수를 방지하며, Redux 앱을 만들기 쉽게 해주는 모범 사례를 통해 만들어졌습니다.

 

일단은 먼저 Redux 사용방법을 알아보자

 

 

설치 방법

npm install redux
npm install react-redux
npm install --save-dev redux-devtools

 

 

기본 예제 설명

먼저 앱의 상태를 저장하는 하나의 저장소(store)

상태 트리를 변경하기 위해 무슨 일을 할지 서술하는 객체인 액션(action)

액션이 상태트리를 어떻게 변경할지 명시하기 위해 작성해야하는 reducer

 

Reducer 기본 예제

/**
 * 이것이 (state, action) => state 형태의 순수 함수인 리듀서입니다.
 * 리듀서는 액션이 어떻게 상태를 다음 상태로 변경하는지 서술합니다.
 *
 * 상태의 모양은 당신 마음대로입니다: 기본형(primitive)일수도, 배열일수도, 객체일수도,
 * 심지어 Immutable.js 자료구조일수도 있습니다.  오직 중요한 점은 상태 객체를 변경해서는 안되며,
 * 상태가 바뀐다면 새로운 객체를 반환해야 한다는 것입니다.
 *
 * 이 예제에서 우리는 `switch` 구문과 문자열을 썼지만,
 * 여러분의 프로젝트에 맞게
 * (함수 맵 같은) 다른 컨벤션을 따르셔도 좋습니다.
 */
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

state와 action을 매개변수로 받고 state를 반환한다.

 

 

저장소(store만들기)

// 앱의 상태를 보관하는 Redux 저장소를 만듭니다.
// API로는 { subscribe, dispatch, getState }가 있습니다.
let store = createStore(counter)

// subscribe()를 이용해 상태 변화에 따라 UI가 변경되게 할 수 있습니다.
// 보통은 subscribe()를 직접 사용하기보다는 뷰 바인딩 라이브러리(예를 들어 React Redux)를 사용합니다.
// 하지만 현재 상태를 localStorage에 영속적으로 저장할 때도 편리합니다.

store.subscribe(() => console.log(store.getState())))

 

 

action 만들기

// 내부 상태를 변경하는 유일한 방법은 액션을 보내는 것뿐입니다.
// 액션은 직렬화할수도, 로깅할수도, 저장할수도 있으며 나중에 재실행할수도 있습니다.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1

 

 

state와 action 및 Reducer

먼저 앱을 설명하기 위한 초기 상태 값을 정의해야한다.

// 앱의 초기 상태 값 정의
const initialState =  {
  value:  0
 } }

그런 다음 reuder 함수를 정의합니다.

reducer는 현재 state어떤 일이 일어났는지 설명하는 action 객체라는 두 가지 인수를 받습니다.

Redux 앱이 시작될 때 아직 상태가 없으므로 이 감속기의 기본값으로 initialState를 제공합니다:

// 앱에서 어떤 일이 발생했을 때 새로운 상태
// 를 결정하는 "감속기" 함수를 만듭니다.
function counterReducer(state = initialState, action) {
  // 리듀서는 일반적으로 발생한 액션의 유형을 보고
  // 상태를 업데이트하는 방법을 결정합니다.
  switch (action.type) {
    case 'counter/incremented':
      return { ...state, value: state.value + 1 }
    case 'counter/decremented':
      return { ...state, value: state.value - 1 }
    default:
     // 리듀서가 이 액션 타입을 신경 쓰지 않는 경우,
      // 기존 상태를 변경하지 않고 반환합니다.
      return state
  }
}

액션 객체에는 항상 액션의 고유한 이름 역할을 하는 문자열인 type  필드가 있습니다

type은 이 코드를 보는 모든 사람이 그 의미를 이해할 수 있도록 읽기 쉬운 이름이어야 합니다.

action의 유형에 따라 새로운 상태 결과로 새로운 객체를 반환하거나, 변경할 사항이 없는 경우 기존 상태 객체를 반환해야 합니다.

원본 객체를 직접 수정하는 대신 기존 상태를 복사하고 복사본을 업데이트하여 상태를 불변으로 업데이트한다는 점에 유의하세요.

 

 

store 생성 

이제 감속기 함수가 생겼으므로 Redux 라이브러리 createStore API를 호출하여 store 인스턴스를 생성할 수 있습니다.

// createStore` 함수를 사용하여 새 Redux 스토어를 생성합니다,
// 업데이트 로직에 '카운터 리듀서'를 사용합니다.
const store = Redux.createStore(counterReducer)

reducer 함수를 createStore에 전달하면, 이 함수는 reducer 함수를 사용하여 초기 상태를 생성하고 향후 업데이트를 계산합니다.

현재는 redux 툴킷의 configureStore를 사용한다.

 

UI

모든 애플리케이션에서 사용자 인터페이스는 화면에 기존 상태를 표시합니다.

사용자가 어떤 작업을 수행하면 앱은 데이터를 업데이트한 다음 해당 값으로 UI를 다시 그립니다.

// "사용자 인터페이스"는 단일 HTML 요소의 일부 텍스트입니다
const valueEl = document.getElementById('value')

// 스토어 상태가 변경될 때마다
// 최신 스토어 상태를 읽고 새 데이터를 표시합니다
함수 render()  {
  const state = store.getState()
 valueEl.innerHTML  = state.value.toString()
 }

// 초기 데이터로 UI를 업데이트합니다
render()
// 그리고 향후 데이터가 변경될 때마다 다시 그리도록 구독합니다
store.subscribe(render)

 store.getState() 메서드를 사용하여 Redux 스토어에서 최신 상태를 가져오는 방법을 알고 있는 함수를 작성한 다음 해당 값을 가져와서 표시하도록 UI를 업데이트합니다.

 

Redux 스토어를 사용하면 store.subscribe( )를 호출하고 스토어가 업데이트될 때마다 호출될 구독자 콜백 함수를 전달할 수 있습니다.

따라서 렌더 함수를 구독자로 전달하면 스토어가 업데이트될 때마다 UI를 최신 값으로 업데이트할 수 있습니다.

 

Dispatching Actions

마지막으로, 어떤 일이 일어났는지 설명하는 액션 객체를 생성하고 이를 스토어에 디스패치하여 사용자 입력에 응답해야 합니다. 

store.dispatch(action)를 호출하면 스토어가 감속기를 실행하고 업데이트된 상태를 계산한 후 구독자를 실행하여 UI를 업데이트합니다.

//  액션 객체를 '디스패치'하여 사용자 입력을 처리하고,
// 앱에서 '무슨 일이 일어났는지'를 설명해야 합니다
document.getElementById('increment').addEventListener('click', function  ()  {
 store.dispatch({ type: 'counter/incremented'  })
 })

document.getElementById('decrement').addEventListener('click', function  ()  { 
 store.dispatch({ type: 'counter/decremented'  })
 })

document
  .getElementById('incrementIfOdd')
  .addEventListener('click', function  ()  {
    // 상태에 따라 무엇을 할 것인지 결정하는 로직을 작성할 수 있습니다
    if (store.getState().value  %  2 !==  0)  {
 store.dispatch({ type: '카운터/인크리멘티드'  })
     }
   })

document
  .getElementById('incrementAsync')
  .addEventListener('click', function  ()  {
    // store와 상호작용하는 비동기 로직을 작성할 수도 있습니다
    setTimeout(function  ()  {
 store.dispatch({ type: '카운터/증분'  })
     }, 1000)
   })

 

 

 

Action에 대하여

action은 type 필드가 있는 일반 JavaScript 개체입니다.

액션은 애플리케이션에서 발생한 일을 설명하는 이벤트라고 생각할 수 있습니다.

 

type 필드는 이 작업에 설명적인 이름을 부여하는 문자열이어야 합니다(ex- todos/todoAdded)

 

액션 객체에는 어떤 일이 일어났는지에 대한 추가 정보가 담긴 다른 필드가 있을 수 있습니다.

일반적으로 이러한 정보는 payload라는 필드에 저장됩니다.

const addTodoAction =  { 
  type: 'todos/todoAdded',
  payload: '우유 구매'
 }

 

액션 크리에이터는 액션 객체를 생성하고 반환하는 함수입니다.

일반적으로 액션 객체를 매번 직접 작성할 필요가 없도록 하기 위해 사용합니다:

const addTodo  =  text =>  { 
  return  {
    type: 'todos/todoAdded',
    payload: text
   }
 }

 

 

Reducer에 대하여

 

reducer는 현재 stateaction 객체를 수신하고

필요한 경우 상태를 업데이트하는 방법을 결정한 후 새 상태를 반환하는 함수입니다.

(상태, 액션) => newState

reducer는 수신된 액션(이벤트) 유형에 따라 이벤트를 처리하는 이벤트 리스너로 생각할 수 있습니다.

 

Reducer" 함수는 Array.reduce() 메서드에 전달하는 콜백 함수의 종류와 유사하기 때문에 그 이름이 붙여졌습니다.

 

const initialState =  { value:  0  }

function counterReducer(state = initialState, action)  { 
  // 감속기가 이 액션을 신경쓰는지 확인
  if (action.type  === 'counter/increment')  {
    // 그렇다면 `state`의 복사본을 만듭니다
    return  {
       ...state,
      // 그리고 새 값으로 복사본을 업데이트합니다
      value: state.value  +  1
     }
   }
  // 그렇지 않으면 기존 상태를 변경하지 않고 반환합니다
  return state
 }

리듀서는 내부에서 if/else, 스위치, 루프 등 모든 종류의 로직을 사용하여 새 상태를 결정할 수 있습니다.

 

 

Store에 대하여

현재 Redux 애플리케이션 상태는 store 라는 객체에 저장됩니다.

store는 reducer을 전달하여 생성

현재 상태 값을 반환하는 getState 메서드가 있다.

import  { configureStore } from '@reduxjs/toolkit'

const store = configureStore({ reducer: counterReducer })

console.log(store.getState())
// {value: 0}

 

 

Dispatch에 대하여

Redux 저장소에는 dispatch라는 메서드가 있습니다. 

상태를 업데이트하는 유일한 방법은 store.dispatch()를 호출하고 액션 객체를 전달하는 것입니다.

store는 reducer 함수를 실행하고 새 상태 값을 저장하며,

우리는 getState()를 호출하여 업데이트된 값을 검색할 수 있습니다:

store.dispatch({ type: 'counter/increment' })

console.log(store.getState())
// {value: 1}

 

actions dispatch는 애플레케이션에서  '이벤트 트리거'라고 생각할 수 있습니다.

어떤 일이 발생했고 스토어에 이를 알리고 싶습니다.

리듀서는 이벤트 리스너처럼 작동하며, 관심 있는 액션이 들리면 그에 대한 응답으로 상태를 업데이트합니다.

const increment  =   () =>  { 
  return  {
    type: 'counter/increment'
   }
 }

store.dispatch(increment())

console.log(store.getState())
// {value: 2}

 

 

Selectors에 대하여

선택기는 스토어 상태 값에서 특정 정보를 추출하는 방법을 알고 있는 함수입니다.

애플리케이션의 규모가 커지면 앱의 여러 부분에서 동일한 데이터를 읽어야 하므로 반복되는 로직을 피하는 데 도움이 될 수 있습니다

const selectCounterValue  = state => state.value

const currentVue = selectCounterVue(store.getState())
console.log(currentVue)
// 2

 

728x90
반응형
LIST