우선 회원가입과 로그인 뷰를 먼저 생성할건데, 그 전에 라우팅이 필요하기 때문에 vue router를 구성해보자.
import { createApp } from 'vue'
import App from './App.vue'
import router from './routes/index.js'
createApp(App)
.use(router)
.mount('#app')
main.js에 router 플러그인을 적용해준다.
import { createRouter, createWebHistory } from 'vue-router';
import Main from '../view/Main.vue';
import Join from '../view/Join.vue';
import Login from '../view/Login.vue';
export default createRouter({ //vue-router version 4 문법
history: createWebHistory(),
routes: [
{
path: '/',
name: 'Main',
component: Main
},
{
path: '/join',
name: 'Join',
component: Join
},
{
path: '/login',
name: 'Login',
component: Login
}
]
})
/경로이면 Main 뷰를, /join 경로이면 Join 뷰를, /login 경로이면 Login 뷰를 라우팅해준다.
폼 입력값에 대해 유효성을 검사하기 위해 vee-validate 라이브러리를 사용하였다.
import { Form, Field } from 'vee-validate';
export default {
components: {
Form,
Field
},
}
Field는 단일 폼 input을 나타내는 컴포넌트이며 Form은 form을 랜더하는 컴포넌트이다.
<template>
<Form @submit="onSubmit">
<div class="container mb-4 w-25">
<div class="userIcon">
<font-awesome-icon icon="circle-user" size="5x" color="lightgray"/>
</div>
<Field name="id" type="id">
<div class="field">
<label for="id" class="form-label">아이디</label>
<input class="form-control"/>
</div>
</Field>
<Field name="password" type="password">
<div class="field">
<label for="pwd" class="form-label">비밀번호</label>
<input class="form-control" type="password"/>
<span class="errMsg"></span>
</div>
</Field>
<div class="loginErr" v-if="loginErr">로그인에 실패하였습니다.</div>
<button type="submit" class="btn btn-secondary mb-3">로그인</button>
</div>
</Form>
</template>
field-level에서 유효성 검사를 할 수도 있고 form-level에서 유효성 검사를 할수도 있는데, 여러 필드를 합쳐 유효성 검사를 한번에 하기 위해 나는 후자의 방식을 사용했다.
yup는 vee-validate와 함께 잘 사용되는 data validation 라이브러리다.
import { object, string } from 'yup';
yup로부터 사용할 rule들을 import한 후
computed: {
schema() {
return object({
id: string().required('아이디를 입력해주세요.'),
password: string().required('비밀번호를 입력해주세요.').matches(/^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[#?!@$%^&*-])(?=.{8,})/,'영문자, 숫자, 특수문자를 조합하여 최소8자리를 입력해주세요.')
});
},
}
rule을 작성해준다. 이 때 반응형으로 폼 스키마를 생성하기 위해 computed에 작성한다.
<Form :validation-schema="schema">
props를 사용하여 스키마를 Form에 전달해준다.
<Field name="id" type="id" v-slot="{ field, errorMessage, meta }">
<div class="field">
<label for="id" class="form-label">아이디</label>
<input class="form-control" v-bind="field" :class="{ 'is-invalid': !meta.valid && errorMessage }"/>
<span class="errMsg" v-if="errorMessage && !meta.valid">{{ errorMessage }}</span>
</div>
</Field>
v-slot을 통해 해당 컴포넌트에 지정된 slot을 가져올 수 있다. slot에는 몇가지 속성들이 있는데 그 중에서 field, errorMessage, meta를 사용하였다.
field : v-bind input 값에 vee-validate의 기능을 적용시킬 수 있게끔 하는 속성
errorMessage : 오류 메세지를 나타내는 속성
meta : field 상태에 대한 유용한 정보들을 갖고 있는 속성
:class="{ 'is-invalid': !meta.valid && errorMessage }"
해당 필드의 값이 유효하지 않으면서 에러메세지가 존재한다면 'is-invalid' 값을 부여하는 클래스 바인딩을 적용했다. 바인딩 결과는 아래와 같다.

로그인 버튼을 누르기 전 반응형으로 field를 검사하기 때문에 바로바로 화면에 에러메세지가 출력된다.
<Form v-else @submit="onSubmit" :validation-schema="schema">
Form에 onSubmit 핸들러를 추가하면, 폼 제출 시 vee-validate가 폼 입력 값들을 핸들러의 첫번째 인자로 전달해준다.
각 필드에 대한 추가 error 관리도 가능한데, 이때 인자로 actions를 전달해주면 됨!
methods: {
async onSubmit(userData, actions) {
try{
const res = await joinUser(userData); //API 통신
if(!res.data.resultData.duplicatedId){
this.showSuccessModal();
}else{
actions.setFieldError('id', '이미 사용중인 아이디 입니다.');
}
}catch(err) {
console.log(err);
}
}
}
이 후 api 통신 코드를 작성해주면 끝!
로그인 뷰도 회원가입 뷰와 같은 방식으로 생성해주면 된다.