How to Make it ?
앞서 무엇을 만들지 설명을 하지 않았다.
대부분에 boilerplate
는 찾아보면 카운터를 예로 들고 있다.
뭘만들지 고민해보다가 About
페이지에 게시판을 만들기로 결정 했다.
DB가 없기 때문에 JSON 방식으로 더미 데이터를 저장해 놓고
Promise 를 활용해서 Api 처럼 활용 가능 하도록 MockApi 를 구현할 계획 이다.
Router
components 폴더에 컴포넌트 별로 폴더를 만들고
About, Header, Main
파일을 생성한다.
앞서 만들었던 Hello
컴포넌트를 삭제한다.
Header 컴포넌트에 라우트 Main
과 About
2개를 정의할것 이다.
앞서 지난 포스팅에 react-router 설치를 빼먹었다.
npm i -S react-router
Header.js
import React, { Component } from 'react'
import { Link } from 'react-router';
export default class Header extends Component {
render() {
return (
<nav>
<Link to="/main" activeClassName="active">Main</Link>
{" / "}
<Link to="/about" activeClassName="active">About</Link>
{this.props.children}
</nav>
);
}
}
react-router로 부터 Link 를 사용해서 구현한다. activeClassName
속성은
클릭된 Link 태그의 class를 active 로 설정해주는 속성이다.
그리고 하단부에 this.props.children
이 보인다.
이는 Router 에서 매우 중요한 개념인데 children
을 내려준이유는 지금 작성한
페이지 컴포넌트가 Header
이기 때문이다.
<Route path='/' component={Header} >
<Route path='main' component={main} />
<Route path='about' component={about} />
</Route>
우리가 구현해야할 라우터는 이렇게 생겼다.
실제 페이지에서 보면 각 라우터로 이동하는 Header
에는
about
그리고 main
앵커 태그가 상단부에 고정되어 있고
Header 밑으로 앵커 태그 클릭에 따라 main
또는 about
페이지가 출력 된다.
즉 Header라는 부모 컴포넌트 밑에 자식으로 main 과 about 컴포넌트가 있기 때문에
Header 컴포넌트에서 this.props.children
은 자식이 있다고 명시해주는 역할을 하는것이다.
Header 컴포넌트에 this.props.children
을 명시하지 않으면 component가 Route에 정의되어 있어도
자식을 찾을수 없기 때문에 main
이나 about
은 랜더 되지 않는다.
Header.js
에서 링크를 클릭할시에 어디로 이동할지 구현해줬으니
이동할곳에 대한 컴포넌트를 정의해줘야 할 차례이다.
src 폴더 상위에 있는 index.js
를 수정해야 한다.
첫번째 포스팅 당시 Hello.js
를 render하는 부분을 Router로 수정할 것이다.
생성한 컴포넌트들을 호출해서 라우터를 구성한다.
index.js
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import Header from './components/common/Header';
import Main from './components/main/Main';
import About from './components/about/About';
render(<Router history={browserHistory} >
<Route path="/" component={Header}>
<IndexRoute component={Main} />
<Route path="main" component={Main} />
<Route path="about" component={About} />
</Route>
</Router>,document.getElementById("root"));
[그림]
라우터가 완성 되었다.
라우터까지 React에 대한 내용이였다.
Redux 로 프로젝트를 구조화 시키면서 라우터는 분리될것이다.
Redux
보통사람들은 Redux와 Flux를 많이 비교한다.
Flux 가 먼저 나왔지만 GitHub 상에 Repository 인기를 보면 후에 나온
Redux 의 인기가 더 높다.
몇가지 차이점이 있지만 내가 생각하기에 둘에 가장큰 차이점은 Redux 는
한개의 Store 를 사용하고 있다는 점이다.
동영상 을 보고 필자가 이해한 흐름을
iWorks로 직접 그려봤다.
여러분도 직접 그려보면 이해에 도움이 될것이라 확신한다.
이 흐름도를 기반으로 프로젝트 구조화를 진행해보자.
Actions
앞서 생성한 actions
폴더에는 액션을 정의하는 actionTypes.js
파일
그리고 상황별 actions을 dispatch 하는 **Actions.js
파일을 생성할것이다.
actionsTypes.js
에 위치는 필자가 생각하는 기준이고 다른 boilerplate 에선
constants.js
로 정의 되어 있거나 src 최상단에 자리잡고 있는 경우도 있다.
이는 구현하기 나름이다.
우리는 LOAD_BOARD_CLEAR 라는 상수를 정의 할것이다.
우리가 시도하려는 액션은 게시판에 더미데이터를 불러오는데서 시작하기 때문이다.
actionTypes.js
export const LOAD_BOARD_CLEAR = 'LOAD_BOARD_CLEAR';
해당 방식으로 액션들을 정의해줘도 되고 이런 방식도 있다.
actionTypes.js
const ACTION = {
LOAD_BOARD_CLEAR : 'LOAD_BOARD_CLEAR'
}
Object.freeze(ACTION);
export default ACTION;
참고로 전자의 방식은 책이나 예제에서 많이 소개하고 있는 방식이다.
이에 따라 필자도 전자의 방식으로 구현을 이어가겠다.
다음은 actions 를 dispatch 하는 boardActions.js
파일을 생성한다.
해당 파일은 우선 생성만 해놓는다.
파일 안에 구현해야될 코드는 앞선 작업이 마무리된뒤에 계속해서 진행될 것이다.
Reducers
Reducer 는 state(구) 와 action 을 받아서 새로운 state 를 반환한다.
이때문에 시간여행 디버깅도 가능해 지는데 지금은 우선
새로운 state 를 반환 한다는 정도만 알아둬도 좋을것 같다.
reducers 폴더에 boardReducer.js
를 생성한다.
boardReducer.js
import { LOAD_BOARD_CLEAR } from '../actions/actionTypes';
export default function boardReducer(state = [], action) {
switch(action.type) {
case LOAD_BOARD_CLEAR :
return action.contents;
default :
return state;
}
}
state = []
코드가 생소할수도 있다. 이는 ES6 문법으로 state 값을 빈배열로
설정해준다.
실제 프로젝트에서는 여러개의 Reducer가 있고 초기값도 제각각 인데
초기 state 를 설정하는 더 좋은 방법이 있다.
initState.js 라는 파일을 생성하자. initState.js
export default {
contents : []
}
boardReducer.js
import { LOAD_BOARD_CLEAR } from '../actions/actionTypes';
import initState from './initState'
export default function boardReducer(state = initState.contents, action) { ... }
예제 에서는 Reducer 가 1개이기 때문에 combineReducers 를 사용하지 않아도 무관하지만
실제 프로젝트에서는 여러개의 Reducer 가 존재한다.
그렇기 때문에 combineReducers 를 사용한다.
root.js
파일을 생성한다.
import { combineReducers } from 'redux';
import board from './boardReducer';
const rootReducer = combineReducers({
contents: board
});
export default rootReducer;
boardReducer 를 호출해서 cobineReducers 로 묶어준다.
이때 객체의 key값 contents
를 생략하고 board 만 입력해도 무방한데
board 라는 Reducer 의 key값은 자동으로 board
가 된다.
Store
실제 개발환경에서는 process.env 를 통해 환경이 dev
or 'prod' 인지 체크하고
환경에 따라 다른 Store를 호출하는 방식을 활용한다.
Store 가 여러 middleware 를 포함할수 있기 때문이다.
배포 환경에서 불필요한 middleware 까지 호출 한다면 당연히 느려질수 밖에 없을것이다.
예제는 분기처리하지 않고 한개의 파일만 생성한다.
store.js
를 생성한다.
store.js
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk'
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
thunk middleware는 actions 폴더에있는 boardActions.js
에서 asynchronism dispatch 를 가능하게 해준다.
Provider & Router Separation
Redux 구조화가 거의 마무리 단계에 이르렀다.
Redux의 최상위 파일은 보통 이러한 구조를 가지고 있다.
<Provider store={store} >
<Router history={browserHistory} routes={routes} />
</Provider>
Provider 안에 Router 가 있는데 필자가 작성한 예제에서는 Route가 2개지만
얼마든지 많아질수있다. 그래서 Route를 분리해야한다.
src 하위에 routes.js
파일을 생성한다.
routes.js
import React from 'react';
import { Route, IndexRoute, browserHistory } from 'react-router';
import Header from './components/common/Header';
import Main from './components/main/Main';
import About from './components/about/About';
export default (
<Route path="/" component={Header}>
<IndexRoute component={Main} />
<Route path="main" component={Main} />
<Route path="about" component={About} />
</Route>
);
Route 분리가 완료되었다.
이제 index.js 파일을 수정한다.
index.js
import 'babel-polyfill';
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
import configureStore from './store/store';
const store = configureStore();
render(
<Provider store={store} >
<Router history={browserHistory} routes={routes} />
</Provider>,document.getElementById("root"));
여기까지 따라 왔다면 Redux로 기본적인 구조화가 완료된 상태 이다.
하지만 뭔가 눈에 보이는것이 없다.
아직 더미 데이터를 만들지 않았고 디스패치된 액션이 없기 때문이다.
다음 포스팅에서 는 MockApi 를 만들고 webpack을 활용해서 css 를 페이지에 로드
하는 방법을 소개한다.
'FrontEnd > React,Redux' 카테고리의 다른 글
PART6:Simply build App with Redux - Create / Redux Second Action (0) | 2016.09.27 |
---|---|
PART5:Simply build App with Redux - Component(Smart/Dumb) (0) | 2016.09.26 |
PART4:Simply build App with Redux - Data Flow (0) | 2016.09.22 |
PART3:Simply build App with Redux - Mock, webpack_loaders, Component (0) | 2016.09.22 |
PART1:Simply build App with Redux - Component , Initialization (0) | 2016.09.17 |