dukDukz

0806 Redux-saga 본문

웹 개발/React

0806 Redux-saga

헤일리_HJ 2021. 8. 6. 12:31

[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