front-end/Vue.js

Axios 기초 배우기 #0 (기본세팅)

정구 2022. 2. 23. 10:12
728x90

Vuetify를 활용하여 템플릿을 꾸미고 서버와의 통신 없이 회원 DB 같은 정보를 만들어 단순한 로그인 기능을 구현하였다.

간단한 라우팅을 지원한다.

 

앞으로 이 예제를 서버 통신을 통한 로그인 기능으로 개선한다.

 

 

 

src/App.vue

<template>
  <div id="app">
    <v-app id="inspire">
      <v-app id="inspire">
        <v-navigation-drawer v-model="drawer" app>
          <v-list dense>
            <v-list-item :to="{ path: '/' }">
              <v-list-item-action>
                <v-icon>mdi-home</v-icon>
              </v-list-item-action>
              <v-list-item-content>
                <v-list-item-title>Home</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
            <v-list-item v-if="isLogin === false" :to="{ path: '/login' }">
              <v-list-item-action>
                <v-icon>mdi-email</v-icon>
              </v-list-item-action>
              <v-list-item-content>
                <v-list-item-title>로그인</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
            <v-list-item v-else :to="{ path: '/mypage' }">
              <v-list-item-action>
                <v-icon>mdi-dog</v-icon>
              </v-list-item-action>
              <v-list-item-content>
                <v-list-item-title>mypage</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list>
        </v-navigation-drawer>

        <v-app-bar app color="indigo" dark>
          <v-app-bar-nav-icon
            @click.stop="drawer = !drawer"
          ></v-app-bar-nav-icon>
          <v-toolbar-title>Application</v-toolbar-title>
          <v-spacer></v-spacer>
          <v-toolbar-items class="hidden-sm-and-down">
            <v-menu offset-y v-if="isLogin">
              <template v-slot:activator="{ on, attrs }">
                <v-btn text icon v-bind="attrs" v-on="on">
                  <v-icon>mdi-dots-vertical</v-icon>
                </v-btn>
              </template>
              <v-list>
                <v-list-item :to="{ path: '/mypage' }">
                  <v-list-item-title>마이페이지</v-list-item-title>
                </v-list-item>
                <v-list-item @click="$store.dispatch('logout')">
                  <v-list-item-title>로그아웃</v-list-item-title>
                </v-list-item>
              </v-list>
            </v-menu>
            <v-btn text v-else :to="{ path: '/login' }">로그인</v-btn>
          </v-toolbar-items>
        </v-app-bar>

        <v-main>
          <router-view />
        </v-main>
        <v-footer color="indigo" app>
          <span class="white--text">&copy; {{ new Date().getFullYear() }}</span>
        </v-footer>
      </v-app>
    </v-app>
  </div>
</template>

<script>
import { mapState } from "vuex";
export default {
  name: "App",

  data: () => ({
    drawer: null,
  }),
  computed: {
    ...mapState(["isLogin"]),
  },
  methods: {
    // ...mapActions(["logout"]),
  },

  props: {
    source: String,
  },
};
</script>

 

 

 

src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  vuetify,
  render: h => h(App)
}).$mount('#app')

 

 

 

src/views/Home.vue

<template>
  <hello-world />
</template>

<script>
  import HelloWorld from '../components/HelloWorld'  //HelloWorld 컴포넌트 없어도 됨

  export default {
    name: 'Home',

    components: {
      HelloWorld,
    },
  }
</script>

 

 

 

src/views/Login.vue

<template>
  <v-container fill-height style="max-width:450px;">
    <v-layout align-center row wrap>
      <v-flex xs12>
        <v-alert type="error" :value="isLoginError">
          아이디와 비밀번호를 확인하세요
        </v-alert>
        <v-alert type="success" :value="isLogin">
          로그인 성공
        </v-alert>
        <v-card>
          <v-toolbar flat color="blue-grey lighten-5">
            <v-toolbar-title>로그인</v-toolbar-title>
          </v-toolbar>
          <div class="pa-3">
            <v-text-field v-model="email" label="이메일을 입력하세요">
            </v-text-field>
            <v-text-field
              v-model="password"
              type="password"
              label="패스워드를 입력하세요"
            >
            </v-text-field>
            <v-btn
              block
              large
              depressed
              color="primary"
              @click="login({ email, password })"
              >로그인</v-btn
            >
          </div>
        </v-card>
      </v-flex>
    </v-layout>
  </v-container>
</template>
\
<script>
import { mapState, mapActions } from "vuex";
export default {
  data() {
    return {
      email: null,
      password: null,
    };
  },
  computed: {
    ...mapState(["isLogin", "isLoginError"]),
  },
  methods: {
    ...mapActions(["login"]),
  },
};
</script>

 

 

 

src/views/Mypage.vue

<template>
  <div>
    <h1>{{ userInfo.name }}님 환영합니다.</h1>
  </div>
</template>

<script>
import { mapState } from "vuex";
export default {
  computed: {
    ...mapState(["userInfo"]),
  },
};
</script>

 

 

 

src/store/index.js

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

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    userInfo: null,
    allUsers: [
      { id: 1, name: "hoza", email: "hoza@gmail.com", password: "123456" },
      { id: 2, name: "lego", email: "lego@gmail.com", password: "123456" },
    ],
    isLogin: false,
    isLoginError: false,
  },
  mutations: {
    // 로그인이 성공했을 때,
    loginSuccess(state, payload) {
      state.isLogin = true;
      state.isLoginError = false;
      state.userInfo = payload;
    },
    // 로그인이 실패했을 때
    loginError(state) {
      state.isLogin = false;
      state.isLoginError = true;
    },
    logout(state) {
      state.isLogin = false;
      state.isLoginError = false;
      state.userInfo = null;
    },
  },
  actions: {
    //로그인 시도
    login({ state, commit }, loginObj) {
      let selectedUser = null;
      state.allUsers.forEach((user) => {
        if (user.email === loginObj.email) selectedUser = user;
      });
      if (selectedUser === null || selectedUser.password !== loginObj.password)
        commit("loginError");
      else {
        commit("loginSuccess", selectedUser);
        router.push({ path: "/mypage" });
      }
    },
    logout({ commit }) {
      commit("logout");
      router.push({ path: "/" });
    },
  },
  modules: {},
});

 

 

 

src/router/index.js

import Vue from "vue";
import VueRouter from "vue-router";
import store from "../store";

Vue.use(VueRouter);

const rejectAuthUser = (to, from, next) => {
  if (store.state.isLogin === true) {
    //이미 로그인된 유저니깐 막아야됨
    alert("이미 로그인되어있습니다.");
    next("/");
  } else {
    next();
  }
};

const onlyAuthUser = (to, from, next) => {
  if (store.state.isLogin === false) {
    //아직 로그인된 유저니깐 막아야됨
    alert("로그인이 필요합니다.");
    next("/");
  } else {
    next();
  }
};

const routes = [
  {
    path: "/",
    name: "home",
    component: () => import(/* webpackChunkName: "home" */ "../views/Home.vue"),
  },
  {
    path: "/login",
    name: "login",
    beforeEnter: rejectAuthUser,
    component: () =>
      import(/* webpackChunkName: "login" */ "../views/Login.vue"),
  },
  {
    path: "/mypage",
    name: "mypage",
    beforeEnter: onlyAuthUser,
    component: () =>
      import(/* webpackChunkName: "mypage" */ "../views/Mypage.vue"),
  },
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});

export default router;

 

 

 

혹시 몰라 package.json도 남긴다.

src/package.json

{
  "name": "login_ex",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuetify": "^2.4.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "sass": "~1.32.0",
    "sass-loader": "^10.0.0",
    "vue-cli-plugin-vuetify": "~2.4.2",
    "vue-template-compiler": "^2.6.11",
    "vuetify-loader": "^1.7.0"
  }
}

 

 

728x90