Link Search Menu Expand Document

리덕스 라이브러리 이해하기

  • 컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리
  • Context API를 대체하여 전역 상태를 관리
  • 코드의 유지 보수성을 높이고 작업 효율을 극대화 함
  • 편리한 개발자 도구, 미들웨어를 제공하여 비동기 작업 관리의 효율성을 높임

개념 미리 정리하기

액션(action)

{
    type: 'TOGGLE_VALUE'
    data: {
        id: 1,
        text: '리덕스 배우기'
    }
}
  • 상태의 변화를 나타내는 객체로 type 필드를 반드시 갖고 있음
  • type 이외의 값은 상태를 업데이트할 때 참고하는 값

액션 생성 함수(action creator)

const changeInput = text => ({
    type: 'CHANGE_INPUT',
    text
});
  • 액션 객체를 직접 작성하지 않고 액션 객체를 생성하는 함수를 만들어 관리

리듀서(reducer)

const initialState = {
    counter: 1
};

function reducer(state = initialState, action) {
    switch (action.type) {
        case INCREMENT:
            return {
                counter: state.counter + 1
            };
        default:
            return state;
    }
}
  • 변화를 일으키는 함수
  • 현재 상태와 전달받은 액션 객체를 참고하여 새로운 상태를 만들어서 반환

스토어(store)

  • 스토어는 다음과 같은 항목으로 구성됨
    • 현재 애플리케이션 상태
    • 리듀서
    • 디스패치, 구독 등 몇 개의 내장 함수
  • 한 개의 프로젝트는 단 하나의 스토어만을 가질 수 있음

디스패치(dispatch)

  • 액션을 발생시키는 내장 함수
  • dispatch(action) 형태로 사용
  • 이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜 새로운 상태를 생성

구독(subscribe)

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

unsubscribe(); // 추후 구독을 비활성화할 때 함수를 호출
  • subscribe 함수 안에 리스너 함수를 파라미터로 넣어서 호출할 경우, 액션이 디스패치되어 상태가 업데이트될 때마다 호출됨

리액트 없이 쓰는 리덕스

  • 리덕스는 Angular, Vue와 같은 다른 UI 라이브러리/프레임워크 또는 바닐라 JS와 사용할 수도 있음
// DOM 레퍼런스
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');

// 액션 타입
const TOGGLE_SWITCH = 'TOGGLE_SWITCH';
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';

// 액션 생성 함수
const toggleSwitch = () => ({ type: TOGGLE_SWITCH });
const increase = difference => ({ type: INCREASE, difference });
const decrease = () => ({ type: DECREASE });

// 초깃값 설정
const initialState = {
    toggle: false,
    counter: 0
}

// 리듀서 함수
// state가 undefined일 때는 initialState를 기본값으로 사용
function reducer(state = initialState, action) {
    // action.type에 따라 다른 작업을 처리함
    switch (action.type) {
        case TOGGLE_SWITCH:
            return {
                ...state, // 불변성 유지
                toggle: !state.toggle
            }
        case INCREASE:
            return {
                ...state,
                counter: state.counter + action.difference
            };
        case DECREASE:
            return {
                ...state,
                counter: state.counter - 1
            }
        default:
            return state;
    }
}

// 스토어
const store = createStore(reducer);

// render 함수
const render = () => {
    const state = store.getState(); // 현재 상태 불러옴
    // 토글 처리
    if (state.toggle) {
        divToggle.classList.add('active');
    } else {
        divToggle.classList.remove('active');
    }
    // 카운터 처리
    counter.innerText = state.counter;
};

render();
// 구독
store.subscribe(render);

// 액션 발생시키기 (디스패치)
divToggle.onclick = () => {
    store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
    store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
    store.dispatch(decrease());
};
  • querySelector(selector): selector 조건에 일치하는 첫 번째 element 반환
    • p.warning: <p class='warning'>
    • #: <button id="increase">
  • 액션 이름은 고유해야 함
  • 상태의 불변성을 유지하기 위하여 spread 연산자(...) 사용

리덕스의 세 가지 규칙

단일 스토어

  • 여러 개의 스토어를 사용할 수도 있으나 상태 관리가 복잡해질 수 있으므로 권장하지 않음

읽기 전용 상태

  • 상태를 업데이트할 때 기존의 객체를 변경하지 않고 새로운 객체를 생성하여 불변성 유지

리듀서는 순수한 함수

  • 순수한 함수는 다음과 같은 원칙을 지켜야 함
    1. 리듀서 함수는 이전 상태와 액션 객체를 파라미터로 받음
    2. 파라미터 외의 값에는 의존하면 안됨
    3. 이전 상태는 절대로 건드리지 않고, 변화를 준 새로운 상태 객체를 만들어서 반환
    4. 똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과 값을 반환
  • 파라미터가 같아도 다른 값을 만들 수 있는 행위
    • 무작위 값 생성
    • 현재 시간 가져오기
    • 네트워크 요청
  • 네트워크 요청과 같은 비동기 작업은 미들웨어를 통해 관리