Redux로 상태관리하기 - Redux Basic 1

lbr·2022년 8월 16일
0

Redux 개요

context가 가지고 있는 전역 데이터를 잘 활용하는 부분이 중요하게 됩니다.
그래서 context에 주어지는 전역 데이터를 어떻게 효과적으로 관리할 것인지에 대한 라이브러리Redux입니다.

store 안에 있는 상태를 보라색 공이 변경하면, 그 상태를 가지고 있는 다른 컴포넌트들이 자동으로 다시 렌더가 됩니다.

순서

  1. 단일 스토어를 만드는 법" 과,
  2. 리액트에서 스토어 사용하는 법" 을 익히는 시간
  • 단일 스토어다!
  • [만들기] 단일 스토어 사용 준비하기
    1. import redux
    2. 액션을 정의하고,
    3. 액션을 사용하는, 리듀서를 만들고,
    4. 리듀서들을 합친다.
    5. 최종 합쳐진 리듀서를 인자로, 단일 스토어를 만든다.
  • [사용하기] 준비한 스토어를 리액트 컴포넌트에서 사용하기
    1. import react-redux
    2. connect 함수를 이용해서 컴포넌트에 연결 또는 Hook으로 connect의 역할 대체 가능

실습 환경 설정

npx create-react-app redux-start
npm i redux
npm start

Action - 액션

  • 액션은 사실 그냥 객체 (object) 입니다.
  • 두 가지 형태의 액션이 있습니다.
    - { type: 'TEST' } // payload 없는 액션
    type만 가지고 있는 액션. 나머지는 없음.
    - { type: 'TEST', params: 'hello' } // payload 있는 액션
    type이 있고, 그 이외에 다른 프로퍼티가 있는 액션.
  • type 만이 필수 프로퍼티이며, type 은 문자열 입니다.

이렇게 만들어진 Action은 store에 전달이 되면 store에 있는 상태를 변경하는 용도로 사용이 됩니다.

store의 상태를 변경할 때 특별한 인자 값을 주지 않으면, 타입만 있는 액션이 전달이 됩니다.
액션을 전달하면서 액션이 전달하는 값을 이용하여 상태를 변경해야 한다면, 타입을 제외하고 다른 파라미터가 들어가게 됩니다.

액션 객체를 만들어 낼 때에는 리터럴을 사용하지 않고, 액션을 만들어내는 함수를 만들어서 사용하게 됩니다.(실수를 방지하기 위함)

리덕스의 액션 생성자

function 액션생성자(...args) { return 액션; }
  • 액션을 생성하는 함수를 "액션 생성자 (Action Creator)" 라고 합니다.
  • 함수를 통해 액션을 생성해서, 액션 객체를 리턴해줍니다.
  • createTest('hello'); // { type: 'TEST', params: 'hello' } 리턴

그래서 액션의 종류별로 액션생성함수가 똑같은 갯수로 만들어지게 됩니다.

Redux의 액션이 하는 일

  • 액션 생성자를 통해 액션을 만들어 냅니다.
  • 만들어낸 액션 객체를 리덕스 스토어에 보냅니다.
  • 리덕스 스토어가 액션 객체를 받으면 스토어의 상태 값이 변경 됩니다.
  • 변경된 상태 값에 의해 상태를 이용하고 있는 컴포넌트가 변경됩니다.
  • 액션은 스토어에 보내는 일종의 인풋이라 생각할 수 있습니다.

액션을 준비하기 위해서는?

액션을 준비하기 위해서는 2가지 단계가 있습니다.

  1. 액션의 타입을 정의하여 변수로 빼는 단계
    - 강제는 아닙니다. (그러므로 안해도 됩니다.)
    - 그냥 타입을 문자열로 넣기에는 실수를 유발할 가능성이 큽니다.
    - 미리 정의한 변수를 사용하면, 스펠링에 주의를 덜 기울여도 됩니다.
  2. 액션 객체를 만들어 내는 함수를 만드는 단계
    - 하나의 액션 객체를 만들기 위해 하나의 함수를 만들어냅니다.
    - 액션의 타입은 미리 정의한 타입 변수로 부터 가져와서 사용합니다.

액션 정의

애플리케이션에서 사용하는 모든 액션들을 정의하고, 액션 생성 함수를 만들어 넣습니다.

// 1. to do list 의 to do를 넣는 액션인 add to do 라는 액션의 타입을 정의하겠습니다.

const ADD_TODO = "ADD_TODO";

// 2. ADD_TODO 타입을 이용하는 액션 생성 함수를 한 개 만듭니다.
function addTodo(todo) {
  return {
    type: ADD_TODO,
    todo,
  };
}
// 이 함수는 전역 state의 todo라는 state 안에 새로운 todo를 넣을 것입니다.

// 액션을 생성할 때 이 addTo라는 함수를 이용해서 생성할 것이고, 그 때 인자로 할 일을 넘기면 됩니다.

// 여기까지는 리덕스의 어떤 라이브러리를 이용하지 않았습니다.
// 액션 객체와 액션 생성 함수를 만드는데 까지는 순수 javascript 입니다.
// 이 다음은 액션을 이용해서 상태를 변경할 수 있는 reducer에 대해서 배워보겠습니다.

Reducers - 리듀서

리덕스의 리듀서란 ?

  • 액션을 주면, 그 액션이 적용되어 달라진(안달라질수도...) 결과를 만들어 줌.
  • 그냥 함수입니다.
    - Pure Function (같은 인풋을 받으면 같은 결과를 낸다는 의미. 즉, 시간에 따라서 달라지는 결과를 가진다던지..이런 형태의 코드가 들어가면 안됩니다.)
    - Immutable(불변) - 원래 오리지널 state와 새로 바뀐 state가 별도의 객체로 만들어져야 합니다.
    리듀서를 통해서 스테이트가 달라졌음을 리덕스가 인지하는 방식이 Immutable 방식이기 때문입니다.
    그래서 Immutable 하지 않게 처리가 되면 문제가 발생할 수 있습니다.
function 리듀서(previousState, action) {
  return newState;
}

리듀서 : 함수 이름
previousState : 현재의 state
action : 액션 객체

previousState 와 action 객체를 이용해서 새로운 state 객체를 만들어 내는 것이 리듀서의 역할입니다.

  • 액션을 받아서 state를 return하는 구조.
  • 자로 들어오는 previousState 와 리턴되는 newState 는
    다른 참조를 가지도록 해야합니다.(위에서 말한 Immutable) 그래야만 redux가 상태가 변경되었음을 제대로 인지하고 처리해 줄 수 있습니다.

리듀서 함수 만들어보기

리듀서 함수를 만들어보겠습니다.

function todoApp(previousState, action) {
   return previousState;
 }

만약 어떠한 변화도 없게 위처럼 작성하면, 액션을 받더라도 아무 변화 없이 previousState가 return 되서 아무 변화도 일어나지 않습니다.

리듀서를 만들기 전에 먼저 state의 모습을 구상해보겠습니다.

state
['코딩', '점심 먹기'];

위처럼 state가 구성되어 있다고 생각하고, 아래처럼 리듀서를 작성해보겠습니다.

import { ADD_TODO } from "./actions";

function todoApp(previousState, action) {
  // 초기값을 설정해주는 부분.
  if (previousState === undefined) {
    return [];
  }

  if (action.type === ADD_TODO) {
    // ADD_TODO는 다른파일에서 정의했으므로 정의한 곳으로 가서 export하고, import로 가져와야합니다.
    return [...previousState, action.todo];
  }

  // Immutable 하지 않다는 것은 예를 들면 이런것입니다.
  // previousState.push(); // 객체를 변경하지만, 객체의 레퍼런스는 변경되지 않는 상황.
  // 그러면 redux는 바뀌었다고 생각하지 않습니다.

  // ADD_TODO 타입이 아닌 경우에는 아무 대응도하지 않고 현재값을 그대로 리턴하게 합니다.
  return previousState;
}

설명하기 위해서 위처럼 코드를 작성했지만, 더 쉽게 하는 방법이 있습니다.

const initialState = []; // 보통은 먼저 초기 상태를 정의합니다.

function todoApp(previousState = initialState, action) {
  // 초기값을 설정해주는 부분.
  // if (previousState === undefined) {
  //   return [];
  // }

  if (action.type === ADD_TODO) {
    // ADD_TODO는 다른파일에서 정의했으므로 정의한 곳으로 가서 export하고, import로 가져와야합니다.
    return [...previousState, action.todo];
  }

  // Immutable 하지 않다는 것은 예를 들면 이런것입니다.
  // previousState.push(); // 객체를 변경하지만, 객체의 레퍼런스는 변경되지 않는 상황.
  // 그러면 redux는 바뀌었다고 생각하지 않습니다.

  // ADD_TODO 타입이 아닌 경우에는 아무 대응도하지 않고 현재값을 그대로 리턴하게 합니다.
  return previousState;
}

const initialState = []; : 먼저 초기 상태를 정의하고,
function todoApp(previousState = initialState, action) : 매개변수에서 default 값으로 초기화값을 할당합니다.

리듀서 마무리

action과 마찬가지로 리듀서도 redux 라이브러리를 활용하는 부분은 아닙니다.
리듀서를 pure하게 만들고, Immutable 하게 처리가 되게 만들면 리듀서 함수의 작성이 끝납니다.
이러한 코드를 활용해서 redux의 store를 만들게 되면, 이러한 부분이 동작을 하게 됩니다.

이 다음부터는 store를 만들어 보겠습니다.

0개의 댓글