본문 바로가기
Dev/Vue.js

[Vue.js] Today I Learned - 로그인

by dev_jsk 2020. 10. 27.
728x90
반응형

로그인

웹사이트를 이용하기 위한 가장 기본적인 과정. 추가적인 기능으로는 성공 시 처리 뿐만 아니라 로그인 실패 시 처리, 유효성 검사, 로그아웃 처리가 필요하다.

로그인 기능 함수

전달받은 정보를 API서버로 전송하여 결과를 받는 함수 생성

// src/api/index.js
import axios from 'axios';

const instance = axios.create({
  baseURL: process.env.VUE_APP_API_URL,
});
// 로그인 함수
function loginUser(userData) {
  return instance.post('login', userData);
}

export { loginUser };

데이터 바인딩

폼에 입력된 데이터를 바인딩하여 API 함수와 연결

<!-- src/components/LoginForm.vue -->
<!-- v-model 을 이용해 데이터 바인딩 -->
<input id="username" type="text" v-model="username" />
<input id="password" type="text" v-model="password" />

<script>
import { loginUser } from '@/api/index';

export default {
  data() {
    return {
      // form values
      username: '',
      password: '',
    };
  },
  methods: {
    async submitForm() {
      const userData = {
        username: this.username,
        password: this.password,
      };
      // API 함수 loginUser() 호출
      const { data } = await loginUser(userData);
      console.log(data.user.username);
      // 입력란 비우기
      this.initForm();
    },
    initForm() {
      this.username = '';
      this.password = '';
    },
  },
};
</script>

에러 처리

성공 처리보다 중요한 것이 에러가 발생했을 때의 처리이다. try-catch문을 이용하여 에러 처리를 할 수 있다.

// src/components/LoginForm.vue
export default {
  data() {
    return {
      // form values
      username: '',
      password: '',
    };
  },
  methods: {
    async submitForm() {
      // try-catch문 사용
      try {
        // 비즈니스 로직
        const userData = {
          username: this.username,
          password: this.password,
        };
        const { data } = await loginUser(userData);
        console.log(data.user.username);
      } catch (error) {
        // 에러 핸들링할 코드
        console.log(error.response.data);
      } finally {
        this.initForm();
      }
    },
    initForm() {
      this.username = '';
      this.password = '';
    },
  },
};

입력란 유효성 검사

입력란에 대해 유효성 검사를 하여 올바른 형식이 아니거나 빈값일 경우 로그인이 불가능하도록 검사한다.

 

이메일 형식 검사 함수

해당 프로젝트에서는 아이디를 이메일 형식으로 사용하고 있기 때문에 이메일 형식 검사 함수가 필요하다. 또한, 로그인 기능 뿐만 아니라 회원가입 시에도 이메일 형식을 검사해야 하기 때문에 별도의 파일로 관리한다.

// src/utils/validation.js
// 이메일 형식 검사
function validateEmail(email) {
  // 이메일 형식 정규표현식
  var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return re.test(String(email).toLowerCase());
}

export { validateEmail };

computed 속성을 이용한 유효성 검사 동작

computed 속성을 이용하여 boolean 형식의 값을 통해 올바른 형식일 경우에만 기능동작이 가능하게 구성한다.

<!-- src/components/LoginForm.vue -->
<template>
  <div class="contents">
    <div class="form-wrapper form-wrapper-sm">
      <form @submit.prevent="submitForm" class="form">
        <div>
          <label for="username">id:</label>
          <input id="username" type="text" v-model="username" />
          <p class="validation-text">
            <!-- 이메일 형식 및 입력란 공백 확인 -->
            <span class="warning" v-if="!isUsernameValid && username">
              Please enter an email address
            </span>
          </p>
        </div>
        <div>
          <label for="password">pw:</label>
          <input id="password" type="text" v-model="password" />
        </div>
        <!-- 이메일 형식 및 입력란 공백 확인 -->
        <button
          :disabled="!isUsernameValid || !password"
          type="submit"
          class="btn"
        >
          로그인
        </button>
      </form>
      <p class="log">{{ logMessage }}</p>
    </div>
  </div>
</template>

<script>
import { loginUser } from '@/api/index';
import { validateEmail } from '@/utils/validation';

export default {
  data() {
    return {
      // form values
      username: '',
      password: '',
      // log
      logMessage: '',
    };
  },
  computed: {
    // username 형식 확인하여 true/false 리턴
    isUsernameValid() {
      return validateEmail(this.username);
    },
  },
  methods: {
    async submitForm() {
      try {
        // 비즈니스 로직
        const userData = {
          username: this.username,
          password: this.password,
        };
        const { data } = await loginUser(userData);
        console.log(data.user.username);
        this.$store.commit('setUsername', data.user.username);
        // 메인 페이지 이동
        this.$router.push('/main');
        // this.logMessage = `${data.user.username} 님 환영합니다`;
      } catch (error) {
        // 에러 핸들링할 코드
        console.log(error.response.data);
        this.logMessage = error.response.data;
      } finally {
        this.initForm();
      }
    },
    initForm() {
      this.username = '';
      this.password = '';
    },
  },
};
</script>

로그인 후 메인 페이지 이동

정상적으로 로그인 후 메인 페이지로 이동하도록 자바스크립트를 통해 구현한다. (router.push 참고)

// src/components/LoginForm.vue
methods: {
  async submitForm() {
    try {
      // 비즈니스 로직
      const userData = {
        username: this.username,
        password: this.password,
      };
      const { data } = await loginUser(userData);
      this.$store.commit('setUsername', data.user.username);
      // 메인 페이지 이동
      this.$router.push('/main');
    } catch (error) {
      // 에러 핸들링할 코드
      console.log(error.response.data);
    } finally {
      this.initForm();
    }
  },
  initForm() {
    this.username = '';
    this.password = '';
  },
},

로그인 한 아이디 헤더에 표시하기

로그인 후 헤더 영역에서 로그인 여부를 분기처리하여 로그인한 사용자의 아이디를 표시하거나, 미로그인 상태일 경우 로그인, 회원가입 이동이 가능한 텍스트를 표시하도록 구현한다.

 

데이터 전달을 위한 Vuex 설치

npm i vuex

Vuex 등록

Vuex를 사용하기 위해 src/store/index.js 파일을 생성하여 Vuex 환경을 구성하고 main.js에 Vuex 환경을 구성한 파일을 등록한다.

// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    username: '',
  },
  getters: {
    isLogin(state) {
      // state.username 값의 유무에 따라 true, false를 리턴한다.
      return state.username !== '';
    },
  },
  mutations: {
    setUsername(state, username) {
      state.username = username;
    },
    // 로그아웃을 username 값을 공백으로 하는것으로 처리
    clearUsername(state) {
      state.username = '';
    },
  },
});

// main.js
import Vue from 'vue';
import App from './App.vue';
import router from '@/routes/index';
import store from '@/store/index';

Vue.config.productionTip = false;

new Vue({
  render: h => h(App),
  router,
  // store(Vuex) 등록
  store,
}).$mount('#app');

로그인 아이디 헤더에 표시

로그인에 성공하였을 경우 헤더에 로그인 아이디를 표시한다.

<!-- src/components/LoginForm.vue -->
<script>
  methods: {
    async submitForm() {
      try {
        // 비즈니스 로직
        const userData = {
          username: this.username,
          password: this.password,
        };
        const { data } = await loginUser(userData);
        // 로그인 아이디 전달
        this.$store.commit('setUsername', data.user.username);
        // 메인 페이지 이동
        this.$router.push('/main');
      } catch (error) {
        // 에러 핸들링할 코드
        console.log(error.response.data);
      } finally {
        this.initForm();
      }
    },
    initForm() {
      this.username = '';
      this.password = '';
    },
  },
</script>

<!-- src/components/common/AppHeader.vue -->
<template>
  <!-- 전달받은 로그인 아이디 출력 -->
  <span class="username">{{ $store.state.username }}</span>
</template>

로그인 유무에 따라 헤더 버튼 분기 처리

로그인 상태일 경우 아이디 및 로그아웃 표시, 미로그인 상태일 경우 로그인 및 회원가입 이 표시되도록 버튼 분기처리를 한다.

<!-- src/components/common/AppHeader.vue -->
<template>
  <header>
    <div>
      <router-link to="/" class="logo">
        TIL
      </router-link>
    </div>
    <div class="navigations">
      <!-- 로그인 후 -->
      <template v-if="isUserLogin">
        <span class="username">{{ $store.state.username }}</span>
      </template>
      <!-- 로그인 전 -->
      <template v-else>
        <router-link to="/login">로그인</router-link>
        <router-link to="/signup">회원가입</router-link>
      </template>
    </div>
  </header>
</template>

<script>
export default {
  computed: {
    // 로그인 유무 확인
    isUserLogin() {
      return this.$store.getters.isLogin;
    },
  },
};
</script>

로그아웃 처리

Vuex 구성 시 정의한 clearUsername() 함수를 이용하여 로그아웃 처리를 구현한다.

<!-- src/components/common/AppHeader.vue -->
<template>
  <header>
    <div>
      <router-link to="/" class="logo">
        TIL
      </router-link>
    </div>
    <div class="navigations">
      <!-- 로그인 후 -->
      <template v-if="isUserLogin">
        <span class="username">{{ $store.state.username }}</span>
        <a href="javascript:;" @click="logoutUser">Logout</a>
      </template>
      <!-- 로그인 전 -->
      <template v-else>
        <router-link to="/login">로그인</router-link>
        <router-link to="/signup">회원가입</router-link>
      </template>
    </div>
  </header>
</template>

<script>
export default {
  computed: {
    // 로그인 유무 확인
    isUserLogin() {
      return this.$store.getters.isLogin;
    },
  },
  methods: {
    // 로그아웃 처리
    logoutUser() {
      this.$store.commit('clearUsername');
      // 로그인 페이지로 이동
      this.$router.push('/login');
    },
  },
};
</script>
728x90
반응형

댓글