React 난 이렇게 배웠다[Redux]

진건희·2024년 5월 1일
1
post-thumbnail
post-custom-banner

Redux란?

리덕스는 JavaScript 앱의 상태 관리를 위한 라이브러리로, 애플리케이션의 상태를 중앙 집중적으로 관리하여 상태 관련 로직을 효율적으로 관리할 수 있게 해준다.
↳ useState와 같은 상태 관리를 하는 라이브러리

context api의 전역적으로 관리[앱 or 웹 전체 관리]
+
useState의 상태 관리

두 가지의 기능을 합친 존재 같은 느낌

useState와 Redux의 차이

범위

useState : 로컬 상태를 관리하며, 해당 컴포넌트 내에서만 유효하다.
Redux : 전역 상태를 관리하며, 애플리케이션 전체에서 유효하다.

useState : 해당 페이지에서 사용하는 '개별' 정보
Redux : 전체 앱에서 공유하는 '전체' 정보

관리 방식

useState : 컴포넌트 내에서 상태를 직접 변경하고 관리한다.
Redux : 액션과 리듀서를 통해 상태를 변경하고 관리한다.

useState : 각 페이지에서 직접 정보를 바꾸고 관리
Redux : 앱 전체에서 일관된 방식으로 정보를 바꾸고 관리

구현

useState : 리액트 내장 훅을 사용하여 상태를 관리한다.
Redux : Redux 라이브러리를 사용하여 전역 상태를 관리한다.

useState : 리액트에서 기본적으로 제공하는 훅을 사용하여 상태를 관리합니다. 코드 작성이 비교적 간단하고 직관적.
Redux : Redux 라이브러리를 설치하고 설정한 후, 액션, 리듀서, 스토어 등을 직접 구현하여 사용합니다. 초기 설정과 코드 구조가 필요하기 때문에 상대적으로 구현이 복잡할 수 있음.

복잡성

useState* : 간단한 상태 관리에 적합하며, 컴포넌트 간의 데이터 공유가 필요한 경우에 유용하다.
Redux : 대규모 애플리케이션 및 복잡한 상태 관리에 적합하며, 상태 변화를 예측 가능하고 추적 가능하게 한다.

useState : 한 페이지 안에서 정보를 관리하는 것
Redux : 앱 전체에서 정보를 관리하는 것

유지보수성

useState : 각 컴포넌트에서 상태를 독립적으로 관리하므로 유지 보수가 비교적 쉽습니다.
Redux : 전역 상태를 중앙 집중적으로 관리하므로 상태 변경 로직을 중앙화하여 유지 보수가 용이합니다.

useState : 각 페이지를 개별적으로 유지보수하는 것
Redux : 앱 or 웹 전체를 일관되게 유지보수하는 것

저티어 해석 ↓


범위

useState : 해당 페이지에서 사용하는 '개별' 정보
Redux : 전체 앱에서 공유하는 '전체' 정보

관리 방식

useState : 각 페이지에서 직접 정보를 바꾸고 관리
Redux : 앱 전체에서 일관된 방식으로 정보를 바꾸고 관리

구현

useState : 리액트에서 기본적으로 제공하는 훅을 사용하여 상태를 관리함.
코드 작성이 비교적 간단하고 직관적.
Redux : Redux 라이브러리를 설치하고 설정한 후, 액션, 리듀서, 스토어 등을 직접 구현하여 사용함.
초기 설정과 코드 구조가 필요하기 때문에 상대적으로 구현이 복잡할 수 있음.

복잡성

useState : 한 페이지 안에서 정보를 관리하는 것
Redux : 앱 or 웹 전체에서 정보를 관리하는 것

유지보수성

useState : 각 페이지를 개별적으로 유지보수하는 것
Redux : 앱 or 웹 전체를 일관되게 유지보수하는 것


핵심 요소

단일 상태 트리(Single State Tree) : 리덕스는 애플리케이션의 전체 상태를 하나의 객체로 관리한다.
이를 단일 상태 트리라고 부르며, 모든 컴포넌트에서 이 상태에 접근할 수 있다.

액션(Action) : 상태를 변경하는 데 필요한 정보를 나타내는 객체다.
액션은 반드시 type이라는 필드를 가져야 하며, 추가적인 데이터를 포함할 수 있다.

리듀서(Reducer) : 현재 상태와 액션을 받아서 새로운 상태를 반환하는 함수다.
리듀서는 순수 함수여야 하며, 이전 상태를 변경하지 않고 새로운 상태를 생성하여 반환한다.

스토어(Store) : 전체 애플리케이션의 상태를 담고 있는 객체다.
스토어에는 현재 상태와 리듀서가 포함되어 있으며, 상태 변경을 구독하고 액션을 디스패치할 수 있다.

저티어 해석 ↓

단일 상태 트리(Single State Tree) : 앱 안에 있는 모든 정보를 하나로 모아놓은 것.
그래서 어디서든 이 정보에 접근할 수 있음.

액션(Action) : 어떤 일이 일어났는지 알려주는 메시지 같은 것.
예를 들어, "할일 목록에 새로운 할일이 추가되었다"라는 메시지를 보낼 수 있음.

리듀서(Reducer) : 액션을 받아서 앱의 상태를 바꾸는 역할을 하는 함수.
앱이 할일 목록을 가지고 있는데, 새로운 할일이 추가되면 그 목록을 업데이트하는 역할.

스토어(Store) : 앱 안에 있는 모든 정보를 담고 있는 곳.
이런 정보를 통해 앱의 상태가 어떻게 변하는지 추적할 수 있다.


원칙

single source of truth

  • 동일한 데이터는 항상 같은 곳에서 가져온다.
    ↳ 즉, 스토어라는 딱 하나 뿐인 데이터 공간이 있다.

state is read-only

  • 리액트에서는 setState 메소드를 이용해야만 상태 변경이 가능하다.
    ↳ 리덕스에서도 액션이라는 객체를 통해서만 상태 변경이 가능함.

Changes are made with pure function

  • 변경은 순수함수로만 가능하다.
    ↳ 리듀서와 연관되는 개념
    ↓ store(스토어) - Action(액션) - Reducer(리듀서)

사용법

스토어 생성

createStore 함수를 사용하여 스토어를 생성

저티어 해석
스토어를 만든다.
이 스토어는 앱 안에 있는 모든 정보를 담아두는 곳이다.

import { createStore } from 'redux'; // createStore 함수를 사용하여 스토어를 생성
import rootReducer from './reducers'; // 여러 개의 리듀서를 합친 루트 리듀서

const store = createStore(rootReducer); // rootReducer를 사용하여 스토어를 생성

액션 정의

액션을 정의하여 상태 변경에 필요한 정보를 포함시킨다.

저티어 해석
액션을 정의한다.
이 액션은 어떤 일이 일어났는지 알려주는 메시지 같은 것이다.

// 액션을 정의하여 상태 변경에 필요한 정보를 포함시킨다.
const addTodo = (text) => ({
  type: 'ADD_TODO', // 액션의 타입을 정의
  payload: { text } // 추가할 할일의 텍스트 정보를 페이로드(payload)에 포함시킴.
});

리듀서 작성

각 액션에 따라 상태를 어떻게 변경할지 정의하는 리듀서 함수를 작성한다.

저티어 해석
리듀서를 만든다.
이 함수는 액션을 받아서 상태를 어떻게 바꿀지 정의함.

// 각 액션에 따라 상태를 어떻게 변경할지 정의하는 리듀서 함수를 작성한다.
const initialState = {
  todos: [] // 초기 상태로 빈 할일 목록을 설정.
};

// todoReducer 함수는 이전 상태와 액션을 받아서 새로운 상태를 반환.
const todoReducer = (state = initialState, action) => {
  switch(action.type) {
    case 'ADD_TODO': // ADD_TODO 액션이 전달되면
      return {
        ...state, // 기존 상태를 유지한 채
        todos: [...state.todos, { text: action.payload.text, completed: false }] // 새로운 할일을 추가함.
      };
    default:
      return state; // 그 외의 경우에는 기존 상태를 반환.
  }
};

액션 디스패치

dispatch 메서드를 사용하여 액션을 스토어에 전달하여 상태를 변경합니다.

저티어 해석
액션을 보낸다.
이렇게 하면 스토어에 있는 정보가 업데이트 됨.

// dispatch 메서드를 사용하여 액션을 스토어에 전달하여 상태를 변경함.
store.dispatch(addTodo('Buy groceries')); // 'Buy groceries'를 추가하는 액션을 디스패치함.

구독

subscribe 메서드를 사용하여 상태 변경을 감지하고 업데이트한다.

저티어 해석
상태를 감시한다.
상태가 바뀔 때마다 실행할 함수를 등록함.

// subscribe 메서드를 사용하여 상태 변경을 감지하고 업데이트.
store.subscribe(() => {
  console.log('Updated state:', store.getState()); // 상태가 업데이트될 때마다 새로운 상태를 출력함.
});

실습

실습용 파일 만들기
npx create-react-app redux
redux라는 이름의 React 폴더를 만듬

cd redux
redux 폴더로 들어감

npm i redux
redux 설치하기
↳ 리덕스는 라이브러리이기 때문에 추가 설치가 필요함

Store(스토어)

스토어를 일단 생성한다.
데이터 공간을 만드는 거다.

import { createStore } from 'redux';

// 초기 상태 정의
const initialState = {
  todos: []
};

// 리듀서 함수
const todoReducer = (state = initialState, action) => {
  switch(action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.payload.text }]
      };
    case 'REMOVE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload.id)
      };
    default:
      return state;
  }
};

// 스토어 생성
const store = createStore(todoReducer);

redux에 있는 store를 사용한다고 선언하는 코드
import { createStore } from 'redux';

↓ 그 다음으로 스토어(store)를 구축하기 시작 ↓

초기 상태를 빈 상태로 정의하는 코드

const initialState = {
  todos: []
};

↓ 상태 관리를 하는 애플리케이션에서 사용되는 리듀서 함수를 정의 ↓

const todoReducer = (state = initialState, action) => {//코드 내용}
todoReducer 함수는 현재 상태와 액션을 받아들여서 새로운 상태를 반환하는데, 초기 상태가 정의되어 있지 않으면 initialState를 기본값으로 사용한다.

↓ switch 투두리스트 생성, 제거 만들러 ↓

switch 문을 사용하여 액션 타입에 따라 다른 동작을 수행함.

case '무언가':
  return {
  	실행 코드
  };

ADD_TODO, REMOVE_TODO로 할일 생성과 할일 제거를 가능하게 해주는 코드

    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.payload.text }]
      };
    case 'REMOVE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload.id)
      };
    default:
      return state;
  }

'ADD_TODO' 액션 :
새로운 할 일을 추가하기 위한 액션.
새로운 할 일은 현재 시간을 이용하여 고유한 ID를 생성하고, 액션에서 받아온 텍스트를 가진 객체로 구성됨.
기존 상태(state)를 복제하고, todos 배열에 새로운 할 일 객체를 추가하여 새로운 상태를 반환.

'REMOVE_TODO' 액션 :
할 일을 삭제하기 위한 액션.
액션에서 받아온 ID와 일치하지 않는 할 일들로 이루어진 새로운 todos 배열을 만들어서 해당 할 일을 제거한 상태를 반환함.

기본 (default) 동작:
지정된 액션 타입이 없는 경우, 현재 상태를 그대로 반환합니다.

↓ 스토어(store) 생성 ↓

Redux의 createStore 함수를 사용하여 스토어를 생성합.
createStore 함수에 리듀서 함수를 전달하여 스토어를 초기화.
const store = createStore(todoReducer);
리듀서 함수를 이용해 스토어를 등록하면 그에 맞게 상태를 관리하고 ui를 갱신한다.

요약
스토어는 앱의 상태를 담고 있는 객체다.
리듀서는 액션을 통해 상태를 변경하는 함수로, 스토어를 생성할 때 함께 전달.
initialState는 앱의 초기 상태를 정의하는 객체.
이 예제에서는 빈 할일 목록을 초기 상태로 정의했다.

액션(Action)

액션(Action) 정의

const ADD_TODO = 'ADD_TODO';  // 액션 타입 정의
const addTodo = (text) => ({  // 액션 생성 함수
  type: ADD_TODO,
  payload: { text }
});

액션 타입 정의 부분

ADD_TODO라는 문자열을 액션 타입으로 정의함.
액션 타입은 보통 대문자 문자열 상수로 정의됨.
이건 액션을 식별하는 데 사용됨.

액션 생성 함수 부분

addTodo 함수는 할 일을 추가하는 액션을 생성하는 함수입.
이 함수는 text라는 매개변수를 받아와서 액션 객체를 반환함.
반환된 액션 객체는 typepayload 속성을 가짐.

type은 액션의 종류를 식별하는데 사용되는 문자열.
여기서는 위에서 정의한 ADD_TODO를 사용합니다.

payload는 액션과 관련된 데이터를 포함하는 객체.
여기서는 새로운 할 일의 텍스트를 payload로 포함함.

↓ 디스패치 사용하러 ↓

디스패치(dispatch) 사용

store.dispatch(addTodo('할 일 추가'));  // 할일 추가 액션 디스패치

위의 예시에서는 '할 일 추가'라는 텍스트를 가진 새로운 할일을 추가하는 액션을 생성하고, 이를 스토어디스패치하여 상태를 변경하는 것을 보여준다.
이런 식으로 액션을 정의하고 사용하는 것이 리덕스에서 상태를 변경하는 일반적인 패턴이다.

구독

이제 상태 변경을 구독하고, 상태가 변경될 때마다 콘솔에 현재 상태를 출력하는 코드가 추가할 차례.
이렇게 함으로써 스토어의 상태 변경을 실시간으로 감지할 수 있게 만들 예정.

구독
상태를 저장하고, 상태 변경을 실시간으로 감지

// 스토어 구독
store.subscribe(() => {
  console.log('Updated state:', store.getState());
});

이 코드는 Redux 스토어를 구독(subscribe)하는 부분이다.
Redux 스토어의 구독은 상태가 변경될 때마다 특정 동작을 수행할 수 있도록 해준다.
구독을 통해 상태가 업데이트될 때마다 콜백 함수가 호출되어 원하는 작업을 수행할 수 있습니다.

구독
store.subscribe() 함수를 사용하여 스토어를 구독한다.
이 함수는 콜백 함수를 매개변수로 받는다.
이 콜백 함수는 상태가 변경될 때마다 호출됨.

콜백 함수
구독된 상태가 변경될 때마다 호출되는 함수.
여기서는 단순히 변경된 상태를 콘솔에 출력하는 로그 메시지를 포함함.
store.getState() 함수를 사용하여 현재 상태를 가져옴.
이를 통해 상태의 변화를 추적하고 로그에 출력할 수 있음.


결과

import { createStore } from 'redux';

// 액션 타입 정의
const ADD_TODO = 'ADD_TODO';

// 액션 생성 함수
const addTodo = (text) => ({
  type: ADD_TODO,
  payload: { text }
});

// 리듀서 함수
const todoReducer = (state = { todos: [] }, action) => {
  switch(action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.payload.text }]
      };
    default:
      return state;
  }
};

// 스토어 생성
const store = createStore(todoReducer);

// 스토어 구독
store.subscribe(() => {
  console.log('Updated state:', store.getState());
});

// 할일 추가 액션 디스패치
store.dispatch(addTodo('할 일 추가'));

이 코드는 Redux를 사용하여 간단한 할 일 목록을 관리하는 코드이다.
각 부분의 작동 방식과 전체적인 작동 결과를 다시 되새겨 보자.

액션 타입 정의 및 액션 생성 함수
ADD_TODO라는 액션 타입을 정의하고, 이를 사용하여 할 일을 추가하는 액션을 생성하는 addTodo 액션 생성 함수를 정의함.
이 함수는 받은 텍스트를 페이로드로 가지고 ADD_TODO 타입의 액션 객체를 반환한다.

리듀서 함수
todoReducer 함수는 현재의 상태와 액션을 받아들여서 새로운 상태를 반환하는데, ADD_TODO 액션 타입을 처리하여 새로운 할 일을 추가함.
ADD_TODO 액션이 발생하면 현재 상태를 복제한 후, 새로운 할 일을 현재 할 일 목록에 추가하여 새로운 상태를 반환함.

스토어 생성
createStore 함수를 사용하여 Redux 스토어를 생성함.
이 때, 위에서 정의한 리듀서 함수를 전달하여 초기화한다.

스토어 구독
store.subscribe()를 호출하여 스토어를 구독함.
이는 스토어의 상태 변화를 감지하고, 상태가 업데이트될 때마다 등록된 콜백 함수가 호출되도록 설정함.
여기서는 상태가 업데이트될 때마다 현재 상태를 콘솔에 출력하는 콜백 함수를 등록한다.

할일 추가 액션 디스패치
store.dispatch()를 사용하여 addTodo 액션 생성 함수를 호출하고, 반환된 액션 객체를 스토어에 dispatch 한다.
이를 통해 할 일을 추가하는 액션이 발생하게 됨.

↓ 요약 ↓

결과적으로, 할일 추가 액션이 디스패치되면 Redux는 리듀서 함수를 실행하여 상태를 업데이트하고, 이에 따라 구독된 콜백 함수가 호출되어 새로운 상태가 콘솔에 출력된다.
이렇게 구독 및 액션 디스패치를 통해 Redux는 상태의 변화를 감지하고 관리하여 예상대로 동작하게 된다.

↓ 투두리스트를 만들기 위한 코드 추가 ↓

import React, { useState } from 'react';
import { createStore } from 'redux';

// 액션 타입 정의
const ADD_TODO = 'ADD_TODO';

// 액션 생성 함수
const addTodo = (text) => ({
  type: ADD_TODO,
  payload: { text }
});

// 리듀서 함수
const todoReducer = (state = { todos: [] }, action) => {
  switch(action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.payload.text }]
      };
    default:
      return state;
  }
};

// 스토어 생성
const store = createStore(todoReducer);

// 투두리스트 컴포넌트
const TodoList = ({ todos }) => (
  <ul>
    {todos.map(todo => (
      <li key={todo.id}>{todo.text}</li>
    ))}
  </ul>
);

// 앱 컴포넌트
const App = () => {
  // 투두리스트 상태 설정
  const [todos, setTodos] = useState([]);

  // 스토어 구독
  store.subscribe(() => {
    setTodos(store.getState().todos);
  });

  // 할일 추가 함수
  const handleAddTodo = () => {
    const todoText = prompt('할 일을 입력하세요:');
    if (todoText) {
      store.dispatch(addTodo(todoText));
    }
  };

  return (
    <div>
      <h1>투두리스트</h1>
      <TodoList todos={todos} />
      <button onClick={handleAddTodo}>할 일 추가</button>
    </div>
  );
};

export default App;

웹결과

처음 보았을 때

할 일 추가 버튼을 눌렀을 때

할 일 추가하였을 때

이렇게 Redux 공부와 Redux를 이용한 투두리스트 만들기를 해보았다.

profile
프론트엔드 공부 중인 GSM 학생입니다.
post-custom-banner

0개의 댓글