[리액트를 다루는 기술] 16장 리덕스 라이브러리 이해하기

  • 리덕스 : 리액트 상태 관리 라이브러리

    무엇이 장점?

    1. 컴포넌트 상태 업데이트 관련 로직을 다른 파일로 분리 -> 더욱 효율적으로 관리 가능
    2. 컴포넌트끼리 똑같은 상태를 공유해야할때 -> 여러 컴포넌트를 거치지 x
    3. 전역상태를 관리할 때 굉장히 효과적..!(Context API 보다 더욱 체계적으로 관리)
    4. 코드의 유지보수성을 높여줌, 작업효율 극대화
      ++ 편리한 개발자도구, 미들웨어 기능 제공 -> 비동기작업 효율적 관리 가능
  • 실습진행

    핵심키워드 알아보기 - Parcel로 프로젝트 구성 - 토글 스위치와 카운터 구현

16.1 개념 미리 정리하기

  • 개념을 익혀보장 ^^~

16.1.1 액션

  • 액션 : 상태에 어떠한 변화가 필요할때 발생, 하나의 객체로 표현
{
    type: 'TOGGLE_VALUE'
    data: {
        id: 1,
        text: '리덕스 배우기'
    }
    
}
{
    type: 'CHANGE_INPUT',
    text: '안녕하세요'
}
  • type : 액션의 이름
  • 이외의 값 : 상태 업데이트시 참고, 커스텀 가능

16.1.2 액션 생성 함수

  • 액션생성함수 : 액션객체를 만들어 주는 함수
function addTodo(data) {
    return {
        type: 'ADD_TODO',
        data
    };
}

// 화살표 함수로도 가능
const changeInput = text => {{
    type: 'CHANGE_INPUT',
    text
}};
  • 액션객체 일일이 만들기 귀찮 -> 함수로 만들어 관리

16.1.3 리듀서

  • 리듀서(reducer) : 변화를 일으키는 함수
  • 액션 만들어 발생 -> 리듀서가 파라미터(현재상태, 전달받은액션객체)로 받음 -> 두 값을 참조 -> 새로운 상태로 만들어 반환
const initialState = {
    counter: 1
};
function reducer(state = initialState, action){
    switch(action.type){
        return {
            counter: state.counter + 1
        };
        default:
            return state;
    }
}

16.1.4 스토어

  • 스토어(store) : 프로젝트에 리덕스를 적용하기 위해 만듬, 1프젝 1스토어 가능, 스토어 안에는 현재 어플리케이션 상태, 리듀서, 몇가지 내장함수가 들어가 있음

16.1.5 디스패치

  • 디스패치(dispatch) : 스토어의 내장함수중 하나, 액션을 발생시키는 것, dispatch(action)과 같은 형태로 파라미터로 넣어 호출

16.1.6 구독

  • 구독(subscribe) : 스토어의 내장함수중 하나
  • subscribe 함수내 리스너 함수를 파라미터로 호출시 -> 액션이 디스패치됨 -> 상태가 업데이트 될때마다 호출
const listener = () => {
console.log(‘상태가 업데이트됨‘);

}
const unsubscribe = store.subscribe(listener);


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

16.2 리액트 없이 쓰는 리덕스

  • 리덕스에 대해..

    리액트에 종속되는 라이브러리가 x
    리액트에서 사용하려고 만들어졌지만 다른 UI 라이브러리/프레임워크와 함께 사용 가능(angular-redux, ember redux, Vue, 바닐라(vanilla) 자바스크립트에서도 사용할 수 있음)

16.2.1 Parcel로 프로젝트 만들기

  • parcel-bundler 설치
npm install -g parcel-bundler
  • package.json 파일 생성
 mkdir vanilla-redux
 cd vanilla-redux
// package.json 파일을 생성합니다.
 npm init -y 
  • index.html , index.js 각각 생성
<html>
  <body>
    <div>바닐라 자바스크립트</div>
    <script src=”./index.js“></script>
  </body>
</html>
console.log('hello parcel');
  • 다음 명령어로 개발용 서버 실행하기(실행오류)
parcel index.html
  • 리덕스 모듈설치
npm add redux

16.2.2 간단한 UI 구성하기

  • index.css
.toggle {
  border: 2px solid black;
  width: 64px;
  height: 64px;
  border-radius: 32px;
  box-sizing: border-box;
}


.toggle.active {
  background: yellow;
}
  • index.html 수정
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="index.css" />
  </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>

16.2.3 DOM 레퍼런스 만들기

  • DOM 직접 수정해야함 -> 별도의 라이브러리를 사용하지 않아서
  • index.js
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#increase');
const btnDecrease = document.querySelector('#decrease');

16.2.4 액션 타입과 액션 생성 함수 정의

  • 액션에 이름 정의 -> 문자열 형태, 대문자, 고유한 특성
  • 액션객체는 type 값을 반드시 갖고 있어야함
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#id');
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.5 초깃값 설정

  • 초깃값의 형태는 자유(숫자, 문자열, 객체 등)
  • index.js 수정
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
};

16.2.6 리듀서 함수 정의

  • 리듀서 : 변화를 일으키는 함수
  • state와 action 값을 받아옴
  • index.js 수정
const divToggle = document.querySelector('.toggle');
const counter = document.querySelector('h1');
const btnIncrease = document.querySelector('#id');
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;
  }
}

16.2.7 스토어 만들기

  • createStore 함수 사용 -> 리덕스에서 해당 함수를 불러옴, 함수 파라미터에는 리듀서 함수를 넣어줌
import { createStore } from 'redux';

(...)

const store = createStore(reducer);

16.2.8 render 함수 만들기

  • render 함수 : 상태가 업데이트 될때마다 호출
  • index.js 수정
(...)

const store = createStore(reducer);



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



render();

16.2.9 구독하기

  • 스토어 상태 바뀔때마다 render 함수 호출되게 하기
  • subscribe 함수의 파라미터로는 함수 형태의 값을 전달해 줌
  • 나중에는 사용 xs

    컴포넌트에서 리덕스 상태를 조회하는 과정에서 react-redux라는 라이브러리가 이 작업을 대신해 주기 때문

(...)
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);

16.2.10 액션 발생시키기

  • 디스패치 : 액션을 발생시키는 것
  • index.js 수정
(...)
divToggle.onclick = () => {
  store.dispatch(toggleSwitch());
};
btnIncrease.onclick = () => {
  store.dispatch(increase(1));
};
btnDecrease.onclick = () => {
  store.dispatch(decrease());
};

16.3 리덕스의 세 가지 규칙

16.3.1 단일 스토어

  • 상태관리가 복잡해질수 있으므로 권장 x

16.3.2 읽기 전용 상태

  • 리덕스도 리액트 처럼 불변성을 지켜주어야함
  • 상태를 업데이트할 때 기존의 객체는 건드리지 않고 새로운 객체를 생성해 주어야 함
  • WHY? 내부적으로 데이터가 변경되는 것을 감지하기 위해 얕은 비교(shallow equality) 검사를 하기 때문

16.3.3 리듀서는 순수한 함수

  • 순수한 함수의 조건
    1. 리듀서 함수는 이전 상태와 액션 객체를 파라미터로 받음
    2. 파라미터 외의 값에는 의존하면 x
    3. 이전 상태는 절대로 건드리지 않고, 변화를 준 새로운 상태 객체를 만들어서 반환
    4. 똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과 값을 반환해야함

좋은 웹페이지 즐겨찾기