반응형

흥미로운 아이를 발견함.

loginModule.js

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

const loginModule = {
    namespaced: true,    
      actions: {
        signUp(params){
          console.log('signup action activated')
          console.log(params)
           
        },
      },
}
export default loginModule;

MemberRegisterView.vue

<template>
  <b-container fluid>
    <h1>Sign Up</h1>
    <b-form>
      <b-form-group
        label="Name"
        label-for="username">
        <b-form-input
          type="text"
          id="username"
          placeholder="Enter name"
          v-model="user.username"
          v-validate="{required : true ,min:3, max:20}" />
      </b-form-group>

      <b-form-group
        label="Email"
        label-for="email">
        <b-form-input
          type="email"
          id="email"
          placeholder="Enter email address"
          v-model="user.email"
          v-validate="{required : true , max:50}" />
      </b-form-group>
      <b-form-group
        label="Password"
        label-for="password">
        <b-form-input
          type="password"
          id="password"
          placeholder="Enter password"
        
          v-model="user.password"
          v-validate="{required : true , min: 6 ,max:40}" />
      </b-form-group>
      <b-button
        variant="primary"
        @click="handleRegister">
        Sign Up
      </b-button>
    </b-form>
  </b-container>
</template>

<script>
export default {
  name: "MemberRegisterView",
  data() {
    return {
      user: {},
    };
  },
  methods: {
    handleRegister() {
      this.$store.dispatch("loginModule/signUp", this.user)
    },
  },
};
</script>

그래서 console을 확인해보면!!!

그래서 찾아보니,

Action handlers receive a context object which exposes the same set of methods/properties on the store instance, so you can call context.commit to commit a mutation, or access the state and getters via context.state and context.getters

이렇게 되어있다.

Action 은 context 객체를 받아야하는거다.

그 context 객체 안에는 commit, dispatch, getters  등이 들어있는거다! 

그래서 바꾼거!

loginModule.js

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

const loginModule = {
    namespaced: true,    
      actions: {
        signUp(context, params){
          console.log('signup action activated')
          console.log(params)
           
        },
      },
}
export default loginModule;

이렇게 actions 에 받는아이에 context를 넣으면, 

이렇게 콘솔창에 잘 뜬다!!!

 


회원가입으로 다시 돌아가면, 

axios 를 실행한 후에 backend 에서 쿼리를 실행하다 오류가 발생했을때, actions에서 아무것도 안하고 그냥 console에 에러만 발생하더라. 그래서 위에서 배운 try catch 를 넣어봤다. (에러 처리를 위해서)

loginModule.js

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

const loginModule = {
    namespaced: true,    
      actions: {
        async signUp(context, params){
          try{
            const res = await axios.post('/api/signUp/action',params);
            if(res.data.isSuccess == true){
              alert('회원가입이 완료되었습니다.');
            }else{
              alert('화원가입을 진행중 오류가 발생했습니다. 관리자에게 문의해주세요.');
            }
          }catch{
            alert('화원가입을 진행중 오류가 발생했습니다. 관리자에게 문의해주세요.');
          }
        },
      },
}
export default loginModule;

이렇게 했더니, 

 

alert창이 잘 떳다.

그런데 이제 생각해보니, 회원가입 전에 있는 이메일주소인지를 확인을 하고 넣어야하는데...? 라는 생각이 들었다.

그래서 그과정을 넣어보도록하겠다.

java에서 미리 확인해서 보내는 과정을 넣었다. 

// 회원가입
	@PostMapping(value = "/signUp/action")
	@ResponseBody
	public HashMap<String, Object> signUpAction(@RequestBody HashMap<String, Object> paramMap){
		HashMap<String, Object> returnMap = new HashMap<>();
		System.out.println(paramMap);
		if(paramMap != null) {
			// 일단 입력한 이메일주소가 이미 등록되었는지 확인하기
			int emailDupCheck = service.getCountMember(paramMap);
			if(emailDupCheck > 0) {
				returnMap.put("isSuccess", false);
				returnMap.put("errorMsg", "emailDup");
			}else {
				int result = service.addNewMemeber(paramMap);
				if(result == 1) {
					returnMap.put("isSuccess", true);
				}else {
					returnMap.put("isSuccess", false);
				}
			}
		}
		
		return returnMap;
	}

loginModule.js

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

const loginModule = {
    namespaced: true,    
      actions: {        
        async signUp(context, params){
          try{
            const res = await axios.post('/api/signUp/action',params);
            if(res.data.isSuccess == true){
              alert('회원가입이 완료되었습니다.');
            }else{
              if(res.data.errorMsg != null && res.data.errorMsg == 'emailDup'){
                alert('이미 등록된 이메일 주소입니다. 다시 입력해주세요.');  
              }else{
                alert('화원가입을 진행중 오류가 발생했습니다. 관리자에게 문의해주세요.');
              }
            }
          }catch{
            alert('화원가입을 진행중 오류가 발생했습니다. 관리자에게 문의해주세요.');
          }
        },
      },

}
export default loginModule;

이렇게 했는데.... 말이 안되는 것 같다. 왜냐면, 일단 vue 를 쓰는건 결국엔 frontend와 backend를 완전히 구분지어서 작업하는게 가능하게 한것인데. 결국엔 backend 개발자가 front를 건드리는 느낌..... 아니면 axios는 결국에는 backend의 작업이니 이게 맞는건가.... 잘모르겟다.

좀더 깔끔하게 사용하기 위해서, loginModule.js 에서는 확실하게 Vuex에 관한 것만 넣어놓고 싶기때문에, alert처럼 에러나 action에 대한 각각의 처리는 vue에서 하는게 맞는것같다 ( 이미 만들어져 있는 프로젝트를 참고해보면 ) -> 물어보고 시도해보겟다. 왜냐면, backend 에서 error로 넘겨줘야하는데... 잘모르겟다.

 

 

반응형
반응형

참고: https://vuex.vuejs.org/guide/state.html#getting-vuex-state-into-vue-components

참조: https://v3.vuex.vuejs.org/guide/getters.html

VuexPractice.vue

<template>
  <div>
    <b-button @click="clickAction">
      메롱
    </b-button>
  </div>
</template>

<script>
export default {
  
  methods: {
    clickAction(){
      this.$store.commit('increment')
      console.log(this.$store.state.count)
    }
  },

}
</script>

 

VuexPractice.store.js

const vuexPraticeModule ={
    state: {
        count:0
    },
    mutations: {
        increment(state){
            
            state.count++
            
        }
    }
}

export default vuexPraticeModule

 

store.js

//import router from "@/router";

import Vue from "vue";
import Vuex from "vuex";
import loginModule from "./loginModule";
import vuexPraticeModule from "./VuexPratice.store"

Vue.use(Vuex);

// store는 페이지 새로고침시 데이터가 초기화된다. -> localStorage를 이용

export default new Vuex.Store({
  modules:{
    loginModule,
    vuexPraticeModule,
  }
});

근데 console창에 undefined 가 뜬다

this.$store.state 로 찍었는데 저렇게 안에 잇는데도... 나오지않는다... 모르겟다.. 그러니 일단 넘어가보자!

 

내생각엔, module화를 함에 있어서 뭔가 내가 잘못한 부분이 있는 것 같다. 그래서 그냥 store.js 에 module화 시켰던 부분을 그냥 넣었더니 잘 작동하더라!
//import router from "@/router";

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


Vue.use(Vuex);

// store는 페이지 새로고침시 데이터가 초기화된다. -> localStorage를 이용

export default new Vuex.Store({
  modules:{
    loginModule,
    
  },
  // 모듈화로 뺏던 부분을 넣었다
  state:{
    count:0
  },
  mutations: {
    increment(state){
        
        state.count++
        
    }
}
});

그리고, 역시나 새로고침하면 count는 다시 시작된다

뒤에까지 모두 공부한 후에 왜 안됬는지 공부해야함!!!!

When using a module system, it requires importing the store in every component that uses store state, and also requires mocking when testing the component. 여기에 아마 키가 있지 않나 싶다. 하지만 잠시 넘어가자


전역으로 저장해서 사용해보기

main.js

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import axios from "axios";

new Vue({
  router,
  store,
  render: (h) => h(App),
  computed:{
    count(){
      return this.$store.state.count
    }
  }
}).$mount("#app");

VuexPractice.vue

<template>
  <div>
    {{ count }}
    
  </div>
</template>

<script>
export default {
 
}
</script>

하지만 App 에는 count 가 존재하지않았다.

그래서, 다시 생각해낸 방법 -> 전역으로 가지고 있는게 아닌것같으니 vuex를 전역으로 가지게해보자

main.js

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({
  state:{
    count:0
  }
})

new Vue({
  router,
  store,
  render: (h) => h(App),
   computed:{
     count(){
      
       return this.$store.state.count
     }
   }
}).$mount("#app");

App과 Root에 모두 count가 찍혓는데.... 왜 도대체가 VuexPractice 컴포넌트에는 안찍히는거니.. 혹시 props로 주고받는거해야하니? 근데.. 공식문서에서는 됫자나!!!!!!!!!!

 

Vuex provides a mechanism to "inject" the store into all child components from the root component with the store option (enabled by Vue.use(Vuex)):

 

성공한거!!!! >>>> By providing the store option to the root instance, the store will be injected into all child components of the root and will be available on them as this.$store.

여기서 힌트를 얻엇다!!!main.js

import Vue from "vue";
import App from "./App.vue";
import store from "./store";
import Vuex from 'vuex'

Vue.use(Vuex);

const Counter = {
  template: `<div>{{count}}</div>`,
  computed:{
    count(){
      return store.state.count
    }
  }
}
new Vue({
  router,
  store,
  components: {Counter},
  template: `<div class="app"><counter></counter></div>`,
  render: (h) => h(App),
}).$mount("#app");

store.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state:{
    count: 12
  }, 
});

 

VuexPractice.vue

<template>
  <div>
    {{ count }}
    <b-button>
      메롱
    </b-button>
  </div>
</template>

<script>
export default {
  computed:{
    count(){
      return this.$store.state.count
    }
  }
}
</script>

성공했다ㅏㅏㅏㅏ

아무리 Root에 있다고 하지만, 그래도 state에 접근해있는 변수를 가져오는 것이기때문에 $store에 접근해서 가져와야한다!


mapState helper

state에 너무 많은 변수들이 선언되어있을 때 쉽게 사용하기 위함이라고한다.

mapState helper which generates computed getter functions for us, saving us some keystrokes:

 


Getters
Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. Like computed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed.
Getter는 컴포넌트의 computed와 비슷한 역할을 수행한다. 즉, Vuex Store로부터 상태 값을 읽어올 때 바로 읽어오는 것이 아니라 해당 상태 값을 활용해 계산된 속성으로 읽어오게 된다. 예를 들면, 특정 배열 상태 값이 있다고 할 때 해당 상태 값의 길이를 읽어오는 Getter를 생성해 사용할 수 있다.

store.js

import Vue from "vue";
import Vuex from "vuex";
import axios from 'axios'

Vue.use(Vuex);

export default new Vuex.Store({
  state:{
    user: [
      {userName: '박민아', userRole: '팀원'},
      {userName: '김지아', userRole: '팀원'}
  ],
    isLogin: false,
  },
  getters:{
    getUserByName: (state)=>(userName)=>{
      return state.user.find(user => user.userName === userName)
    }
  },  
});

VuexPractice.vue

<template>
  <div>
    <b-input />
    <b-button @click="clickEvent">
      메롱
    </b-button>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  methods:{
    clickEvent(){
      console.log(this.$store.getters.getUserByName('박민아'))
    },
  }
}
</script>

 

잘찍혓다!

mapGetters

store.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state:{
    count: 12,
    user: [
      {userName: '박민아', userRole: '팀원'},
      {userName: '김지아', userRole: '팀원'}
  ],
    isLogin: false,
  },
  getters:{
    getUserByName: (state)=>(userName)=>{
      return state.user.find(user => user.userName === userName)
    }
  },
});

VuexPractice.vue

<template>
  <div>
    <b-button @click="clickEvent">
      메롱
    </b-button>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed:{
    ...mapGetters(['getUserByName']),
  },
  methods:{
    clickEvent(){
      console.log(this.getUserByName('박민아'))
    },
  }
}
</script>

그러면 , this. 으로 getters에 있는 값에 접근이 가능하다! 

잘찍혓구만


mutations
One important rule to remember is that mutation handler functions must be synchronous. Why?

Now imagine we are debugging the app and looking at the devtool's mutation logs. For every mutation logged, the devtool will need to capture a "before" and "after" snapshot of the state. However, the asynchronous callback inside makes that impossible: the callback is not called yet when the mutation is committed, and there's no way for the devtool to know when the callback will actually be called - any state mutation performed in the callback is essentially un-trackable!

반응형

store.js

import Vue from "vue";
import Vuex from "vuex";
import axios from 'axios'


Vue.use(Vuex);

export default new Vuex.Store({
  state:{
    count: 12,
    user: {userName: null, userRole: '팀원'},
  },
  getters:{
    getUserInfo: state => state.user,
    
  },
  mutations:{
    setUserName: (state, payload) => state.user.userName = payload
  },
  actions:{
    getUserinfo({ commit }, loginEmail){
      return axios.post('/api/getUserInfo',{ userEmail:loginEmail })
      .then(function(response){
     	console.log(response)
        commit("setUserName", response.data.NAME);
      })
      .catch(function(){
        console.log('실패');
      })
    },
  }
  
});

VuexPractice.vue

<template>
  <div>
    <b-input v-model="loginEmail" />
    <b-button @click="clickEvent">
      메롱
    </b-button>
    {{ loginUserName }}님
  </div>
</template>

<script>
import { mapGetters, mapMutations } from 'vuex'

export default {
  data(){
    return{
      loginEmail: "",
      loginUserName: "",
    }
  },
  computed:{
    ...mapGetters(['getUserByName', 'getUserInfo']),
    ...mapMutations(['setIsLogin']),
  },
  methods:{
    clickEvent(){
      this.$store.dispatch("getUserinfo", this.loginEmail)
      console.log('axios 후에 user' + this.$store.state.user)
      this.loginUserName = this.getUserInfo.userName
    },

  }
}
</script>

 

 

여기서 발견한 신기한 점!!!! >>>> console을 지금 dispatch 후에 찍어두고, axios 의 then 에 찍어뒀는데!!!! 놀라운 사실, console에 찍힌 순서를 보면 먼저 dispatch 후에 찍혀야하는 그 console이 먼저 찍혔다!

이건 비동기의 문제라고 생각한다! 전에 배운것처럼 언제 뭐가 먼저 실행될지 모르는 것이기때문에, 먼저 axios가 완료되면 그 다음으로 넘어가게 해줄 수 있게 설정해줘야한다고 배웠다!

 

첫번째, 그래서 VuexPractice.vue 에서 axios 실행되는 dispatch 부분을 수정했다.

async 와 await 를 이용해서 보완해봤다!!! async/await

먼저 함수 내에서 await 을 사용하기 위해 함수앞에 async 를 넣어준다. await 을 사용하여 요청을보내면 응답받은 값을 같은 스코프 내에서 처리가 가능해져서 훨씬 보기도 편하고, 코드도 간결해지고 콜백 지옥 현상도 발생하지 않는다. 

methods:{
    async clickEvent(){
      await this.$store.dispatch("getUserinfo", this.loginEmail)
      console.log('axios 후에 user' + this.$store.state.user.userName)
      this.loginUserName = this.getUserInfo.userName
    },
  }

순서대로 console이 찍힌 것을 볼 수가 있다!

하지만, 여전히 state의 user는 setting 되어있지 않다. 그렇다는 말은 action안에서도 순서대로 실행될 수 있도록 설정해주면 바뀌는지 확인해보자!

그래서 수정한 main.js 의 actions 부분

 actions:{
    async getUserinfo({ commit }, loginEmail){
      return await axios.post('/api/getUserInfo',{ userEmail:loginEmail })
      .then(function(response){
        console.log(response)
         
        commit("setUserName", response.data.NAME);
        
      })
      .catch(function(){
        console.log('실패');
      })
    },
  }

성공!!!

떴다!!!!

 

 

 

 

반응형
반응형

Vuex를 공부하는데...

어떠한 기능 하나 하나 넣을때마다 이해안되는 것 투성이고, reference를 찾아서 시도해보면.....동작이 안하거나, 동작이 해도 그 원리가 이해가 완전히 되지 않거나... 뇌피셜이 너무 많다는거

 

그래서 결국 내려진 처방은???!!! 뚜둥

완전 기초부터 Vuex에 잇는 모든 기능들 사용하면서 연습해보기

 

역시나 부실공사였던거지.... spring boot 랑 연결하고 DB랑 연결하면 무슨소용이냐구..... 

기본이 안되어있어서 로그인 하나 구현하는데 지금 거의 한달인데...ㅠㅠㅠ 

 

 

그런데 다시 기초부터 하나하나 일일이 공부해가면서 기능 하나하나 집중해서 공부하다보니 깨달았다.

 

역시 투자한 시간에 실력이 비례한다는거다!!

 

 

분명 어제는 몰라서 그렇게 헤매던건데 계속 이것저것 하면서 시간도 버리고 삽질을 많이 하다보니, 해결이 되고 이해가 되고 하게되었다. 그리고 안다고 생각하고 넘어가던 그런 자질구레한 설명들도 읽어둬야한다는거다.

그랬더니 전체적인 흐름도 이해가 되었다!

 

오랜만에 자려고 누워서 눈감을때까지 왜 안됫을까? 하면서 내가 짠 코드를 머리속에서 생각하면서 잠이 들었다. 이런 재미가 있었지 하는 기분을 오랜만에 느껴서 좋긴하지만... 시간이 없다ㅠㅠㅠ 너무 핑계지만... ㅠㅠ

진짜 다음달에는 사람들 만나는 시간을 많이 줄여야지

어제 오늘의 하이라이트 >>> Vue warn

반응형

+ Recent posts