
--- client/resources/api/auth.js
+++ client/resources/api/auth.js
... | ... | @@ -1,7 +1,7 @@ |
1 | 1 |
import apiClient from "./index"; |
2 | 2 |
|
3 | 3 |
// 회원가입 기능 |
4 |
-export const resistProc = (data) => { |
|
4 |
+export const registProc = (data) => { |
|
5 | 5 |
return apiClient.post(`/auth/register.json`, data); |
6 | 6 |
}; |
7 | 7 |
|
... | ... | @@ -15,3 +15,12 @@ |
15 | 15 |
return apiClient.post(`/auth/logout.json`); |
16 | 16 |
}; |
17 | 17 |
|
18 |
+// 아이디 중복 확인 |
|
19 |
+export const checkIdProc = (data) => { |
|
20 |
+ return apiClient.post(`/auth/checkId.json`, data); |
|
21 |
+}; |
|
22 |
+ |
|
23 |
+// 이메일 중복 확인 |
|
24 |
+export const checkEmailProc = (data) => { |
|
25 |
+ return apiClient.post(`/auth/checkEmail.json`, data); |
|
26 |
+}; |
--- client/views/pages/common/Join.vue
+++ client/views/pages/common/Join.vue
... | ... | @@ -1,94 +1,110 @@ |
1 | 1 |
<template> |
2 |
- <div class="join-wrap"> |
|
3 |
- <div class="login-logo mb50"> |
|
4 |
- <img src="../../../resources/img/content/login_logo.svg" alt="로고"> |
|
5 |
- </div> |
|
6 |
- <div class="login-box join-box"> |
|
7 |
- <div class="tbl-wrap"> |
|
8 |
- <table class="tbl data"> |
|
9 |
- <colgroup> |
|
10 |
- <col style="width: 180px;"> |
|
11 |
- <col style="width: auto;"> |
|
12 |
- </colgroup> |
|
13 |
- <tbody> |
|
14 |
- <tr> |
|
15 |
- <th style="background-color: #eff1fa;">아이디</th> |
|
16 |
- <td> |
|
17 |
- <div class="input-group"> |
|
18 |
- <input type="text" class="form-control sm" placeholder="3-15자 영문/숫자 조합으로 입력해주세요" v-model="loginId"> |
|
19 |
- <button class="btn sm black">중복확인</button> |
|
20 |
- </div> |
|
21 |
- </td> |
|
22 |
- </tr> |
|
23 |
- <tr> |
|
24 |
- <th style="background-color: #eff1fa;">비밀번호</th> |
|
25 |
- <td><input type="password" class="form-control sm" placeholder="3-16자 영문/숫자 조합으로 입력해주세요" v-model="password"></td> |
|
26 |
- </tr> |
|
27 |
- <tr> |
|
28 |
- <th style="background-color: #eff1fa;">비밀번호 확인</th> |
|
29 |
- <td><input type="password" class="form-control sm" placeholder="3-16자 영문/숫자 조합으로 입력해주세요" v-model="passwordCheck"></td> |
|
30 |
- </tr> |
|
31 |
- </tbody> |
|
32 |
- </table> |
|
2 |
+ <div class="join-wrap"> |
|
3 |
+ <div class="login-logo mb50"> |
|
4 |
+ <img src="../../../resources/img/content/login_logo.svg" alt="로고"> |
|
33 | 5 |
</div> |
34 |
- <div class="tbl-wrap"> |
|
35 |
- <table class="tbl data"> |
|
36 |
- <colgroup> |
|
37 |
- <col style="width: 180px;"> |
|
38 |
- <col style="width: auto;"> |
|
39 |
- </colgroup> |
|
40 |
- <tbody> |
|
41 |
- <tr> |
|
42 |
- <th style="background-color: #eff1fa;">이름</th> |
|
43 |
- <td><input type="text" class="form-control sm" placeholder="한글 15자, 영문 30자까지 가능합니다." v-model="memberName"></td> |
|
44 |
- </tr> |
|
45 |
- <tr> |
|
46 |
- <th style="background-color: #eff1fa;">이메일</th> |
|
47 |
- <td> |
|
48 |
- <div class="input-group"> |
|
49 |
- <input type="text" class="form-control sm" placeholder="3-15자 영문/숫자 조합으로 입력해주세요" v-model="email"> |
|
50 |
- <button class="btn sm black">중복확인</button> |
|
51 |
- </div> |
|
52 |
- </td> |
|
53 |
- </tr> |
|
54 |
- </tbody> |
|
55 |
- </table> |
|
56 |
- </div> |
|
57 |
- <div class="tbl-wrap last-table"> |
|
58 |
- <table class="tbl data"> |
|
59 |
- <colgroup> |
|
60 |
- <col style="width: 180px;"> |
|
61 |
- <col style="width: auto;"> |
|
62 |
- </colgroup> |
|
63 |
- <tbody> |
|
64 |
- <tr> |
|
65 |
- <th style="background-color: #eff1fa;border-radius: 1rem 0 0 1rem; ">전화번호</th> |
|
66 |
- <td> |
|
67 |
- <div class="input-group mb10"> |
|
68 |
- <input type="text" class="form-control sm" placeholder="-제외 휴대전화 번호를 입력해주세요" v-model="phoneNumber"> |
|
69 |
- <button class="btn sm black">인증번호 발송</button> |
|
70 |
- </div> |
|
71 |
- <div class="input-group"> |
|
72 |
- <input type="text" class="form-control sm" placeholder="인증번호 6자리 숫자를 입력해주세요" style="background-color: #ebebeb;" v-model="vertifiCode"> |
|
73 |
- <button class="btn sm black" style="min-width: 119px;">확인</button> |
|
74 |
- </div> |
|
75 |
- </td> |
|
76 |
- </tr> |
|
77 |
- </tbody> |
|
78 |
- </table> |
|
79 |
- </div> |
|
6 |
+ <div class="login-box join-box"> |
|
7 |
+ <div class="tbl-wrap"> |
|
8 |
+ <table class="tbl data"> |
|
9 |
+ <colgroup> |
|
10 |
+ <col style="width: 180px;"> |
|
11 |
+ <col style="width: auto;"> |
|
12 |
+ </colgroup> |
|
13 |
+ <tbody> |
|
14 |
+ <tr> |
|
15 |
+ <th style="background-color: #eff1fa;">아이디</th> |
|
16 |
+ <td> |
|
17 |
+ <div class="input-group"> |
|
18 |
+ <input type="text" class="form-control sm" placeholder="3-15자 영문/숫자 조합으로 입력해주세요" |
|
19 |
+ v-model="loginId"> |
|
20 |
+ <button class="btn sm black" @click="checkIdProc">중복확인</button> |
|
21 |
+ </div> |
|
22 |
+ </td> |
|
23 |
+ </tr> |
|
24 |
+ <tr> |
|
25 |
+ <th style="background-color: #eff1fa;">비밀번호</th> |
|
26 |
+ <td><input type="password" class="form-control sm" placeholder="3-16자 영문/숫자 조합으로 입력해주세요" |
|
27 |
+ v-model="password"></td> |
|
28 |
+ </tr> |
|
29 |
+ <tr> |
|
30 |
+ <th style="background-color: #eff1fa;">비밀번호 확인</th> |
|
31 |
+ <td><input type="password" class="form-control sm mb10" |
|
32 |
+ placeholder="3-16자 영문/숫자 조합으로 입력해주세요" v-model="passwordCheck"> |
|
33 |
+ <div class="error-message " v-if="passwordFail"> |
|
34 |
+ <p><img src="../../../resources/img/component/common/ico_invalid_error_20.svg" |
|
35 |
+ alt="">비밀번호가 일치하지 않습니다</p> |
|
36 |
+ </div> |
|
37 |
+ </td> |
|
38 |
+ </tr> |
|
39 |
+ </tbody> |
|
40 |
+ </table> |
|
41 |
+ </div> |
|
42 |
+ <div class="tbl-wrap"> |
|
43 |
+ <table class="tbl data"> |
|
44 |
+ <colgroup> |
|
45 |
+ <col style="width: 180px;"> |
|
46 |
+ <col style="width: auto;"> |
|
47 |
+ </colgroup> |
|
48 |
+ <tbody> |
|
49 |
+ <tr> |
|
50 |
+ <th style="background-color: #eff1fa;">이름</th> |
|
51 |
+ <td><input type="text" class="form-control sm" placeholder="한글 15자, 영문 30자까지 가능합니다." |
|
52 |
+ v-model="memberName"></td> |
|
53 |
+ </tr> |
|
54 |
+ <tr> |
|
55 |
+ <th style="background-color: #eff1fa;">이메일</th> |
|
56 |
+ <td> |
|
57 |
+ <div class="input-group"> |
|
58 |
+ <input type="text" class="form-control sm" placeholder="유효한 이메일 주소를 입력해주세요" |
|
59 |
+ v-model="email"> |
|
60 |
+ <button class="btn sm black" @click="checkEmailProc">중복확인</button> |
|
61 |
+ </div> |
|
62 |
+ </td> |
|
63 |
+ </tr> |
|
64 |
+ </tbody> |
|
65 |
+ </table> |
|
66 |
+ </div> |
|
67 |
+ <div class="tbl-wrap last-table"> |
|
68 |
+ <table class="tbl data"> |
|
69 |
+ <colgroup> |
|
70 |
+ <col style="width: 180px;"> |
|
71 |
+ <col style="width: auto;"> |
|
72 |
+ </colgroup> |
|
73 |
+ <tbody> |
|
74 |
+ <tr> |
|
75 |
+ <th style="background-color: #eff1fa;border-radius: 1rem 0 0 1rem; ">전화번호</th> |
|
76 |
+ <td> |
|
77 |
+ <div class="input-group mb10"> |
|
78 |
+ <input type="text" class="form-control sm" placeholder="-제외 휴대전화 번호를 입력해주세요" |
|
79 |
+ v-model="phoneNumber"> |
|
80 |
+ <button class="btn sm black">인증번호 발송</button> |
|
81 |
+ </div> |
|
82 |
+ <div class="input-group"> |
|
83 |
+ <input type="text" class="form-control sm" placeholder="인증번호 6자리 숫자를 입력해주세요" |
|
84 |
+ style="background-color: #ebebeb;" v-model="vertifiCode"> |
|
85 |
+ <button class="btn sm black" style="min-width: 119px;" |
|
86 |
+ @click="vertifyCode">확인</button> |
|
87 |
+ </div> |
|
88 |
+ </td> |
|
89 |
+ </tr> |
|
90 |
+ </tbody> |
|
91 |
+ </table> |
|
92 |
+ </div> |
|
80 | 93 |
|
81 |
- <div class="layout center justify-center"> |
|
82 |
- <button class="btn lg netx-btn" @click="stepGo(2)">다음</button> |
|
83 |
- </div> |
|
94 |
+ <div class="layout center justify-center"> |
|
95 |
+ <button class="btn lg netx-btn" @click="registProc">다음</button> |
|
96 |
+ </div> |
|
84 | 97 |
|
98 |
+ </div> |
|
85 | 99 |
</div> |
86 |
- </div> |
|
87 | 100 |
</template> |
88 | 101 |
|
89 | 102 |
<script> |
103 |
+import { errorMessages } from 'vue/compiler-sfc'; |
|
104 |
+import { registProc, checkIdProc, checkEmailProc } from '../../../resources/api/auth'; |
|
90 | 105 |
|
91 | 106 |
export default { |
107 |
+ inject: ['$alert'], |
|
92 | 108 |
data() { |
93 | 109 |
return { |
94 | 110 |
loginId: null, |
... | ... | @@ -98,15 +114,180 @@ |
98 | 114 |
email: null, |
99 | 115 |
phoneNumber: null, |
100 | 116 |
vertifiCode: null, |
117 |
+ passwordFail: false, |
|
118 |
+ checkId: false, |
|
119 |
+ checkEmail: false, |
|
120 |
+ errorMessage: "", |
|
101 | 121 |
}; |
102 | 122 |
}, |
103 |
- methods: {}, |
|
104 |
- watch: {}, |
|
123 |
+ methods: { |
|
124 |
+ // 회원가입 |
|
125 |
+ async registProc() { |
|
126 |
+ const vm = this; |
|
127 |
+ vm.passwordFail = false; |
|
128 |
+ vm.errorMessage = ""; |
|
129 |
+ |
|
130 |
+ // 아이디 검증 |
|
131 |
+ const idRegex = /^[a-zA-Z0-9]{3,15}$/; |
|
132 |
+ if (!vm.loginId || !idRegex.test(vm.loginId)) { |
|
133 |
+ vm.$alert({ message: '아이디는 영문+숫자 조합의 3~15자여야 합니다.' }); |
|
134 |
+ return; |
|
135 |
+ } |
|
136 |
+ if (vm.checkId === false) { |
|
137 |
+ vm.$alert({ message: '아이디 중복 확인을 해야합니다.' }); |
|
138 |
+ return; |
|
139 |
+ } |
|
140 |
+ |
|
141 |
+ // 비밀번호 검증 |
|
142 |
+ const pwRegex = /^[a-zA-Z0-9]{3,16}$/; |
|
143 |
+ if (!vm.password || !pwRegex.test(vm.password)) { |
|
144 |
+ vm.$alert({ message: '비밀번호는 영문+숫자 조합의 3~16자여야 합니다.' }); |
|
145 |
+ return; |
|
146 |
+ } |
|
147 |
+ |
|
148 |
+ // 비밀번호 확인 |
|
149 |
+ if (vm.password !== vm.passwordCheck) { |
|
150 |
+ vm.passwordFail = true; |
|
151 |
+ return; |
|
152 |
+ } |
|
153 |
+ |
|
154 |
+ // 이름 검증 |
|
155 |
+ const name = vm.memberName; |
|
156 |
+ if (!name || !(name.length <= 15 || name.length <= 30)) { |
|
157 |
+ vm.$alert({ message: '이름을 입력해주세요.' }); |
|
158 |
+ return; |
|
159 |
+ } |
|
160 |
+ |
|
161 |
+ // 이메일 검증 |
|
162 |
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
|
163 |
+ if (!vm.email || !emailRegex.test(vm.email)) { |
|
164 |
+ vm.$alert({ message: '올바른 이메일 형식을 입력해주세요.' }); |
|
165 |
+ return; |
|
166 |
+ } |
|
167 |
+ if (vm.checkEmail === false) { |
|
168 |
+ vm.$alert({ message: '이메일 중복 확인을 해야합니다.' }); |
|
169 |
+ return; |
|
170 |
+ } |
|
171 |
+ |
|
172 |
+ // 전화번호 검증 |
|
173 |
+ const phoneRegex = /^01[016789][0-9]{7,8}$/; |
|
174 |
+ if (!vm.phoneNumber || !phoneRegex.test(vm.phoneNumber)) { |
|
175 |
+ vm.$alert({ message: '전화번호는 "-" 없이 10~11자리 숫자로 입력해주세요.' }); |
|
176 |
+ return; |
|
177 |
+ } |
|
178 |
+ |
|
179 |
+ // 인증 코드 검증 |
|
180 |
+ const codeRegex = /^[0-9]{6}$/; |
|
181 |
+ if (!vm.vertifiCode || !codeRegex.test(vm.vertifiCode)) { |
|
182 |
+ vm.$alert({ message: '인증번호는 6자리 숫자여야 합니다.' }); |
|
183 |
+ |
|
184 |
+ return; |
|
185 |
+ } |
|
186 |
+ |
|
187 |
+ const loginData = { |
|
188 |
+ loginId: vm.loginId, |
|
189 |
+ password: vm.password, |
|
190 |
+ memberName: vm.memberName, |
|
191 |
+ email: vm.email, |
|
192 |
+ phoneNumber: vm.phoneNumber |
|
193 |
+ } |
|
194 |
+ try { |
|
195 |
+ const res = await registProc(loginData); |
|
196 |
+ if (res.status === 200) { |
|
197 |
+ vm.$alert({ message: '회원가입이 완료되었습니다.' }); |
|
198 |
+ |
|
199 |
+ vm.$router.push({ path: "/login.page" }); |
|
200 |
+ } |
|
201 |
+ |
|
202 |
+ } catch (error) { |
|
203 |
+ vm.errorMessage = '장애가 발생했습니다.\n'; |
|
204 |
+ vm.showAlert(); |
|
205 |
+ } |
|
206 |
+ }, |
|
207 |
+ // 아이디 중복 확인 |
|
208 |
+ async checkIdProc() { |
|
209 |
+ const vm = this; |
|
210 |
+ vm.checkId = false; |
|
211 |
+ // 아이디 검증 |
|
212 |
+ const idRegex = /^[a-zA-Z0-9]{3,15}$/; |
|
213 |
+ if (!vm.loginId || !idRegex.test(vm.loginId)) { |
|
214 |
+ vm.$alert({ message: '아이디는 영문+숫자 조합의 3~15자여야 합니다.' }); |
|
215 |
+ return; |
|
216 |
+ } |
|
217 |
+ |
|
218 |
+ const checkIdData = { |
|
219 |
+ loginId: vm.loginId, |
|
220 |
+ } |
|
221 |
+ try { |
|
222 |
+ const res = await checkIdProc(checkIdData); |
|
223 |
+ if (res.status === 200) { |
|
224 |
+ if (res.data.result > 0) { |
|
225 |
+ vm.$alert({ message: '중복된 아이디입니다.' }); |
|
226 |
+ } else { |
|
227 |
+ vm.checkId = true; |
|
228 |
+ vm.$alert({ message: '사용 가능한 아이디입니다.' }); |
|
229 |
+ } |
|
230 |
+ } |
|
231 |
+ |
|
232 |
+ } catch (error) { |
|
233 |
+ } |
|
234 |
+ }, |
|
235 |
+ |
|
236 |
+ // 이메일 중복 확인 |
|
237 |
+ async checkEmailProc() { |
|
238 |
+ const vm = this; |
|
239 |
+ vm.checkEmail = false; |
|
240 |
+ |
|
241 |
+ // 이메일 검증 |
|
242 |
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
|
243 |
+ if (!vm.email || !emailRegex.test(vm.email)) { |
|
244 |
+ vm.$alert({ message: '올바른 이메일 형식을 입력해주세요.' }); |
|
245 |
+ return; |
|
246 |
+ } |
|
247 |
+ |
|
248 |
+ const checkEmailData = { |
|
249 |
+ email: vm.email, |
|
250 |
+ } |
|
251 |
+ try { |
|
252 |
+ const res = await checkEmailProc(checkEmailData); |
|
253 |
+ if (res.status === 200) { |
|
254 |
+ if (res.data.result > 0) { |
|
255 |
+ vm.$alert({ message: '중복된 이메일입니다.' }); |
|
256 |
+ } else { |
|
257 |
+ vm.checkEmail = true; |
|
258 |
+ vm.$alert({ message: '사용 가능한 이메일입니다.' }); |
|
259 |
+ } |
|
260 |
+ } |
|
261 |
+ |
|
262 |
+ } catch (error) { |
|
263 |
+ } |
|
264 |
+ }, |
|
265 |
+ |
|
266 |
+ // 인증번호 발송 - 비용이 드는 것이기에 생략 |
|
267 |
+ async sendMail(){ |
|
268 |
+ |
|
269 |
+ }, |
|
270 |
+ |
|
271 |
+ // 인증번호 확인 - 발송과 마찬가지로 생략 |
|
272 |
+ async checkCode() { |
|
273 |
+ |
|
274 |
+ }, |
|
275 |
+ |
|
276 |
+ }, |
|
277 |
+ watch: { |
|
278 |
+ // 아이디, 이메일 변경 시 중복 확인 초기화 |
|
279 |
+ loginId(newVal, oldVal) { |
|
280 |
+ this.checkId = false; |
|
281 |
+ }, |
|
282 |
+ email(newVal, oldVal) { |
|
283 |
+ this.checkEmail = false; |
|
284 |
+ }, |
|
285 |
+ }, |
|
105 | 286 |
computed: {}, |
106 | 287 |
components: {}, |
107 |
- created() {}, |
|
108 |
- mounted() {}, |
|
109 |
- beforeUnmount() {}, |
|
288 |
+ created() { }, |
|
289 |
+ mounted() { }, |
|
290 |
+ beforeUnmount() { }, |
|
110 | 291 |
}; |
111 | 292 |
|
112 | 293 |
</script>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/common/Login.vue
+++ client/views/pages/common/Login.vue
... | ... | @@ -16,10 +16,7 @@ |
16 | 16 |
</div> |
17 | 17 |
</div> |
18 | 18 |
<div class="error-message mb20" v-if="!isValid"> |
19 |
- <p><img src="../../../resources/img/component/common/ico_invalid_error_20.svg" alt=""> 아이디 또는 비밀번호가 |
|
20 |
- 잘못되었습니다.</p> |
|
21 |
- <p><img src="../../../resources/img/component/common/ico_invalid_error_20.svg" alt=""> 아이디와 비밀번호를 |
|
22 |
- 정확히 입력해주세요.</p> |
|
19 |
+ <p><img src="../../../resources/img/component/common/ico_invalid_error_20.svg" alt=""> {{ errorMessage }}</p> |
|
23 | 20 |
</div> |
24 | 21 |
<button class="btn lg primary login-btn mb30" @click="loginProc"><span |
25 | 22 |
class="icon-label lbtn">로그인</span></button> |
... | ... | @@ -27,7 +24,7 @@ |
27 | 24 |
<div class="layout center justify-center links"> |
28 | 25 |
<router-link :to="{ path: '/find.page', query: { type: 'id' } }">아이디 찾기</router-link> |
29 | 26 |
<router-link :to="{ path: '/find.page', query: { type: 'pw' } }">비밀번호 찾기</router-link> |
30 |
- <router-link to="/join.page">회원가입 찾기</router-link> |
|
27 |
+ <router-link to="/join.page">회원가입</router-link> |
|
31 | 28 |
</div> |
32 | 29 |
</div> |
33 | 30 |
</div> |
... | ... | @@ -42,11 +39,15 @@ |
42 | 39 |
isValid: true, |
43 | 40 |
loginId: null, |
44 | 41 |
password: null, |
42 |
+ errorMessage: "", |
|
45 | 43 |
}; |
46 | 44 |
}, |
47 | 45 |
methods: { |
46 |
+ // 로그인 |
|
48 | 47 |
async loginProc() { |
49 | 48 |
const vm = this; |
49 |
+ vm.isValid = true; |
|
50 |
+ vm.errorMessage = ""; |
|
50 | 51 |
// 사용자 아이디와 비밀번호 입력값 검증 |
51 | 52 |
if (!vm.loginId || vm.loginId.trim() === '') { |
52 | 53 |
return; |
... | ... | @@ -56,8 +57,8 @@ |
56 | 57 |
} |
57 | 58 |
|
58 | 59 |
const loginData = { |
59 |
- loginId : vm.loginId, |
|
60 |
- password : vm.password |
|
60 |
+ loginId: vm.loginId, |
|
61 |
+ password: vm.password |
|
61 | 62 |
} |
62 | 63 |
|
63 | 64 |
try { |
... | ... | @@ -79,6 +80,9 @@ |
79 | 80 |
this.$router.push({ path: "/" }); |
80 | 81 |
} |
81 | 82 |
} catch (error) { |
83 |
+ vm.isValid = false; |
|
84 |
+ this.errorMessage = error.response?.data?.message || "로그인 중 오류가 발생했습니다."; |
|
85 |
+ |
|
82 | 86 |
} |
83 | 87 |
}, |
84 | 88 |
}, |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?