리덕스_8_리스트 조회하기
망고플레이트를 예시로 사용해봅니다.
망고플레이트: 나만의 맛집 검색
솔직하고 거짓없는 리뷰로 나만의 맛집을 쉽고 빠르게 찾아보세요!
www.mangoplate.com
1. 새 프로젝트를 만든다.
package.json에 아래있는 것들을 추가한다.
"express": "^4.17.1", "mongodb": "^3.5.8", "redux": "^4.0.5", "react-redux": "^7.2.0", "react-router": "^5.2.0", //페이지 넘길 때 사용 "react-router-dom": "^5.2.0", "redux-logger": "^3.0.6" //log 표시 할 떄 사용 "redux-thunk": "^2.3.0" //비동기일 때 사용 "axios": "^0.19.2" //서버 연결시 사용 |
2. src안에 폴더를 4개 넣는다.
1. components
2. atcions
3. reducers
4. store
3. action 폴더에 types이라는 이름의 javascript파일을 하나 만든다.
액션을 등록할 때에는 변수로 등록을 해야한다.
그래서 const로 변수를 만들어준다.
//액션: 구분하는 문자열 저장
export const FETCH_CATEGORY ='FETCH_CATEGORY'
export const FETCH_CATE_FOOD ='FETCH_CATE_FOOD'
export const FETCH_FOOD_DETAIL ='FETCH_FOOD_DETAIL'
카테고리를 가져오는 것, 음식정보 가져오는 것, 자세한 음식정보 가져오는 것
총 3개의 STATE가 생성되었다고 보면 된다.
4. reducer에 foodReducer라는 이름의 javascript파일을 하나 만든다.
store에 store라는 이름의 javascript파일을 하나 만든다. (저장공간용)
foodReducer에 types에 적어놓은 애들을 import해주거 초기값을 만들어준다. ,
import {FETCH_CATEGORY,FETCH_CATE_FOOD,FETCH_FOOD_DETAIL} from "../actions/types";
const initialState ={
category:[],
cafe_food:[],
food_detail:{}
}
초기값과 action을 받는 function을 만든다.
switch문으로 FETCH_CATEGORY일때 FETCH_CATE_FOOD일때 FETCH_FOOD_DETAIL일때의 경우를 쓴다.
export default function (state=initialState, action) {
switch (action.type) {
case FETCH_CATEGORY:
return{
...state,
category: action.payload
}
case FETCH_CATE_FOOD:
return {
...state,
cafe_food: action.payload
}
case FETCH_FOOD_DETAIL:
return {
...state,
food_detail: action.payload
}
default:
return state
}
}
...state는 생략하는 것이 아니라 전에 있는 것을 가져오는 것이다.
+) 값을 보내는 dispatch는 이런식으로 쓴다.
dispatch({
type: FETCH_CATEGORY //카테고리의 function을 달라는 것
payLoad: data(category) //카테고리에 해당되는 데이터 보내기
})
5. reducer에 index.js.도 만든다. reduce가 여러개 일 때 하나로 모아주는 역할을 한다.
index에 combineReducers 를 import한다. 얘가 묶어 줄 때 사용하는 함수이다.
import {combineReducers} from "redux";
import foodReducer from "./foodReducer";
export default combineReducers({
food: foodReducer
})
recipe도 있으면 food: foodReducer 밑에 쓰면 된다.
6. 이렇게 만든 것들은 store에서 관리한다. 그래서 store.js로 이동한다.
**rootReducer는 index.js를 의미한다.
import rootReducer from './reducers'
import {createLogger} from "redux-logger/src";
import thunk from "redux-thunk";
//미들웨어
//logger 생성
const logger = createLogger();
//thunk 생성(비동기프로그램)
const initialState={}
const middleware=[thunk,logger];
createStore로 스토어를 하나 만들어준다.
const store = createStore(
rootReducer,
initialState,
compose()
)
compose는 합성시켜주는 역할을 한다. (어떻게 변경되는지 모니터링이 필요해서 넣은 것. 없어도 된다.)
안에 미드웨어와 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 이걸 넣어준다.
compose(applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
store 전체 코드)
import rootReducer from "../reducers"
import {createLogger} from "redux-logger/src";
import thunk from "redux-thunk";
import {applyMiddleware, compose, createStore} from "redux";
//미들웨어
//logger 생성
const logger = createLogger();
//thunk 생성(비동기프로그램)
const initialState={}
const middleware=[thunk,logger];
const store = createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
)
export default store;
7. components에 js를 세개 만든다.
8. 서버 연결을 위해 food-server를 만든다.
const express=require("express")
const app=express();
app.listen(3355,()=>{
console.log("Server Start...","http://localhost:3355")
})
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
const Client=require("mongodb").MongoClient;
app.get('/category',(request,response)=>{
var url="mongodb://211.238.142.181:27017";//몽고디비 주소
Client.connect(url,(err,client)=>{
var db=client.db('mydb');
db.collection('category').find({})
.toArray((err,docs)=>{
// 요청한 사용자 => 데이터 전송
response.json(docs);
console.log(docs)
client.close();
})
})
})
9. index.html에 css를 넣는다.
10. components에 있는 Category.js에 function을 만든다.
useDispatch를 import 해주어야 foodReducer.js에 있는 case문에서 action할 수 있다.
import React,{useEffect} from "react";
import {FETCH_CATEGORY} from "../actions/types";
import {useDispatch,useSelector} from "react-redux";
import axios from 'axios'
export default function Category(props) {
const dispatch=useDispatch(); //reducer 함수를 호출
useEffect(()=>{
axios.get('http://localhost:3355/category').then((result)=>{
dispatch({
type:FETCH_CATEGORY,
payload:result.data
})
})
},[])
//state 갱신 => store에서 변경된 state를 읽어온다.
const category_data = useSelector((state)=>state.foods.category)
const html = category_data.map((m)=>
<div className="col-md-4">
<div className="thumbnail">
<img src={m.poster} alt="Lights" style={{"width": "100%"}}/>
<div className="caption">
<p>{m.title} </p>
</div>
</div>
</div>
)
return(
<div className={"row"}>
{html}
</div>
)
}
11. category를 클릭하면 넘어가야 하니깐 다른 페이지들도 해준다.
CateFood에 이렇게 대충 코딩한다.
import React from "react";
function CateFood(props) {
return(
<div className={"row"}>
</div>
)
}
export default CateFood
FoodDetail.js에도 똑같이 넣어준다. 이렇게 해야 오류가 생기지 않는다.
import React from "react";
function FoodDetail(props) {
return(
<div className={"row"}>
</div>
)
}
export default FoodDetail
12. App.js에서 최종 동작하므로 이걸 수정해준다.
import React from 'react';
import {BrowserRouter as Router,Route, Switch} from "react-router-dom";
import Category from "./components/Category";
import CateFood from "./components/CateFood";
import FoodDetail from "./components/FoodDetail";
import store from "./store/store";
import {Provider} from 'react-redux'
function App() {
return (
<Provider store={store}>
<Router>
<switch>
<Route exact path={"/"} component={Category}/>
<Route path={"/cate_food"} component={CateFood}/>
<Route path={"/food_detail"} component={FoodDetail}/>
</switch>
</Router>
</Provider>
);
}
export default App;