티스토리 뷰

Redux

[REDUX] Redux 개념, 구조, 실습

Hwan'ss 2019. 8. 10. 16:13

Redux란?

- 리액트에서 상태를 더 효율적으로 관리하는 데 사용하는 상태 관리 라이브러리

- 리덕스는 쉽게 설명하면 상태 관리의 로직을 컴포넌트 밖에서 처리 하는 것이다.

- 리덕스를 사용하면 스토어라는 개체 내부에 상태를 담게 된다.

리덕스를 적용한 구조

- 리덕스를 사용하면 위 그림과 같이 스토어에서 모든 상태 관리가 일어난다. 상태에 어떤 변화를 일으켜야 할 때는 액션(Action) 이라는 것을 스토어에 전달한다. 액션은 객체 형태로 되어 있으며, 상태를 변화시킬 때 이 객체를 참조하여 변화를 일으킨다. 액션을 전달하는 과정은 디스패치(dispatch)라고 한다.

- 스토어가 액션을 받으면 리듀서(Reducer)가 전달받은 액션을 기반으로 상태를 어떻게 변경시켜야 할지 정한다. 액션을 처리하면 새 상태를 스토어에 저장한다.

- 스토어 안에 있는 상태가 바뀌면 스토어를 구독하고 있는 컴포넌트에 바로 전달한다. 부모 컴포넌트로 props를 전달하는 작업은 생략하며, 리덕스에 연결하는 함수를 사용하여 컴포넌트를 스토어에 구독시킨다.

 

용어 정리

  • 액션(Action) : 상태 변화를 일으킬 때 참조하는 객체이다.
    • const mapActionToProps = (dispatch) => {   } 함수 사용
    • Action이라는 단어는 Event와 같아고 생각하면 된다.
    • dispatch 인수에서 Ruduce로 넘길 객체(type)를 정의한다.
    • Action이 실행되고 끝나면 type을 반환하는데 type은 Reduce로 전달된다.
  • 스토어(Store) : 애플리케이션의 상태 값들을 내장하고 있다.
    • state 값을 가지고 있다.
    • 중앙에서 변수 관리 개념이라고 생각하면 된다.
    • 리듀스에 의해서만 state의 값이 변경된다.
  • 리듀서(Reducer) : 상태를 변화시키는 로직이 있는 함수이다.
    • ex) export function reducer(state = {state : 10, age:100}, action)
    • Reducer 함수를 생성 할 때 살찐 에로우를 사용하지 않는다. 
    • Reducer 함수는 순수함수여야 한다. 결과 값을 출력 할 때는 파라미터 값에만 의존해야 하며, 언제나 같은 결과를 출력해야 한다.
    • Reducer에서 state를 사용한다면 반드시 state를 초기화 해야 한다.
    • Reducer에서 state의 변화가 일어난다.
    • 값의 갱신은 반드시 reducer에서 해야 한다.
  • State : 컴포넌트에 최종 출력하기 전 거치는 중간과정이다.
    • mpaStateToProps(state) 함수 사용 
    • state는 store에서 가져왔다 라고 생각하면 된다.
    • Store에 저장되어 있는 변수를 가져와서 최종 가공을 위한 목적으로 사용된다.
    • 예를 들어, num:state.num*100 이라고 갱신을 하더라도 실제 num의 값은 갱신되지 않고 컴포넌트에 출력하는 값을 가공한 것이다.
    • 중간 과정을 거치게 되면 중간 수정이 가능하다. 원화를 달러로 바꿀 수 있듯이 가지고 있는 원화를 실제로 출력을 할 때는 달러로 출력을 하게 되는 것이며, 원화는 변화지 않는다.
  • 디스패치(dispatch) : 액션을 스토어에 전달하는 것을 의미한다.
  • 구독 : 스토어 값이 필요한 컴포넌트는 스토어를 구독한다.
    • 리액트 컴포넌트에서 리덕스 스토어를 구독하는 작업은 후에 react-redux의 connect 함수가 대신 한다.
    • 리덕스의 내장 함수를 사용하여 subscribe, unsubscribe 함수를 사용하여 구독 및 구독 취소를 할 수 있다.

Redux 코드 작성 순서

Step1 : Action 함수 mapActionToProps()  작성 (App.js)

Step1

Step2 : Reduce 함수 reducer() 작성 (App.js)

Step2

Step3 : State 함수 mapStateToProps() 작성 (App.js)

Step3

Step4 : 필요한 라이브러리를 설치한다. 

  • npm install redux --save (save는 package-json에 받은 라이브러리가 저장된다.)
  • npm install react-redux --save
  • npm install redux-actions --save

  리듀스를 Store에 등록

  • import App,{reducer} from './App';   // App의 reducer 사용하겠다.
  • import {createStore} from 'redux';  // Step4에서 설치한 라이브러리 import
  • import {Provider} from 'react-redux';

Step4

Step5 : export를 통해 state와 action을 연결

Step5

전체 구조도

리덕스의 흐름은 " A(Action)-R(Reducer)-S(State)

전체 구조도

실습 코드

App.js

// App.js

import React, { Component } from 'react';
import { connect } from 'react-redux';

class App extends Component {
  render() {
    console.log('App');
    return (
      <div>
        <button onClick={this.props.onMyClick}>btn1</button>
        <h1>{this.props.num}</h1>
        <button onClick={this.props.onAgeClick}>btn2</button>
        <h1>{this.props.age}</h1>

      </div>
    );
  }
}

const mapActionToProps = (dispatch) => {
  console.log('actionTo() call');
  console.log('Action');
  
  return {
    onMyClick: () => {
      dispatch({
        type: "MYCLICK", 
      })
    },

    onAgeClick:() => {
      dispatch({
        type : "AGECLICK",
      })
    },

  }
}

export function reducer(state = {num:10, age:100}, action) {
  console.log('reduce');
  
  switch (action.type) {
    case "MYCLICK":
      console.log(state.num); 

      state={
        ...state, 
        num:state.num+1,
      }
      return state;

    case "AGECLICK" :
      state={
        ...state,
        age:state.age-1,
      }
      return state;

    default: return state
  }
}

function mapStateToProps(state) {
  console.log('state');
  return {
    num : state.num,
    age : state.age,
  }
}

export default connect(
    mapStateToProps,
    mapActionToProps, 
)(App);

index.js

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
/////////////////////////////////// 수정 시작
import App,{reducer} from './App'; 
import {createStore} from 'redux';
import {Provider} from 'react-redux';
/////////////////////////////////// 수정 끝
import * as serviceWorker from './serviceWorker';

const store = createStore(reducer);

ReactDOM.render(
<Provider store={store}><App/></Provider>, 
document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

실행결과(btn1 2번 클릭 후 btn2 2번 클릭)

실행결과

리덕스의 세 가지 규칙

1. 스토어는 단 한개

- 스토어는 언제나 단 한개이다. 스토어를 여러 개 생성해서 상태를 관리하면 안된다. 그 대신 리듀서를 여러 개를 만들어서 관리 할 수 있다.

 

2. state는 읽기 전용

- 리덕스의 상태, state는 읽기 전용이다. 이 값은 절대로 직접 수정하면 안된다. 직접 수정을 하게 된다면 구독 함수를 제대로 실행하지 않거나 컴포넌트의 리렌더링이 되지 않을 수 있다.

- 상태를 업데이트 할 때는 언제나 새 상태 객체를 만들어서 넣어 주어야 한다. 업테이트를 할 때마다 새 객체를 만든다면 메모리 누수가 일어나지 않을까? 아니다. 이전에 사용하던 객체들이 메모리에 누적되지 않는다. 상태 레퍼런스가 사라지면 자동으로 메모리 관리를 한다.

 

3. 변화는 순수 함수로 구성

- 모든 변화는 순수 함수로 구성해야 한다. 여기에서 함수는 리듀서 함수를 말한다. 순수 함수에서 결과값을 출력할 때는 파라미터 값에만 의존해야 하며, 같은 파라미터는 언제나 같은 결과를 출력해야 한다.

- 예를 들어 리듀서 함수 내부에서 외부 네트워크와 데이터베이스에 직접 접근하면 안된다. 요청 실패 할 수도 있고, 외부 서버의 반환 값이 변할 수 있기 때문이다.

- 리듀서 함수 내부에서는 현재 날짜를 반환하는 new Date() 함수나 Math.random() 함수 등도 사용 하면 안된다.

 

정리 요약

 리덕스는 더욱 효율적으로 상태 관리를 할 수 있는 라이브러리이다. 스토어에 상태 정보를 가진 객체를 넣어 두고, 액션이 디스패치 되었을 때 리듀서 함수를 이용하여 상태를 변화시키는 것이 주요 역할이다. 그리고 상태가 변화될 때마다 스토어에 구독된 함수를 실행시킨다.

 

 

2019.08.08(목) ~ 2019.08.09(금)

'Redux' 카테고리의 다른 글

[REDUX] Redux - 하위 컴포넌트, children 사용  (0) 2019.08.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함