오티스의개발일기

[REACT NATIVE] REDUX 미들웨어 그리고 클라이언트단부터의 로직의 흐름 본문

개발/react-native

[REACT NATIVE] REDUX 미들웨어 그리고 클라이언트단부터의 로직의 흐름

안되면 될때까지.. 2022. 12. 20. 10:44
728x90


다음글 >

2022.12.20 - [개발/react-native] - [REACT NATIVE] IMMER 사용하기 + REDUX 추가설명

 

[REACT NATIVE] IMMER 사용하기 + REDUX 추가설명

< 이전글 2022.12.20 - [개발/react-native] - [REACT NATIVE] REDUX 미들웨어 그리고 클라이언트단부터의 로직의 흐름 [REACT NATIVE] REDUX 미들웨어 그리고 클라이언트단부터의 로직의 흐름 저번글에서 react native

otis.tistory.com



< 이전글

 

2022.12.19 - [개발/react-native] - [REACT NATIVE] REDUX 적용하기 + 폴더 구조 + 깃허브 참조

 

[REACT NATIVE] REDUX 적용하기 + 폴더 구조 + 깃허브 참조

다음글 > 2022.12.20 - [개발/react-native] - [REACT NATIVE] REDUX 미들웨어 그리고 클라이언트단부터의 로직의 흐름 < 이전글 2022.12.18 - [개발/react-native] - [REDUX] 에대해서 오늘은 리엑트 네이티브에 리덕스를

otis.tistory.com


저번글에서 react native에서 redux를 추가하는방법을 배워보았다.

 

redux-saga를 공부를 하고있는데 아무리봐도 도저히 이해가 안가는부분이 있었다.

 

일단은 미들웨어에서 어떻게 처리하는지도 모르겠고

 

함축된 함수들이 너무 많고 자바를 하던나에게는 좀너무 생소했다. 

 

자바뿐만아니라 단순 자바스크립트도 그렇다 함수를 생성하고 그 함수가 어디로 연결되어있고 등등

 

이러한것들이 정확하게 보여지지 않아 더이상 redux-saga를 공부하면 안된다는 생각이들었고

 

기본적인 redux의 흐름과 미들웨어를 직접 만들어 이해해보기로 하였다.

 

이번공부를 통해 thunk의 흐름은 확실하게 알게되었다.

 

 

지금부터 작성하는것들은 react-native가 아닌 순수 자바스크립트이다.

폴더구조는 비슷하게 만들었긴 했지만 function들만 존재하니 보면서 흐름을 파악하려는 생각으로 이글을 읽었으면 한다.

 

 

 

폴더 구조

폴더 구조는 이런식으로 이루어져있다.

 

일단 action안에 userpost에 관한 action들이 담겨져있고

reducer안에는 postuser의 리듀서를 각각 생성 그리고

index안에서 combineReducers 를 사용해 두가지의 리듀서를 묶어주었다.  rootReducer라고 생각하면 좋을것같다.

그리고 설명은 user 파일에 관한 설명만 진행할것이다.

post는 작업하다 만것도 있고 거의 동일하다. 복사 붙여넣기이기 때문에 생략하도록 하겠다.

 

 

 

일단 리듀서 들을 먼저 만들어보자

/reducers/user.js

const initialState = {
    isLoggingIn: true,
    data: null
};
// preVState = initialState 초기 데이터
const userReducer = (preVState = initialState, action) => {
    const {type, data} = action; // action에 있는 type과 data를 미리 꺼내놓은것
    switch (type) {
        case 'LOG_IN' :
            return {
                ...preVState,
                data: data
            }
        case 'LOG_OUT' :
            return {
                ...preVState,
                data: null,
            }
        case 'LOG_IN_SUCCESS' :
            return {
                ...preVState,
                data: data
            }
        case 'LOG_IN_FAILURE' :
            return {
                ...preVState,
                data: data
            }
        default : // 타입이 잘못들어왔거나 뭔가 문제가있으면 초기데이터를 반환
            return preVState;
    }
};

module.exports = userReducer

 

전 글과봤던것 처럼 크게 다른것없다.

 

 

 

 

루트 리듀서를 만들어보자 

/reducers/index

const { combineReducers } = require('redux');
const userReducer = require('./user')
const postReducer = require('./post')

module.exports = combineReducers({
    user: userReducer,
    posts: postReducer,
})

이것은 리듀서를 모와주는 부모 리듀서 이다.

 

 

 

이제부터가 본론이다

이제 우리는 미들웨어를 직접 만들어볼것이고

 

기본 미들웨어를 만든후

thunk를 직접 만들어볼것이다.

api를 직접 만든다는 말이 너무 무섭다고 느껴질수도있지만

thunk 는 대략 14줄도 안되는 코드이다. 무서울것이 없다.

 

 

const customMiddleware = (store) => (next) => (action) => { // 미들웨어 직접 만들어보기
   console.log('액션 로깅 = ', action);
   next(action); // next == dispatch 라고 생각하면 편하다.
    console.log(store.getState());
};


const enhancer = applyMiddleware(
    customMiddleware,
); // 기능증강 추가

const store = createStore(reducer, initialSate, enhancer);

일단

 

상단 customMiddleware를 봐보자

() => () => () => {};

이 함수를 처음보는 사람도 있을것이다.

이것을 풀어서보면

function (store) {
    function (next) {
        function (action) {

        }
    }
}

이러한 뜻이다.

 

그리고 주석에도 있듯이 nextdispatch라고 봐도 무관하다.

 

customMiddleware 코드를 다시 봐보자

 

const customMiddleware = (store) => (next) => (action) => { // 미들웨어 직접 만들어보기
   console.log('액션 로깅 = ', action); 
   next(action); // next == dispatch 라고 생각하면 편하다.
    console.log(store.getState());
};

next(action) 은 사실상 dispatch가 일어나는 것이라 보면된다 

dispatch(action) 이것과 동일하다.

우리가 react-native 스크린부분에서 버튼을 클릭하면 dispatch(함수) 했건것이랑 동일한것이다.

 

원리는 이렇다   action = {type: 'LOG_IN' , data: {'id' : 'id', pw : '1234'}}

1. 사용자가 버튼을 누른다 => dispatch(action)

2.customMiddleware  이 함수가 실행된다.

3. 우리가 방금 실행한 함수가 바로실행되지않고 customMiddleware  에서 next(action) 전후로

할거 다~ 하고 실행시킨다.

 

정말 간단하게 말하면

어떤 유튜버한명이있다. 그리고 유튜버 지인인 내가 신차를 산것이다.

근데 그 유튜버가 신차에 대한 리뷰를 쓰고싶다고 하는것이다. 또한 자신이 산것처럼 하고싶다고 한다

이것들을 진행하려면 돈은 내가내고 유튜버가 신청서를 대신쓰고 그돈을 유튜버가 받아 자신이 산것처럼한후

차를 받아 리뷰를한후 나에게 돌려준다.

 

결국 나는 차를구매 하기위해 돈을 (dispatch(action)) 을 낸것이고

지인 유튜버가 마치 자기가 산것처럼

서류작성후 = (console.log('액션 로깅 = ', action); )

돈을 내고 = (next(action))   

리뷰는 하고 = (console.log(store.getState());)

전후 하고싶은것들을 다~ 한거다 

 

뭐 당연히 유튜버가 하고싶은게 내가 원하는거라는 가정이 된 함수이지만

이해하기어렵다면 위에 설명한것처럼 이해하면 좀더  편할수도있다.

 

 

 

어느정도 이해했길 바라면서

이번에는 Thunk를 만들어 보겠다.

 

방금 만든것은 단순 next(action) 을 통해 dispatch이전 혹은 이후 내가 확인하고 싶은것들 등 콘솔로 찍는 단순한

미드웨어를 제작해보았고

thunk는 여기서 한가지가 더 추가된다

바로 비동기 라는것이다.

 

코드를 한번 봐보자

 

const thunkMiddleware =  (store) => (dispatch) => (action) => {
    if (typeof action == 'function') { // 들어온 엑션이 함수라면  == 비동기
        return action(store.dispatch, store.getState()); // => 이 action 이
    }
    return dispatch(action);
}

 

주석에 보이듯 들어온 action이 함수 형태라면

비동기를 부른다는 의미이다.

예를들어

const action =  {

   type: 'login',

   data: '{....}',

};

dispatch (action);

이건 액션이 오브젝트 형식으로 보낸거다

그말은 즉은 동기로 보낸것이고

if문을 빠져나와 아까작성한 customMiddleware 처럼 작동할것이다.

 

 

아래의 코드를 봐보자

// /action/user.js

const logIn = (data) => { // async action creator 비동기 액션
    return (dispatch, getState) => { // 비동기 액션
        dispatch(logInRequest(data));
        try {
            setTimeout(function () {
                dispatch(logInSuccess({
                    userId: 1,
                    nickName: 'zerocho'
                }));
            }, 2000)
        } catch (e) {
            dispatch(logInFailure)
        }
    };
}

하지만  이러한 코드를 넣으면?

함수를 넣은것이다.

그리고 return 부분에 dispatch, getState 가 있어서 혼란스러울수도 있다.

대체 저 dispatchgetState는 어디서 오는거지......

 

const thunkMiddleware =  (store) => (dispatch) => (action) => {
    if (typeof action == 'function') { // 들어온 엑션이 함수라면  == 비동기
        return action(store.dispatch, store.getState()); // => 이 이부분에서 dispatch와 getState가 들어온것
    }
    return dispatch(action);
}

 

저기 보이는 주석에 서 들어온것이다.

 

customMiddleware를 생각해보자

아까 설명한것처럼 사실 사용자가 버튼을 눌렀지만 바로 실행되지않고

그것을 미들웨어가 먹고 자신이 하고싶은데로 뿌린다고 했지않았는가?

그렇기 떄문에 저기서 받은 action을 받고 매게변수로 dispatchgetState를 넣은것이다.

 

이 사진이 제일 적절한것같다.

 

 

뭐어쨌든

 

다시한번 정리해보자

 

 

 

1. 유저가 버튼을 누른다  (dispatch(action))  = 여기 action은 함수라고 가정하자

 

2. 미들웨어가 함수인지 그냥 액션인지 확인한다.

 

3. 액션이면 그대로 next(action) [ = dispatch(action) ] 을 실행

 

4. 함수면 함수를 실행해준다

 

5. 실행이 다되면 api에서 성공 실패 유무를 판단하고 그 함수에서 또다른 dispatch 를 호출한다 

예로들면 성공 dispatch()

 

6. 성공 이든 실패든 dispatch를 실행시키면 다시 미들웨어로 간다 

이때는 성공 실패 유무를 가리니까 object 형식의 action을 보낼것이다.

그러므로 기본적인 흐름인 dispatch(action)  => userReducer 로 넘어간다.

 

7. switch 를 통해 어떤 것인지 확인하고 리턴을 한다.

상단 reducer를 확인해보자.

 

++ 아 참고로 dispatch가 진행되고 데이터가 바뀌면 getState가 자동실행될것이다.

그렇게 되면 클라이언트단에 useSeletor가 실행되어 바뀐 데이터가 화면에 나올것이다.

 

store.subscribe [데이터가 변하면 자동 실행되는 구독 기능]라는 기능이있지만 자동으로 실행이된다. 따로 작성할 필요는 없다.

 

 

 

이렇게 미들웨어와 redux의 흐름이 끝이났다.

 

잘못된점은 댓글로 남겨주면 고맙겠다.

 

또한 다음편에는 진짜 redux-sagatoolkit에 대해 업로드 해보겠다.

728x90
Comments