dukDukz
0806 Redux-saga 본문
[configureStore.js]
SAGA
- 웬만하면 프로젝트 단위가 클 때 사용 많이 한다.
- 코드 생산성은 좋지가 않다.
- 사용하는 이유 : 코드의 안정성 때문에 많이 쓴다.
- function * yeild 로 내 코드가 어디서 오류 났는지 정확히 알 수 있어서 쓰는 경우가 대부분
- 설정하는게 까다로워서
- 설정 : wrapper에 내용을 넣어주기
cd / blog/front 에서
# npm install redux-saga
- import createSaga from 'redux-saga'
configureStore
미들웨어 넣어놓고
하나는 thunkMiddleware
Store = createStore(reducer,enhancer)
enhancer 는 미들웨어를 장착해주는 녀석
그래서 saga 도 저 middlewares 배열에 넣어주면 된다.
wrapper는 따로 설명 X
const configureStore = ()=>{
const sagaMiddleware = createSaga()
const middlewares = [sagaMiddleware]
...
Store.sagaTask = sagaMiddleware.run(rootSaga)
// rootSaga는 우리가 만들어줘야함
front 폴더에서 saga라는 폴더를 만들어줌
/saga/index.js 생성
[configureStore.js]
import rootSaga from '../saga/index'
[/saga/inedx.js]
rootSaga 는 제너레이터 함수임 - 그래서 * 을 붙여줌
all() 은 redux-saga 애 있는 애임
그래서
import {all} from 'redux-saga/effects' // 맨처음에 rootSaga() 를 실행했을 때 만나는 이펙트
함수 여러개를 실행시킬때 사용한다.
# redux-saga의 sideeffects
all call fork take takeEvery takeLatest put throttle
1. all
함수를 여러개 실행할 때 사용한다.
함수긴 한데, 인자값이 배열이다.
여러개의 함수를 받아야 하기 때문에 배열로 받는거임
2. fork
함수를 하나만 실행하는 것
제너레이터 함수임 - 그래서 * 를 붙여줘야함
all 을 쓰기 위해서 fork 를 먼저 사용
* rootSaga() 는
dispatch 가 reducer로 가기 전에 사용 된다.
ex )
login
dispatch 가 reducer 에 값을 넘겨줄때 어떤 값? action 값을 갖다 바친다.
3. take
action 값 type에 따라 함수를 호출하는 아이
인자값 내용이 같아질 때 까지 가만히 있다가 같아지면 아래의 코드를 실행함
yield 에서 멈춰있다가 같아지면 밑에 코드 실행
function* test(){
yield take('USER_LOGIN_REQUEST',testAction)
}
USER_LOGIN_REQUEST action값이 발동이 되면
testAction 값을 호출시킬거다.
* testAction 의 역할
: 훔쳐왔을 때 현재 상태 체크 or 뭔가 요청을 보냄
비동기 통신으로 데이터를 넘겨주거나
실패를 했다면 error msg 를 주는 처리를 한다.
[pages/login.jsx]
여기서 handleSubmit 안에 있는 data는 어떤 정보를 담고 있을까?
-> inputbox 의 적었던 내용을 담고 있는 객체이다.
const data = {
userid: userid.value,
userpw: userpw.value
}
그 data라는 변수를
await dispatch(UserLogin_REQUEST(data))
여기에 꽂아넣음
UserLogin_REQUEST 는 [reducers/user.js] 에 있음
export const UserLogin_REQUEST = data => {
return{
type : USER_LOGIN_REQUEST, // "USER_LOGIN_REQUEST"
data, // {userid : 'web7722', userpw : '1234'}
}
}
즉 반환을 해주는데
{
type: 'USER_LOGIN_REQUEST',
data: {
userid: 'web7722',
userpw: '1234'
}
}
이렇게 만들어주는게 UserLogin_REQUEST
그래서 이 함수를 쓰는게 dispatch 를 보내는거랑 같은 의미이다.
reducer를 보내기 위해 작업을 했던거임
(reducer에 값을 전달해주는거임)
[login.jsx]
async await 없이
handleSubmit 에서
import { UserLogin_REQUEST } from "../reducers/user"
const handleSubmit = (e) => {
...
dispatch(UserLogin_REQUEST(data))
}
4. takeLatest
action 값 type 에 따라 함수를 호출하는 녀석
+ 중복되는 액션이 다발적으로 발생 되었을 때, 맨 마지막거 하나만 실행시킴
# Reducer user.js
들어가면
로딩이 끝나면
useEffect(() => {
IsLogin === true && Router.push('/')
//: alert('아이디와 패스워드가 다릅니다.')
}, [loading])
배열안에 특정 값이 있으면 loading값이 바뀔때마다 실행됨
로그인이 되었다면 UserLogin_SUCCESS 실패라면 UserLogin_ERROR 처리를
해주는거를 [saga/index.js] 에서 해주면 된다.
1) fetch 혹은 axios 를 써서 요청을 날린다 - backend server에
# function* testAction(){} 여기서 작성
그리고 결과값을 받은 다음에 (backend server에서)
결과값에 따른 action 값을 내보낸다.
백앤드 부분
[/back/server.js]
node server로 백 서버를 열어준다.
if (userid == 'web7722' && userpw == '1234') {
result = {
result: 'OK',
msg: '로그인 성공'
}
} else {
result = {
result: 'Fail',
msg: '로그인 실패, 아이디나 패스워드 확인 부탁'
}
}
res.json(result)
[saga/index.js]로 와서
axios 설치
frontend 터미널에서
$ npm install axios
import axios from 'axios'
function* testAction(){
axios.post('url',{
userid : 'web7722',
userpw : '1234'
})
}
이런식으로 보내줘야함
Saga 가 인자값으로 action 값을 받아 올 수 있다.
function* testAction(action){
console.log(action)
}
{
data: {userid: "web7722", userpw: "1234"}
type: "USER_LOGIN_REQUEST"
}
이러한 값이 찍힌다.
axios.post('http://localhost:3000/api/login',action.data)
이렇게 백으로 보내준다.
saga 에서만 async await 를 달지 않아도 사용할 수 있는 방법이 있다.
5. call
함수를 하나만 실행하는것 (비동기 일 때 사용)
fork (함수를 하나만 실행하는것 - 동기 일 때 사용)
[saga/index.js]
const result = yield call(axios.post,'http://localhost:3000/api/login',action.data)
이렇게 사용함
결과값을 받아 와야하니까 앞에 result를 달아준다.
result 가 성공적이라고 가정하고 코드 작성해보자
6. put
action 값을 실행시킨다. = dispatch 를 실행시킨다.
즉 put은 dispatch라고 생각하면 된다.
dispatch === put " true "
if(data.result === 'OK'){
yield put({
type : 'USER_LOGIN_SUCCESS',
})
} else {
yield put({
type : 'USER_LOGIN_FAIL'
})
}
중간에 가로 채서 dispatch를 한번 더 날려주는 느낌으로~
사이드 이펙트가 들어가는 것은 제너레이터 함수라고 생각하면됨
그리고 사이드 이펙트는 무조건 앞에 yield 붙여주기
axios 쓰는게 불편하다면 함수를 한번 더 빼는것도 좋다.
# SAGA 를 쓰는 주 목적 :
비동기 통신 때문에
Context 만으로도 충분히 가능하지만
이거의 단점은 비동기 통신할때 애매한 부분이 있다.
화면단 login.jsx 부분에서 useEffect 를 사용하면서 까지 비동기통신을 해야하는데 그렇게 되면 login.jsx 의 코드가 너무 길어진다.
Thunk 의 단점
코드가 짧고 여러가지를 처리하기 어려움
그래서 SAGA 가 나왔다,
그러면 yield 가 나온 이유는?
# Saga 도 쪼갤 수 있다.
/saga/user.js 생성
watchLogin 과 fork(watchLogin) 부분을 잘라서 가져온다.
[/saga/index.js]
import userSaga from './user'
yield all([
fork(userSaga)
])
이렇게 하면 reducers 랑 같이 saga도 각각 관리할 수 있다,
export default function* rootSaga(){
yield all([
fork(userSaga),
fork(postSaga),
fork(aaSaga)
])
}
이런식으로 코드를 쪼갤 수 있다.
-------------------------------------
wrapper 를 사용하는 이유는 redux 를 사용하기 위해서 - 전역 상태 관리를 하기 위해서 사용
모든 페이지 갈때마다 wrapper 가 실행이 된다.
Store 를 만들어서 wrapper 에 담음
[순서]
[login.jsx]
1. const handleSubmit = (e) => {} 로그인 버튼을 눌렀을 때
2. dispatch(UserLogin_REQUEST(data)) 디스패치가 발동된다
- dispatch는 import { useDispatch } from 'react-redux' 여기서 가져옴
UserLogin_REQUEST 이 함수가 실행이되면서 action 값을 만들어줌
그리고 이 action 값을 reducer 에게 준다.
[reducers/user.js]
에 보면 action 값을 만들어주는애가 있다
reducer의 리턴값은 뭐냐 state 값이 바뀜
# dispatch 가 뭐냐?
reducer 를 좋아함
dispatch 가 실행되면 reducer 가 실행된다.
항상 action 값을 준다.
[reudcers/index.js]
combineReducers : reducer 합치기
USER_LOGIN_REQUEST 값을 줬음
# Saga 의 Effect
dispatch -> reducers(여기서 return을 해줄 때)-> middleware -> state
이렇게 실행이 된다.
즉 [saga/user.js] 전체가 3번째로 실행되는 middleware 라는 것 이라는것이다.
export default function* userSaga(){}
이 부분은 페이지가 로드 되자마자 실행이됨
근데 그럼 순서가 안맞는데?
사실 실질적 3번의 부분은
yield takeLatest('USER_LOGIN_REQUEST',login)
여기라고 할 수 있고
4번은
function* login(action){}
이 부분이다.
즉 Saga라는 아이들은 action값들을 다 실행시켜놓고 대기하고 있는거고 실행이되면 호출하는것이다.
'웹 개발 > React' 카테고리의 다른 글
React Next | 인피니티 스크롤 (Infinite Scroll) (0) | 2021.08.27 |
---|---|
React Next 기본 틀 (0) | 2021.08.27 |
0730 제너레이터 (0) | 2021.08.05 |
0729 Redux (0) | 2021.07.29 |
0727 Styled Component 의 장점 - props 값 전달 가능 (0) | 2021.07.27 |