Skip to content

[Redux] Redux Basics

🔶 개요

◾ 리덕스란?

  • 리액트 상태 관리 라이브러리 (가장 많이 사용하는)
  • 컴포넌트 상태 업데이트 관련 로직을 다른 파일로 분리 → 더 효율적인 관리 가능
  • 컴포넌트끼리 똑같은 상태 공유 필요 시 → 여러 컴포넌트 거치지 않고 손쉽게 상태 값 전달/업데이트 가능
  • 최적화(실제 업데이트 필요한 컴포넌트만 리렌더링)
  • 전역 상태 관리 시 → 효과적

    • Context API - 같은 작업 가능. (리액트 v16.3 릴리즈로 개선 전, 사용 방식 불편했음) - 단순 전역 상태 관리만 필요 시→ 충분

    • ↕ 리덕스와 비교 - 상태 관리 더 체계적 → 플젝 규모 클 때 좋음 - 코드 유지 보수 ⬆ - 작업 효율 극대화 ⬆ - 개발자 도구 지원 (매우 편리) - 미들웨어 (기능) 제공 → 비동기 작업 훨씬 효율적 관리 가능

◾ 리액트 종속 라이브러리 x

  • 리액트에서 사용하려고 만들어졌지만
  • 다른 UI 라이브러리/프레임워크와 함께 사용 가능
    • 바닐라 자바스크립트(라이브러리/프레임워크 없이 사용하는 순수 자바스크립트 그 자체)와 함께 사용 가능

🔷 개념

◾ 액션 (action)

  • 발생 : 상태에 변화 필요 시
  • 하나의 객체로 표현
  • 형식

                // 1
                {
                    type: 'TOGGLE_VALUE'
                }
    
                // 2
                {
                    type: 'ADD_TODO',
                    data: {
                        id: 1,
                        text: '리덕스 배우기'
                    }
                }
    
                // 3
                {
                    type: 'CHANGE_INPUT',
                    text: '안녕하세요'
                }
    

  • type

  • 필수
  • 액션 이름 같은 것
    • 문자열 형태
    • 주로 대문자
    • 고유해야
  • type 외 값
  • 추후, 상태 업데이트 시 참고할 값
  • 작성자 마음대로

◾ 액션 생성 함수 (action creator)

  • 액션 객체 만들어주는 함수
  • 형식

                // 화살표 함수
                const changeInput = text => ({
                    type: 'CHANGE_INPUT',
                    text
                })
    
                // 일반 함수
                function addTodo(data) {
                    return {
                        type: 'ADD_TODO',
                        DATA
                    }
                }
    

  • 사용 이유

  • 변화 만들 때마다 액션 객체 만들어야 함.

    → 매번 (액션 객체를) 직접 작성하기 번거로움

    → 실수로 정보 놓칠 수 있음

    ⇒ 이런 일 방지

◾ 리듀서 (reducer)

  • 변화를 일으키는 함수
  • 실행 과정
  • 액션 만들어서 발생 → 리듀서의 parameter: 현재 상태 & 전달받은 액션 객체 받음 → 두 값 참고 → 새로운 상태 반환
  • 형태

                const initialState = {
                    counter: 1
                }
    
                // state가 undefined일 때는 initialState를 기본값으로 사용
                function reducer(state = initialState, action) {
                    switch (action.type) {
                        case INCREMENT:
                            return {
                                ...state, // 불변성 유지 (spread 연산자 사용 예시)
                                counter: state.counter + 1
                            };
                        default: 
                            return state;
                    }
                }
    

  • 세팅

  • 리듀서 첫 호출 시, state 값 = undefined

    → undefined일 때, initialState을 기본 값으로 설정하기 위해 함수 파라미터에 기본 값 설정

  • 상태의 불변성 유지하면서 데이터 변화 시켜야 함

    → spread 연산자(...) 사용

    • 🤚 리덕스 상태는 최대한 깊지 않은 구조로 진행 추천
      • 이유: spread 연산자로 불변성 관리&업데이트가 번거롭지 않기 위해
    • 객체 구조 복잡 or 배열도 다룰 경우 → immer라이브러리 사용하면 쉽게 리듀서 작성 가능
    • 루트 리듀서 (rootReducer)
    • 스토어 만들 때 (createStore 함수를 사용하여) 리듀서 1개만 사용해야 함

→ 리듀서를 1개로 합쳐야 함

combineReducers 유틸 함수 (리덕스 제공) 사용하면 쉬움

  • 형태 예시
                import { combineReducers } from 'redux';
                import counter from './counter'; // 리듀서 1
                import todos from './todos'; // 리듀서 2

                const rootReducer = combineReducers({
                  counter,
                  todos,
                });

                export default rootReducer;

◾ 스토어 (store)

  • 플젝 1개 당 store 1개
  • 스토어 내부
  • 현재 어플리케이션 상태
  • 리듀서
  • 몇 가지 중요한 내장 함수
  • 만드는 법 & 형식
  • 만드는 법

    • createStore 함수 사용

    → 함수의 parameter에 리듀서 함수 넣기

  • 형식

                import {createStore} from 'redux';

                (...)

                const store = createStore(reducer);

◾ 디스패치 (dispatch)

  • (스토어 내장 함수 中 하나)
  • 역할
  • 액션 발생시킴.
  • 액션을 스토어에 전달
  • 사용 방식
  • 형태: dispatch(action)

→ 액션 객체를 파라미터로 넣어 호출

→ 호출 후: 스토어- 리듀서 함수 실행 → 새로운 상태 만듦

◾ 구독 (subscribe)

  • 대체제: react-redux
  • (스토어 내장 함수 中 하나)
  • subscribe 함수 내부에 함수(listener)를 파라미터로 넣어 호출 시

→ 함수( listener )가 액션이 디스패치되어 상태가 업데이트 될 때마다 호출

  • 형태
            const listener = () => {
                console.log('상태가 업데이트됨');
            }
            const unsubscribe = store.subscribe(listener);

            unsubscribe(); // 추후 구독을 비활성화할 때 함수를 호출

🔶 리덕스의 세 가지 규칙

1. 단일 스토어

  • 추천: 1 어플리케이션 : 1 스토어
  • 이유
  • 여러 스토어 사용 가능→ 상태 관리 복잡해질 가능성 多

2. 읽기 전용 상태

  • 리덕스 상태는 읽기 전용

→ 상태 업데이트 할 때

- 기존 객체는 건드리지 x
- 새 객체 생성
  • 불변성 유지 이유
  • 얕은 비교 검사 하기 때문

    *얕은 비교 검사: 내부적으로 데이터 변경 감지하기 위함

3. 리듀서는 순수한 함수

  • 조건
  • 리듀서 함수의 parameter: 이전 상태 & 액션 객체
  • parameter 외의 값→ 의존 x
  • 이전 상태 건드리지 x & 변화 준 새 상태 객체를 만들어 반환
  • 같은 parameter 호출된 리듀서 함수 → 같은 값 반환

🔷 리덕스 사용 패턴

프레젠테이셔널 / 컨테이너 컴포넌트 분리 (필수 X)

  • 프레젠테이셔널 컴포넌트
  • UI 컴포넌트 (상태 관리 X)
  • 컨테이너 컴포넌트
  • 리덕스 연동 컴포넌트
    • 리덕스로부터 상태 받아옴
    • 리덕스 스토어에 액션을 디스패치
  • 장점
  • 코드 재사용성 ⬆
  • 관심사 분리 → UI 작성시 좀 더 집중 가능

◾ 파일 구분 방식

  • 리덕스 사용 시
  • 액션 타입, 액션 생성 함수, 리듀서 코드 작성
  • 일반적인 구조

    • 디렉토리: actions, constants, reducers
    • 내부: 기능 별로 파일 1개씩

  • Ducks 패턴

    • 액션 타입, 액션 생성 함수, 리듀서 → 기능 별로 파일 1개에 몰아서 다 작성

  • 참고 '리액트를 다루는 기술'_저자 김민준


작성자: 정선미

작성일: 2021-07-18