웹앱프로젝트/Vue.js

Vuex의 actions, modules 기본실습 ( + promise, async/await 설명)

Minah Park 2022. 8. 18. 10:23
반응형
Actions

Actions 는 mutations 와 비슷하다. 하지만 다른점이 있다면,

  • Instead of mutating the state, actions commit mutations.
  • Actions can contain arbitrary asynchronous operations.

그래서 Actions 에 로그인한 정보를 DB에서 비교하며 맞는지를 확인하는 비동기작업을 axios 를 이용해서 구현해보겠다.

 

여기서 전부터 의문이 생겼던 부분은, 비동기적 특성상 코드가 짜여진 순서대로 진행이 되지 않기 때문에 이를 통제하기 위해, Promise 객체의 resolve/reject 를 사용할 것이냐? async/await을 사용할 것인가이다.

참조: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise

아직도 완벽하게 Promise라는 객체가 이해가 되지는 않았다. 하지만 일단 내가 이해한 바로 설명해보면,

Promise
객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

대기 중인 프로미스는 값과 함께 이행할 수도, 어떤 이유(error)로 인해 거부될 수도 있습니다. 이행이나 거부될 때, 프로미스의 then 메서드에 의해 대기열(큐)에 추가된 처리기들이 호출됩니다. 이미 이행했거나 거부된 프로미스에 처리기를 연결해도 호출되므로, 비동기 연산과 처리기 연결 사이에 경합 조건은 없습니다.

Promise.prototype.then()
 및 Promise.prototype.catch() 메서드의 반환 값은 새로운 프로미스이므로 서로 연결할 수 있습니다.

Promise.reject(reason): 주어진 사유로 거부하는 Promise 객체를 반환합니다.

Promise.resolve() : 주
어진 값으로 이행하는 Promise 객체를 반환합니다. 이때 지정한 값이 then 가능한(then 메서드를 가지는) 값인 경우, Promise.resolve()가 반환하는 프로미스는 then 메서드를 "따라가서" 자신의 최종 상태를 결정합니다. 그 외의 경우, 반환된 프로미스는 주어진 값으로 이행합니다.

※어떤 값이 프로미스인지 아닌지 알 수 없는 경우, 보통 일일히 두 경우를 나눠서 처리하는 대신 Promise.resolve()로 값을 감싸서 항상 프로미스가 되도록 만든 후 작업하는 것이 좋습니다.


참조: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
async function

async function 선언은 AsyncFunction 객체를 반환하는 하나의 비동기 함수를 정의합니다. 비동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, 암시적으로 Promise 를 사용하여 결과를 반환합니다. 그러나 비동기 함수를 사용하는 코드의 구문과 구조는, 표준 동기 함수를 사용하는것과 많이 비슷합니다.

async 함수의 실행을 일시 중지하고 전달 된 Promise의 해결을 기다린 다음 async 함수의 실행을 다시 시작하고 완료후 값을 반환합니다.

async/await함수의 목적사용하는 여러 promise의 동작을 동기스럽게 사용할 수 있게 하고, 어떠한 동작을 여러 promise의 그룹에서 간단하게 동작하게 하는 것이다.
promise가 구조화된 callback과 유사한 것 처럼 async/await또한 제네레이터(generator)와 프로미스(promise)를 묶는것과 유사하다.


async function foo() {
    return 1
}​

위 코드는 아래와 같습니다.

 

function foo() {
    return Promise.resolve(1)
}​


async 함수의 본문은 0개 이상의 await 문으로 분할된 것으로 생각할 수 있습니다. 첫번째 await 문을 포함하는 최상위 코드는 동기적으로 실행됩니다. 따라서 await 문이 없는 async 함수는 동기적으로 실행됩니다. async function의 반환값이 암묵적으로 Promise.resolve 로 감싸지기 때문이다. 하지만 await 문이 있다면 async 함수는 항상 비동기적으로 완료됩니다. 

참조: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction

 

요약해보면,

axios.post 는 return type이 Promise 이다.

Promise는 비동기적인 처리를 위해 사용되는 콜백함수의 에러, 예외처리의 어려움을 해결해준다.
그래서 Promise 객체는 resolve와 reject를 받아 처리한다. 
Promise 다음에는 then() 과 catch()를 사용한다 .
then() : 사용한 Promise 객체에서 인수로 전달한 resolve가 호출되면 실행된다.
catch() : 생성한 Promise 객체에서 인수로 전달한 reject가 호출되면 실행된다.

※ async & await 
- 비동기식 코드를 동기식으로 표현하여 간단하게 나타내는것.
- 코드가 장황환 Promise를 보완하기 위해 도입됨.
- Promise 객체를 반환한다.

- function 앞에 async 를 붙이면 해당 함수는 항상 Promise를 반환
- await 키워드를 만나면 Promise가 처리(settled)될 때까지 기다리고, 결과는 그 이후 반환

※ async & await  예외 처리
- try{ } catch(e){ } 를 사용함.

참조: https://hi-zini.tistory.com/entry/%EB%B9%84%EB%8F%99%EA%B8%B0%EC%A0%81-%EB%B0%A9%EC%8B%9D-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95-Callback-Promise-async-await

결국은 Promise 객체가 이렇게 할거야 라고 해주는 객체. 그래서 약속을 지켜보는데, await를 써주면, 그 약속이 지켜지는지 모고 그 다음줄이 실행된다. 

나의 코드의 문제


 

actions 안에 getters 사용해보기

loginModule.js

import router from "@/router";
import axios from "axios";

const loginModule = {
    namespaced: true,
    state: {
        isLogin: false,
        loginEmail: "",
        loginName: "",
      },
    getters:{
      getUserInfo: state => state.isLogin,
    },
      mutations: {
        updateLoginStatus(state, { loginEmail, loginName }) {
          state.isLogin = true;
          state.loginEmail = loginEmail;
          state.loginName = loginName;
          localStorage.setItem("isLogin", true);
          localStorage.setItem("userName", loginName);
        },
    
      actions: {
        
        async signIn({ commit }, { loginEmail, loginPw }){
          // httpheader 에 authrization에 저장
          const res = await axios.post('/api/login/action',{ loginEmail, loginPw });
          if(res.data.isSuccess == true){
            var userName = res.data.userInfo.NAME
            await commit("updateLoginStatus", { loginEmail, loginName:userName });
            if(this.getters['loginModule/getUserInfo'] == true){
              router.push("/user/main")
            }
          }else{
            console.log('실패');
          }
        },
      },
}
export default loginModule;

store.js

import Vue from "vue";
import Vuex from "vuex";
import loginModule from "./loginModule";

Vue.use(Vuex);

export default new Vuex.Store({
   modules:{
    loginModule,   
  }, 
});
반응형

LoginView.vue

<template>
  <b-container fluid>
    <b-form @submit.prevent="loginAction()">
      <h1>Sign In</h1>
      <b-form-group
        label="Email"
        label-for="loginEmail">
        <b-form-input
          type="text"
          id="loginEmail"
          placeholder="Enter email address"
          v-model="loginEmail" />
      </b-form-group>
      <b-form-group
        label="Password"
        label-for="loginPw">
        <b-form-input
          type="password"
          id="loginPw"
          placeholder="Enter password"
          v-model="loginPw" />
      </b-form-group>
      <b-button
                
        variant="primary"
        @click="loginAction">
        Sign in
      </b-button>
      <p>
        Don't have an account?
        <router-link to="/signup">
          Sign up here
        </router-link>
      </p>
    </b-form>
  </b-container>
</template>
<script>
export default {
  name: "LoginView",
  data() {
    return {
      loginEmail: null,
      loginPw: null,
    };
  },
  computed: {
    loginStatus() {
      return this.$store.state.loginStatus;
    },
  },
  methods: {
    loginAction() {
      this.$store.dispatch("loginModule/signIn", {
          loginEmail: this.loginEmail,
          loginPw: this.loginPw,
        });
    },
  },
};
</script>

CommonLayout.vue

<template>
  <div>
    <nav class="header bg-warning">
      <div class="nav-right">
        <p>{{ loginName }}</p>
        님
      </div>
    </nav>
  </div>
</template>

<script>
export default {
  name: "BasicLayout",
  computed: {
    loginName() {
      return localStorage.getItem("userName");
    },
  },
};
</script>

 

내가 DB에 저장해둔 email로 로그인하면, 이렇게 DB에 저장해준 해당하는 이름이 출력된다!

 

반응형