본문으로 바로가기

리덕스는 리액트의 상태 관리 라이브러리이다

16-1. 개념 정리


16-1-1. 액션


상태에 어떤 변화가 필요하면 액션이 발생한다 이 액션은 객체로 표현되고 액션 객체는 다음과 같은 형식으로 이루어져 있다

{
    type: 'TOGGLE_VALUE'
}

액션 객체는 위 처럼 type 필드를 반드시 가지고 있어야한다 이 type이 액션 객체의 이름이된다

16-1-2. 액션 생성 함수


말 그대로 액션 객체를 만들어주는 함수이다

function addTodo(data) {
        return {
                type: 'ADD_TODO',
                data
        };
}

액션 객체를 매번 직접 작성하게되면 실수할 가능성이 있기 때문에 함수로 틀을 만들어 둔 뒤 생성을 관리한다

16-1-3. 리듀서


리듀서는 변화를 일으키는 함수다

액션을 만들어서 발생시키면 리듀서가 현재 상태와 액션 객체를 받아온다

그 뒤에 액션이 지시하는대로 상태를 생성해서 반환한다

const initialState = {
    counter:1
};
function reducer(state = initialState, action){
    switch (action.type) {
        case INCREASE :
            return {
                counter : state.counter +1
            };
        default :
            return state;
    }
}

16-1-4. 스토어


프로젝트에 리덕스를 적용하기 위해 스토어를 만든다

하나의 프로젝트에 하나의 스토어만 가질 수 있으며 스토어 안에는 현재 애플리케이션 상태와 리듀서 그리고 내장 함수들을 가지고 있다

16-1-5. 디스패치


디스패치는 스토어의 내장 함수 중 하나이다

디스패치는 액션을 발생시키는데 이 디스패치는 useReducer에서 사용한 것 과 같이

dispatch(action);

액션 객체를 파라미터로 넣어서 호출한다

이렇게 호출하면 액션이 발생하기 때문에 스토어에서 리듀서 함수를 실행시키고 리듀서는 다시 액션과 상태 값을 가져가서 변화를 만들어내는 것이다

16-1-6. 구독


구독도 스토어의 내장 함수 중에 하나다

subscribe 함수 안에 리스너 함수를 파라미터로 넣어서 호출해주면 이 리스너 함수가 액션이 디스패치되어 상태가 업데이트될 때마다 호출된다

얘는 뭔소린지 모르겠다 밑에서 실습하다 보면 알 수 있을듯

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

unsubscribe();

이런식으로 사용한다고 한다

subscribe


store.subscribe(listener);

이렇게만 해 주어도 프로젝트에서 상태의 변화가 일어날 때 listener함수가 실행된다

const unsubscribe = store.subscribe(listener); //상태변화에 따라 listener가 실행됨

unsubscribe(); // 이제부터 상태변화에 따라 listener가 실행되지 않음

하지만 이렇게 subscribe을 할당해 줄 때 변수에 담아두면 차후 구독을 해제할 때 사용할 수 있다

16-2. 리액트 없이 쓰는 리덕스


리덕스는 리액트에 종속되는 라이브러리가 아니다 그렇기 때문에 다른 라이브러리나 프레임워크와 함께 사용할 수 있는데

우선 parcel을 이용해서 간단한 바닐라 자바스크립트와 리덕스를 사용한 프로젝트를 만들며 작동 원리를 익혀보자

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="index.css">
    <meta charset="UTF-8">
    <title>vanilla js</title>
</head>
<body>
<div class="toggle"></div>
<hr>
<h1>0</h1>
    <button id="increase">+1</button>
    <button id="decrease">+1</button>
    <script src="index.js"></script>
</body>
</html>

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5c3a0993-be22-4866-900c-ec0bc1b766ae/Untitled.png

html, css의 구성은 위와 같다

16-2-1. 액션 타입과 액션 생성 함수 정의


프로젝트의 상태에 변화를 일으키는 것을 액션이라고 한다

액션의 이름은 대문자의 문자열 형태로 작성하며 고유해야한다

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});

액션 객체는 반드시 type값을 가지고 있어야 한다

16-2-2. 초깃값 설정


프로젝트에서 사용할 초깃값을 설정하는데 이 때 초깃값의 형태는 자유이다

const initialState = {
    toggle : false,
    counter : 0,
};

16-2-3. 리듀서 함수 정의


리듀서는 변화를 일으키는 함수로 위에서 만들어준 action과 프로젝트의 상태값을 받아온다

function reducer(state = initialState, action){
    switch (action.type){
        case TOGGLE_SWITCH :
            return {...state, toggle: !state.toggle};
        case INCREASE :
            return {...state, counter: action.counter + action.difference};
        case DECREASE :
            return {...state, counter: state.counter-1};
        default :
            return state;
    }
}

제일 처음 reducer함수가 호출될 경우에는 state의 값이 undefine이기 때문에 그 때를 위해서 initialState를 파라미터에 기본값으로 설정해주었다

16-2-4. 스토어 만들기


스토어를 만들 때는 creatStore함수를 사용한다

const store = createStore(reducer);

스토어 함수를 만들 때 파라미터로 리듀서 함수를 넣어 주어야 한다

16-2-5. render 함수 만들기


내가 만들 render함수는 상태가 변경될 때마다 호출되며 리액트의 render와는 다르게 html을 사용하여 만들어진 UI의 속성을 상태에 따라 변경한다

const render = ()=>{
    const state = store.getState();
    if(state.toggle) divToggle.classList.toggle('active');
    counter.innerText = state.counter;
}
render();

16-2-6. 구독하기


스토어의 상태가 바뀔 때 마다 방금 만들어준 render함수가 호출되도록 해 줄 건데 이 작업은 스토어의 내장 함수인 subscribe을 사용한다

subscribe 함수는 파라미터로 함수 형태의 값을 전달 받는다

store.subscribe(render);

16-2-7. 액션 발생시키기


액션을 발생시키는 것을 디스패치라고 하고 스토어의 내장 함수 dispatch를 사용해서 발생시킬 수 있다

파라미터로는 액션 객체를 받아서 위에서 설명한대로 작동하게 된다

divToggle.onclick = ()=>{
    store.dispatch(toggleSwitch());
};
btnIncrease.onclick = ()=>{
    store.dispatch(increase(1));
};
btnDecrease.onclick = ()=>{
    store.dispatch(decrease());
};

각각의 클릭 이벤트가 발생하면 액션을 발생시켜서 리듀스 함수가 실행되고 스토어의 상태값이 바뀌기 때문에 subscribe 함수로 구독해 두었던 render함수가 실행되어서 바뀐 상태값에 따라 화면이 렌더링된다.

16-3. 리덕스의 세 가지 규칙


16-3-1. 단일 스토어


하나의 프로젝트에는 하나의 스토어만 가질 수 있다고 했지만 사실 여러 개의 스토어를 사용하는 것도 가능하다

특정 상태값의 업데이트가 너무 빈번하게 일어나면 완전히 분리시켜서 따로 스토어를 만들 수도 있지만 상태 관리가 복잡해질 수 있기 때문에 권장하지 않는다

16-3-2. 읽기 전용 상태


리액트에서 setState를 이용해서 state를 업데이트 하는 이유와 같다

새로운 객체를 생성해서 업데이트해 주어야 이전 객체와 비교(얕은 비교)를 할 때 더 나은 성능을 얻을 수 있기 때문

16-3-3. 리듀스는 순수한 함수


변화를 일으키는 리듀서 함수는 순수 함수이다 동일한 파라미터가 들어가면 항상 동일한 결과 값이 나와야하는 것

그래서 리듀스 함수 내부에서 변화할 수 있는 값이 들어가선 안된다 (ex: 랜덤 값 생성, 네트워크 요청..)