Redux란

Redux는 자바스크립트 앱의 state(data 값을 관리해주는 하나의 도구, 이벤트 루프라고 함. React 뿐만 아니라 jquery, Angular 과도 같이 사용할 수 있습니다.

배경지식

컴포넌트간 데이터 공유? 스파게티 코드?

프로젝트 규모가 커질수록 다양한 컴포넌트가 생성이되고, 각 컴포넌트는 다양한 데이터를 요구하게된다. 컴포넌트마다 가지고 있는 데이터를 공유하기 위해 리액트의 데이터 흐름을 깨버릴 수는 없습니다. 혹 그런 방식으로 공유를 하게 되면 스파게티 코드가 만들어지게 된다. 리액트의 parent-child 데이터 교류방법을 준수하는 방법도 큰 프로젝트일 경우 혼란스럽게 된다. 이런 문제점을 해결하기 위해 페이스북에서는 flux 패턴을 디자인하였고, Redux라이브러리를 통해 리액트에 적용할 수 있다.

MVC

model, view, controller로 나뉘는 개발 디자인패턴이다. 참고
모델과 뷰의 양이 기하급수적으로 늘어날경우 프로젝트는 점점 복잡해지게 된다. 이런 문제를 해결하기 위해 만들어진게 flux패턴.

FLUX

action -> dispatcher -> store -> view. 위의 순서대로 작업이 일어나게된다. action을 통해 이벤트가 일어나게 되면, dispatcher가 action을 통제하여 store에 업데이트를 한다. store에서 업데이트된 데이터를 토대로 현재 view가 수정되어야할경우 리렌더링이 일어난다.
dispatcher는 작업이 중첩되면 안된다. 이 사항을 고려해야할듯.

REDUX

위의 내용에서 알 수 있듯이 redux는 리액트에 적절한 경우 꼭 필요한 라이브러리이다. 소규모 프로젝트나 복잡한 데이터 관계에 있지 않은 컴포넌트들은 굳이 redux를 사용하며 스스로를 힘들게 만들 필요는 없겠다.
flux패턴에서 알 수 있듯이 store가 각 컴포넌트의 데이터를 포함하고 있다. 이 store의 특정 데이터에서 변경이 일어날경우(action > dispatcher)이 데이터를 바라보고 있던 view컴포넌트들에서 데이터를 업데이트 하는 방식이다.

3가지 원칙

  1. Single Source of Truth
    단 하나의 store를 사용한다. flux를 토대로 만들어진 라이브러리이지만, 여러개의 store를 사용하는 flux와 다르게 단 하나의 store만 사용하며, 이 안에 모든 state값들이 포함되어있다. 그래서 ‘전지전능한 진리’이다. store에는 데이터가 nested 된 구조로 이루어져있다.
  2. State is Read-only
    store안에 있는 모든 state값들은 ‘읽기’전용 데이터들이다. 컴포넌트에서 직접 state값을 수정할 수 없다. 이를 가능하게 하려면 action > dispatcher 구조를 통해야한다. 객체를 통해 ‘어떠한’ 변화를 ‘무슨’ 값들과 함께 일어날지 store에 dispatch해줌으로 써 state들을 변경하게된다.
  3. Changes are made with pure functions
    위에서 설명했듯이 state를 변경하려면 action > dispatcher를 통해야만한다. 이때 받아온 action객체를 처리하는 것이 reducer입니다. action은 어떤 변화가 일어날지를 알려준다면 reducer는 이때 받은 값을 토대로 변화를 일으킵니다. reducer에 대해선 다음과 같습니다

    • 외부 네트워크 혹은 데이터베이스에 접근하지 않아야한다.
    • return 값은 오직 parameter 값에만 의존되어야한다.
    • 인수는 변경되지 않아야한다.
    • 같은 인수로 실행된 함수는 언제나 같은 결과를 반환해야한다.
    • 순수하지 않은 API 호출을 하지 말아야 한다. (Date 및 Math 의 함수 등)

고무곰님 강의

리덕스는 store 라는 객체에 상태를 저장함. 이걸 컴포넌트는 props로 가져와서 사용할 수 있음. store가 최상위 컴포넌트라고 보면되고, 만약 일반 컴포넌트가 연결을 요청하면 direct한 상하(부모)관계를 정립할 수 있다.

상태를 변경하는 유일한 방법은 action을 사용해서 값을 보낸다(dispatch). action은 무엇이 일어날지를 서술하는 객체이다. 이 action을 통해 상태를 어떻게 변경해야하는지 명시하기 위해서는 reducer를 사용한다.

Container Component

연결된 컴포넌트
데이터를 갖고 있고, presentational component에게 날려주는 역할

Presentational Component

연결되지 않은 컴포넌트(보여지는 컴포넌트)
container component로부터 값을 받아서 표현한다.(마크업, 스타일)

  Presentational Components Container Components
Purpose How things look (markup, styles) How things work (data fetching, state updates)
Aware of Redux No Yes
To read data Read data from props Subscribe to Redux state
To change data Invoke callbacks from props Dispatch Redux actions
Are written By hand Usually generated by React Redux

스테이트가 바뀐것을 한번에 여러 컴포넌트에 적용시킬 수 있게된다??? ㅇㅁㅇ

폴더 구조 예시

redux directory (작은 SPA 에 적합함) /action /Action.js /component /App.js … /reducer /reducer.js /store.js

큰 SPA에 적합 /Bank /action /component /reducer /Tab /action /component /reducer /store.js

요런 방법도 있음 /action /Bank/action.js /Tab/action.js /component …

폴더구조가 많으면 import시 경로 맞추는게 힘들다. 폴더가 간략하면 파일을 찾기가 힘들다.

connect

container component 와 store를 연결하려면 connect를 해야한다.
connect가 물고 있는거는 및에 두명
객체 이름은 mapStateToProps, mapDispatchToProps(앱에서 호출할 메서드를 여기에 저장) 등등

import { connect } from 'react-redux';

// 인자로 상태를 받음, 객체를 반환. 상태용??
const mapStateToProps = state => ({

});

// dispatch를 props로 바꿔줌. 메서드용?
const mapDispatchToProps = state => ({

});

// .... codes ....

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

action

// 이렇게 action을 정의 할 수 있다.
{
  type: 'SAVE_MONEY',
  money	
}

// 요 칭구들은 함수로 action을 만드는 애들임. 이를 action creator 라고 부름.
const save = money => ({
  type: 'SAVE_MONEY',
  money
});

export default {
  save
}

// 또는
export const withdraw = money => ({
  type: 'WITHDRAW_MONEY',
  money
});
// action creator 호출방법
// action creator 를 호출해보자!
calc: (type, money) => dispatch(
  BankAction[type](money)
)

// BankAction[type](money)
// BankAction['save'](1)
// === BankAction.save(1)
// === {type: SAVE_MONEY, money: 1}

컴포넌트와 action은 연결되어 있고, action은 export 만 해줌.. 반면에 reducer는 아무것도 모름.. 단지 store에서 받아와서 값을 변환함. 일련의 action, 메서드가 호출될경우 변수값이 모두 동일한 이름으로 되어있을경우 팔로우 하기 힘들다..
따라서 component -> action 소문자
action -> reducer 대문자 로 구분하는것이다

reducer

reducer 는 보통 switchcase를 많이 사용함

// action
{
  type: 'SAVE_MONEY',
  money 
}


//reducer
const reducer = (prevState, action) => {
  switch(action.type) {
    case 'SAVE_MONEY' :
    return {
      ...
    }
    default ...
  }
}

reducer 는 이전 값을 받아와서 새로운 값을 부여해주는것임…?? ㅇㅁㅇ

combine reducer

combineReducers ({key: value}) combineReducers를 사용하면 depth가 한단계 깊어진다.

리듀서에 동일한 ‘값’이 있을경우, 액션을 통해서 그에 해당한느 일을 하게된다… 복잡스 리듀서의 스테이트는 자기한테 할당되어있는것만 받아서, 그것만 가지고 작업하게된다고한다..!!

최초의 값

store 안에 있는 값이 container component 가 willMount 시점에 가져온다.
가져온다 == reducer를 돌린다.
따라서 reducer에서 값이 없을경우? 예외처리 값이 없다면, default로 보내는 값을 최초 값이라고 생각하고 설정하면 될듯하다..

store

react와 전혀 무관한 독립된 객체임~~~

따라서 그냥 지금 만든거 복붙해서 사용해도 될듯 ㅇㅅㅇ

redux 를 사용해서 좋은점

data 분리
메서드를 다 적을필요 없이 필요한것만 mapDispatchToProps만 보면 된다.
역할분리가 확실함!! 데이터와 관련된것은 action과 reducer로 쪼개서 나눠준다!! 두개 다 순수함수로 이뤄져 있다. 함수 == 보기 편한 코드! ㅇㅅㅇ

문제가 발생하면 어떤 파일을 수정하면 될지 알기 쉽다!! 그렇다고 이게 mvc 패턴이 된것은 아님!!

middleware

실험

Redux를 이해하기 위해서 다양한 블로그에서 알려주는 예제를 따라해 보았다. 참고한 사이트는 다음과 같다.
네이버 D2 react, redux
Velopert Redux 정복하기

알아볼것

  1. Publish and Subscription Design Pattern

참고

redux 사이트 참고: redux가 flux보다 나은 이유, redux개발자의 답변