본문 바로가기

블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기

[99일차] Redux 리액트 리덕스 Middleware Thunk

반응형

 

Generator 

 

gen2 는 일반 함수 / gen 의 함수는 저렇게 됨

함수에 * 별을 다니깐 함수 호출이 바로 실행이 안됨

gen{<suspended>} 안에 있는 next 라는 함수를 붙여야 다음 코드로 진행된다.

gen().next()

 

 

왜 1밖에 안찍히지?  - > generator 사용 이유 

함수 안의 코드들을 내가 구분해서 사용할 수 있음 next를 써야지만 안의 코드 명령들을 이어서 쓸 수 있음 

 

gen 이라는 함수를 어떤 특정 변수에는 담아놔야함 ** 

 

함수에 return 이 없는데 무언가가 return 됨 

{value:undefined, done:false} 

generator 함수 특성 : yield 가 return 이랑 비슷하다고 생각해보기 

next() 할 때마다 yield 가 return 해주는구나~

yield 3 이라고 쓰면 value에 들어감

done의 뜻 : 마무리가 되었는가 false : 아직 안끝났다.  true :끝났다. 더이상 실행할 것이 없다. 

 

 

Generator 에서는 무한 반복이 가능함 

** 특징 : 무한반복 가능  /   변수에 담아서 사용해야 한다. 

 

 

 

 

 

https://ko.javascript.info/generators

 

제너레이터

 

ko.javascript.info

 

 

 

 


 

 

 

 

Middleware

redux-thunk & redux-saga 모두 redux의 미들웨어

 

Redux middleware의 역할 

reducer 라는 값이 상태값 변화게 해주는 아이 

dispatch ----> reducer에 가서 처리되지 전 먼저 들렸다 가는 곳이 middleware 

 

DISPATCH ----------> MIDDLEWARE ----------> REDUCER -------- (middleware 가능) -------> STATE 

 

middleware에서는 비동기 처리 / 다양한 상태 처리 /  DB와의 체크 등등... ----> 그리고 reducer 에 보냄 

고차원함수(?) 

 

 

 

Middleware Thunk 개념만 알고 넘어가기 !  (이걸로 만들 건 아니여) 

thunk 

 

1) configureStore.js

import {createWrapper} from 'next-redux-wrapper'
import {applyMiddleware, compose, createStore} from 'redux'
import reducer from '../reducers'   //index 생략 가능
import {composeWithDevTools} from 'redux-devtools-extension'

const loggerMiddleware = ({dispatch,getState}) =>(next)=>(action)=>{
    console.log('action=',action)
    console.log('dispatch=', dispatch)
    console.log('getState=', getState)
    return next(action)
}

const configureStore=()=>{
    const middlewares = [loggerMiddleware]
    const enhancer = process.env.NODE_ENV === 'production' 
    ? compose(applyMiddleware(...middlewares))
    : composeWithDevTools(applyMiddleware(...middlewares))

    const Store = createStore(reducer, enhancer)
    return Store
}

// Store 자체를 가지는게 아닌 Store를 return해 주는 함수를 담아야함 
const wrapper = createWrapper(configureStore,{
    debug:process.env.NODE_ENV === 'development'
    //debug:true (개발모드이므로) 와 같은 의미 
})

export default wrapper

createStore(reducer, enhancer) -> enhancer 를 미리 만들어 놓음 

 

console.log 

 

action 값은 object  근데 항상 object로 떨어지는게 아니고 우리가 그렇게 만들기 때문

action 값을 객체로 할 것인지 함수로 할 것인지 구분 

 

 

login -> db 확인 (middleware) -> dispatch -> reducer -> state change -> rerendering 

 

 

 

Thunk 사용 해보기 

 

1. thunk 다운

npm i redux-thunk

2. 가져오기, middlewares 배열에 넣기 

configureStore.js

import thunkMiddleware from 'redux-thunk'             //가져오기 


const loggerMiddleware = ({dispatch,getState}) =>(next)=>(action)=>{
    console.log('action=',action)
    console.log('dispatch=', dispatch)
    console.log('getState=', getState)
    return next(action)
}

const configureStore=()=>{
    const middlewares = [loggerMiddleware,thunkMiddleware]        //추가

3. login.jsx 수정

import FormLayout from "../components/FormLayout"
import Head from 'next/head'
import Router from 'next/router'
import userInput from '../hooks/userInput'
// redux 
import {useDispatch} from 'react-redux'
import {UserLoginAction} from '../reducers/user' //가져오기 

const Login = () => {
    const dispatch = useDispatch()

    const userid = userInput('')  // object
    const userpw = userInput('')  // object

    const handleSubmit=(e)=>{
        e.preventDefault()

        //dispatch(UserLoginAction())    // 요렇게 수정 
        //dispatch 안에 넣는게 action -> 여기에 함수 넣기 (thunk)
        dispatch(()=>{
            return(dispatch)=>{
                setTimeout(()=>{
                    dispatch(UserLoginAction())
                },1000)
            }
        })

 
.
.
.

 

4. reducers > user.js

상태값에 loadding:false 추가, User_login_request, success, error 추가

const initialState = {
    IsLogin: false,
    loadding:false,
}

const USER_LOGIN = "USER_LOGIN"  // 오타 났을 때 대비 
const USER_LOGOUT = "USER_LOGOUT"
const USER_LOGIN_REQUEST = "USER_LOGIN_REQUEST"
const USER_LOGIN_SUCCESS = "USER_LOGIN_SUCCESS"
const USER_LOGIN_ERROR = "USER_LOGIN_ERROR"


export const UserLoginAction = () => {
    return {
        type: USER_LOGIN
    }
}

export const UserLogin_REQUEST =()=>{
    return {
        type:USER_LOGIN_REQUEST,
        data
    }
}
export const UserLogin_SUCCESS=()=>{
    return{
        type:USER_LOGIN_SUCCESS
    }
}
export const UserLogin_ERROR=()=>{
    return{
        type:USER_LOGIN_ERROR
    }
}

export const UserLogoutAction = () => {
    return {
        type: USER_LOGOUT
    }
}

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case USER_LOGIN:
            console.log('로그인 신호왓다')
            return {
                ...state,
                IsLogin: true

            }
        case USER_LOGOUT:
            console.log('로그아웃 신호왓다리')
            return {
                ...state,
                IsLogin: false
            }
        default:
            return state
    }
}

export default reducer

↓↓


const initialState = {
    IsLogin: false,
    loadding:false,
}

//const USER_LOGIN = "USER_LOGIN"  // 오타 났을 때 대비 
const USER_LOGOUT = "USER_LOGOUT"
const USER_LOGIN_REQUEST = "USER_LOGIN_REQUEST"
const USER_LOGIN_SUCCESS = "USER_LOGIN_SUCCESS"
const USER_LOGIN_ERROR = "USER_LOGIN_ERROR"

//UserLoginAction 은 middleware thunk 로 인해 사용할 수 있는 것 
// 얘가 실행이 됨
// login.js -> handleSubmit눌렀을 때 실행됨(dispatch) -> 
// user.js -> 아래 코드 실행됨 
export const UserLoginAction = (data) => {
    return async (dispatch)=>{
        dispatch(UserLogin_REQUEST())
        try{ // fetch or axios 성공 fetch:내장 axios : 코드가져와야함
            const response = await fetch('http://localhost:3000/api/login',{
                method:'POST',
                headers:{
                    "Content-type":"application/json",
                },
                // body 내용은 객체이면 안된다 ! 
                body:JSON.stringify({...date})
            })
            const result = await response.json()
            dispatch(UserLogin_SUCCESS(result))
        }catch(e){ // 실패
            dispatch(UserLogin_ERROR())
        }
    }
}

export const UserLogin_REQUEST =(data)=>{
    return {
        type:USER_LOGIN_REQUEST,
        data
    }
}
export const UserLogin_SUCCESS=()=>{ 
    return{
        type:USER_LOGIN_SUCCESS
    }
}
export const UserLogin_ERROR=()=>{
    return{
        type:USER_LOGIN_ERROR
    }
}

export const UserLogoutAction = () => {
    return {
        type: USER_LOGOUT
    }
}

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case USER_LOGIN_REQUEST:
            return{
                ...state,
            }
        case USER_LOGIN_SUCCESS:
            return{
                ...state,
                IsLogin:true
            }
        case USER_LOGIN_ERROR:
            return{
                ...state,
            }
        // case USER_LOGIN:
        //     console.log('로그인 신호왓다')
        //     return {
        //         ...state,
        //         IsLogin: true

        //     }
        case USER_LOGOUT:
            console.log('로그아웃 ')
            return {
                ...state,
                IsLogin: false
            }
        default:
            return state
    }
}

export default reducer

 

 

login.jsx handleSubmit 에 async e , await dispatch 달아주기 

-> 로그인 요청이 끝나야 page가 넘어감 -> async, await 붙여서 UserLoginAction() 안의 try , catch 의 실행이 끝날때까지 기다렸다가 page가 넘어가게됨 (안적어주면 페이지 이동이 먼저 일어남) 

    const handleSubmit= async (e)=>{
        e.preventDefault()

        //dispatch(UserLoginAction())    // 요렇게 수정 
        //dispatch 안에 넣는게 action -> 여기에 함수 넣기 (thunk)
        await dispatch(UserLoginAction())

        userid.value==="asdf" && userpw.value==="asdf" 
        ? Router.push('/') : alert('id 또는 pw가 다릅니다.')
    }

 

 

백엔드 만들기 

0. blog > back 폴더까지 터미널 경로 들어오기 

 

1. 간단한 server 만들기위한 packages 설치 

npm init
npm i express body-parser cors 

2. back > server.js 기본 파일 생성 및 코드 작성 

const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const cors = require('cors')

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended:false}))
app.use(cors())


app.get('/', (req,res)=>{
    res.send('helllo')
})


app.listen(3000,()=>{
    console.log('start port : 3000')
})

helloo뜨는지 확인 

 

3. front에서 요청한 localhost:3000/api/login    method:post 만들기 

app.post('/api/login',(req,res)=>{
    console.log(req.body.userid, req.body.userpw)
    res.json({
        userid:'asdf',
        userpw:'asdf',
    })
})

userid, userpw 의 문제가 있음 확인해보기 

login.jsx 에 매개변수가 비어있었음 ! data 만들어서 넣어주기

    const handleSubmit= async (e)=>{
        e.preventDefault()
        const data ={
            userid:userid.value,
            userpw:userpw.value
        }
        //dispatch(UserLoginAction())    // 요렇게 수정 
        //dispatch 안에 넣는게 action -> 여기에 함수 넣기 (thunk)
        await dispatch(UserLoginAction(data))

        userid.value==="asdf" && userpw.value==="asdf" 
        ? Router.push('/') : alert('id 또는 pw가 다릅니다.')
    }

=> 결과 

로그인 됨 

 

 

 

흐름 : login - handleSubimt  -> dispatch(UserLoginAction() 실행 -> 

user.js - retrun - dispatch(UserLogin_REQUEST) 실행 -> try(UserLogin_SUCCESS) or catch (UserLogin_ERROR) 실행 

 

 

 

 

server.js 수정

app.post('/api/login',(req,res)=>{
    const {userid,userpw} = req.body;
    console.log(userid,userpw)

    //원래는 DB에서 조회해서 결과 던져주기!
    let result={};
    if(userid=="asdf" && userpw =='asdf'){
        result={
            result:'ok',
            msg:'로그인에 성공했습니다.'
        }
    }else{
        result={
            result:'fail',
            msg:'아이디와 패스워드가 틀립니다. '
        }
    }
    
    res.json(result)
})

 

 

 

 

 

 

login.js 주석처리

 

user.js 수정

export const UserLoginAction = (data) => {

    return async (dispatch)=>{
        dispatch(UserLogin_REQUEST())
        console.log(data, 'data')
        try{ // fetch or axios 성공 fetch:내장 axios : 코드가져와야함
            const response = await fetch('http://localhost:3000/api/login',{
                method:'POST',
                headers:{
                    "Content-type":"application/json",
                },
                // body 내용은 객체이면 안된다 ! 
                body:JSON.stringify({...data})
            })
            const result = await response.json()

            result.result=='ok' ? 
            dispatch(UserLogin_SUCCESS(result))
            :  dispatch(UserLogin_ERROR())
            
        }catch(e){ // 실패
            dispatch(UserLogin_ERROR())
        }
    }
}

 

login.jsx에서 상태값을 가져오기위해 useSelector 가져오기

// redux 
import {useDispatch, useSelector} from 'react-redux'

user.js 에loadding ㅇ추가

const initialState = {
    IsLogin: false,
    loadding:false,
}

//const USER_LOGIN = "USER_LOGIN"  // 오타 났을 때 대비 
const USER_LOGOUT = "USER_LOGOUT"
const USER_LOGIN_REQUEST = "USER_LOGIN_REQUEST"
const USER_LOGIN_SUCCESS = "USER_LOGIN_SUCCESS"
const USER_LOGIN_ERROR = "USER_LOGIN_ERROR"

//UserLoginAction 은 middleware thunk 로 인해 사용할 수 있는 것 
// 얘가 실행이 됨
// login.js -> handleSubmit눌렀을 때 실행됨(dispatch) -> 
// user.js -> 아래 코드 실행됨 
export const UserLoginAction = (data) => {

    return async (dispatch)=>{
        dispatch(UserLogin_REQUEST())
        console.log(data, 'data')
        try{ // fetch or axios 성공 fetch:내장 axios : 코드가져와야함
            const response = await fetch('http://localhost:3000/api/login',{
                method:'POST',
                headers:{
                    "Content-type":"application/json",
                },
                // body 내용은 객체이면 안된다 ! 
                body:JSON.stringify({...data})
            })
            const result = await response.json()

            result.result=='ok' ? 
            dispatch(UserLogin_SUCCESS(result))
            :  dispatch(UserLogin_ERROR())

        }catch(e){ // 실패
            dispatch(UserLogin_ERROR())
        }
    }
}

export const UserLogin_REQUEST =(data)=>{
    return {
        type:USER_LOGIN_REQUEST,
        loadding:true,
    }
}
export const UserLogin_SUCCESS=()=>{ 
    return{
        type:USER_LOGIN_SUCCESS,
        loadding:false,
    }
}
export const UserLogin_ERROR=()=>{
    return{
        type:USER_LOGIN_ERROR,
        loadding:false,
    }
}

export const UserLogoutAction = () => {
    return {
        type: USER_LOGOUT
    }
}

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case USER_LOGIN_REQUEST:
            return{
                ...state,
            }
        case USER_LOGIN_SUCCESS:
            return{
                ...state,
                IsLogin:true
            }
        case USER_LOGIN_ERROR:
            return{
                ...state,
            }
        // case USER_LOGIN:
        //     console.log('로그인 신호왓다')
        //     return {
        //         ...state,
        //         IsLogin: true

        //     }
        case USER_LOGOUT:
            console.log('로그아웃 ')
            return {
                ...state,
                IsLogin: false
            }
        default:
            return state
    }
}

export default reducer

 

login.jsx

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형