1. React와 Redux
01. React의 데이터 전달 계층 구도
- 앱의 계층 구도

- 컴포넌트가 상태를 관리하고 속성의 형태로 전달하는 책임을 맡는다

- 이상적인 구성이라면 아래와 같이 각 컴포넌트가 필요로 하는 데이터가 부모에서 자식으로 차례차례 흐른다

그러나 단순 시나리오가 아니라면 불행히도 현실은 그렇게 녹녹치 않다
- 보통의 앱에서는 상태의 생성, 처리, 전달 과정이 아주 많이 일어나기 때문이다
- 어떤 컴포넌트는 상태 변경을 일으킨다, 또 다른 어떤 컴포넌트는 그 상태에 반응한다
- 상태 변경과 관련된 속성은 상태 데이터를 필요로 하는 컴포넌트에 도달하기까지 계층도 아래쪽으로 내려가기도 하지만 또한 위로 올라가기도 한다

- 컴포넌트들 사이에 무분별하게 데이터를 주고 받으면서 발생할 수 있는 몇가지 문제점을 알아야 할 필요가 있다
- 의존성은 코드 관리를 어렵게 만든다. 리액트 목표 중 하나는 스파게티와 같은 복잡한 의존성을 제거하는 것이다, 앱 주변에 데이터가 떠돌게 방치하는 일은 결국 우리가 도망가고 싶은 상황을 만드는 일과 같다
- 상태가 변하거나 속성이 전달될 대 마다 관련된 모든 컴포넌트가 매번 다시 렌더링 된다, 이는 현재 상태에 부합하는 UI의 동기화를 보장하기 위한 자연스러운 동작이다, 그러나 이전에 말했듯 단순히 부모에서 자식으로 값이 전달될 때 많은 컴포넌트들이 불필요한 렌더링을 하게 된다, 이를 방지하기 위해 shouldComponentUpdate 메서드를 재정의하거나 PureComponent를 사용하는 전략이다, 그러나 두 방법 모두 데이터가 증가함에 따라 동기화 작업이 번거롭게 된다는 단점이 있다
- 컴포넌트 계층도 UI에 대한 것이지, 데이터에 대한 것이 아니다 컴포넌트를 배치하고 끼워 넣는 작업은 이미 UI를 작고 관리가 용이하게 분리시키는 좋은 방법이다, 이는 올바른 접근 방법이 맞지만, 상태를 변경시키는 컴포넌트와 그 상태에 반응해야 할 컴포넌트가 직계존비속 관계가 아닌 경우도 많다, 이 경우 위 2번과 마찬가지로 속성이 아주 멀리 전달돼야 하기도 하며, 게다가 종종 한번의 상태 변화에 다수의 전달 작업이 수행되고 한다
02. Redux의 데이터 계층 구도
- 위의 문제들을 해결할 수 있는게 리덕스다, 현재로서는 완벽하게는 아니지만, 거의 그에 가깝게 말이다, 리덕스를 사용하면 각 컴포넌트에 걸쳐 분산돼 있는 애플리케이션의 상태를 모두 데이터 스토어에 저장할 수 있게 해준다

- 이 접근 방법은 여러 문제를 해결해준다, 예건대 어떤 데이터를 앱의 서로 다른 부분들에 공유시키기 위해 컴포넌트 계층의 위아래로 전달시키지 않아도 된다

- 리덕스를 사용하면 변경을 촉발시킬 수 있으며, 오직 직접적으로 영향을 받는 컴포너늩들만 개입할 수 있다, 이는 불필요한 렌더링을 없앨 목적으로 해당 컴포넌트에만 데이터가 전달되게 하는 관리 작업의 과부하를 줄여 준다
03. Redux의 아키텍처 관점
- 스토어를 제외하면 앞으로도 여전히 액션과 리듀서, 그리고 리덕스 파티에 참여하는 다른 조각들에 대해 작업할 것이다, 유일하게 바뀐 사항은 이번에는 리액트로 앱을 만들 것이라는 점이다

04. 리덕스를 이용한 리액트 상태 관리
- 앱이 리덕스 스토어를 참조할 수 있게 한다
- 스토어의 데이터를 필요로 하는 컴포넌트에 액션 생성자, 디스패치 함수, 상태를 속성으로서 매핑
-1. 리액트와 리덕스 합치기
- 이번에 Counter 앱을 만들어서 Counter 컴포넌트 안에 상태 객체를 만들고, 눌러진 버튼에 따라 값이 증가되거나 감소되는 변수를 하나를 두면 될 것이다

- 위 계층구도에서 리덕스를 합류시키면 컴포넌트의 배열이 다소 특이하게 바뀐다
- 초록색 아이템들은 리덕스가 결합된 후에 새로 추가한 것이다
- 첫 번째 단계는 리덕스 스토어로 접근하는 방법을 제공하는 일이며, 이는 Provider Component가 담당
- 두 번째 단계는 어떤 컴포넌트든 디스패치와 액션에 접근 가능하게 하는 일이며, 이는 Connect Component가 담당
- Provider Component는 리액트 앱에서 리덕스를 사용하기 위한 관문 역할을 한다
- 스토어에 대한 참조를 저장하며 앱 안의 모든 컴포넌트에게 스토어로의 접근 방법을 보장
- 컴포넌트 계층도에서 최상위 컴포넌트여야 가능하다
- 그래야만 리덕스의 기능을 전체 앱에 쉽게 전파 할 수 있기 때문이다
- Connect Component는 기능을 담당하는데, Connect는 전통적인 의미의 컴포넌트가 아닌 고차원 컴포넌트(Higher Order Component)이기 때문이다
- 흔히 줄여서 HOC라고 부르며, HOC는 기존 컴포넌트를 래핑하고 HOC 고유의 기능을 추가로 주입함으로써 그 컴포넌트의 기능성을 확장시키는 일관된 방법을 제공
- 말하자면 extends 키워드를 사용해 ES6 클래스로 하는 일과 비슷한, 리액트 만의 방식이라고 생각하면 좋다
- HOC 덕분에 Counter 컴포넌트가 리덕스 스토어와의 작업을 위해 액션과 디스패치에 접근할 수 있게 된다

-2. 시작하기
- create-react-app reduxcounter
- npm install redux
- 리덕스 라이브러리
- 애플리케이션 상태 관리를 위해 리덕스가 제공하는 기본적인 빌딩 블록을 앱에서 사용할 수 있게 해준다
- npm install react-redux
- redux 라이브러리에 대한 리액트의 의존성 패키지
- 언제나 하듯이 src & public 폴더를 깔끔하게 해준다
-3. public / index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Redux Counter</title>
</head>
<body>
<div id="container"></div>
</body>
</html>
-4. src / index.js
- 리듀서를 인자로 받는 createStore 메서드를 사용해 리덕스 스토어를 초기화 해주었다
- 리듀서는 counter 변수에 의해 참조되며. import 구문을 보면 reducer.js라는 파일에 정의돼 있어야 함을 알 수 있다
- 스토어를 만든 다음에 이를 Provider Component의 속성으로 제공
- Priver는 앱의 가장 바깥쪽 컴포넌트로서, 어떤 컴포넌트든 리덕스 스토어와 그 관련 기능에 접근할 수 있도록 보장해준다
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import { Provider } from 'react-redux';
import counter from './reducer';
import App from './App';
import './index.css';
// 스토어
var store = createStore(counter);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>
, document.querySelector("#container")
);

-5. src / reducer.js
- counter 메서드가 하는일을 보면 먼저 상태(state)가 비어있다면 0으로 초기화되는 count 변수가 있다
- increas, decreas라는 두 가지 액션 유형을 다루며, 액션 유형이 increase라면 count 값을 1 증가, decrease라면 count 값을 1 감소 해주는 동작을 한다
// 리듀서
function counter(state, action) {
if (state === undefined) {
return { count: 0 };
}
var count = state.count;
switch (action.type) {
case "increase":
return { count: count + 1 };
case "decrease":
return { count: count - 1 };
default:
return state;
}
}
export default counter;

-6. src / App.js
- 이 코드의 주 목적은 모든 리덕스에 특정적인 후크를 리액트에서 사용할 수 있는 무언가로 바꾸는 것이다
- 더 정확히는 모든 리덕스 후크를 mapStateToProps와 mapDispatchToProps라는 두 함수를 통해 쉽게 소비할 수 있는 속성의 형태로 바꿔 제공한다는 의미다
- mapStateToProps
- 이 함수는 모든 스토어 갱신 작업을 구독하므로, 스토어에 이떤 변경이 일어나도 호출된다
- 그러면 컴포넌트의 속성으로 전달할 스토어 데이터를 담는 객체를 리턴
- mapDispatchToProps
- mapDispatchToProps에서는 액션 생성자의 일을 한다
- 이 함수는 컴포넌트가 스토어를 변경시키는 액션에 디스패치 가능한 두 함수의 이름을 담은 객체를 리턴한다
- increaseCount 함수는 increase 유형의 액션에 디스패치 해준다
- decreaseCount 함수는 decrease 유형의 액션에 디스패치 해준다
- connect
- 이 함수는 예기했던 Connect HOC를 생성시켜준다
- mapStateToProps와 mapDispatchToProps함수를 인자로 받으며, 이를 모두 Counter 컴포넌트로 전달한다
- 보다시피 Counter 컴포넌트 increaseCount, decreaseCount, countValue를 사용할 수 잇게 된다
- 한가지 이상한 점은 render 함수나 그와 비슷한 어떤 것도 없다는 사실이다
- 이는 리액트와 HOC가 자동으로 모든 사항을 처리해주기 때문이다
import { connect } from "react-redux";
import Counter from "./Counter";
// 리덕스 상태를 컴포넌트 속성에 매핑
function mapStateToProps(state) {
return {
countValue: state.count,
};
}
// 액션
var increaseAction = {type : "increase"};
var decreaseAction = {type : "decrease"};
// 리덕스 액션을 컴포넌트 속성에 매핑
function mapDispatchToProps(dispatch) {
return {
increaseCount : function() {
return dispatch(increaseAction);
},
decreaseCount : function() {
return dispatch(decreaseAction);
}
};
}
// HOC
var connectedComponent = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
export default connectedComponent;

-7. src / Counter.js
import React, {Component} from 'react';
class Counter extends Component {
render() {
return(
<div className="contaner">
<button className="buttons" onClick={this.props.decreaseCount}>
-
</button>
<span>{this.props.countValue}</span>
<button className="buttons" onClick={this.props.increaseCount}>
+
</button>
</div>
)
}
}
export default Counter;

-8. src / index.css
body {
margin: 0;
padding: 0;
font-family: sans-serif;
display: flex;
justify-content: center;
background-color: #8E7C93;
}
.contaner {
background-color: #FFF;
margin: 100px;
padding: 10px;
border-radius: 3px;
width: 200px;
display: flex;
align-items: center;
justify-content: space-between;
}
.buttons {
background-color: transparent;
border: none;
font-size: 16px;
font-weight: bold;
border-radius: 3px;
transition: all .15s ease-in;
cursor: pointer;
}
.buttons:hover:nth-child(1) {
background-color: #F45B69;
}
.buttons:hover:nth-child(3) {
background-color: #C0DFA1;
}


