중복 검사
- 중복 검사는 서버(java)에서도 진행해야하고, 클라이언트(js)에서도 진행해야한다.
MemberService
- 아이디, 이메일은 중복검사 하는 중간 로직을 추가
public boolean checkIdentifier(String type, String keyword) {
return memberMapper.existsById(type, keyword);
}
MemberController
- Controller를 통해 아이디, 이메일 비동기 요청 처리
@GetMapping("/check")
@ResponseBody
public ResponseEntity<?> check(String type, String keyword) {
boolean flag = memberService.checkIdentifier(type, keyword);
log.debug("{} 중복체크 결과? {}", type, flag);
return ResponseEntity
.ok()
.body(flag);
}
validation.js (내가 한 것)
아이디, 비밀번호, 이름, 이메일 중복검사 클라이언트에서 진행
function button() {
const $successInput = document.querySelectorAll('.success');
const $button = document.getElementById('signup-btn');
$button.disabled = [...$successInput].length !== 5;
}
async function fetchDuplicateCheck(idValue) {
const res = await fetch(`http://localhost:8383/members/check?type=account&keyword=${idValue}`);
const flag = await res.json();
idFlag = flag;
}
const $idInput = document.getElementById('user_id');
let idFlag = false;
$idInput.addEventListener('keyup', async (e) => {
const accountPattern = /^[a-zA-Z0-9]{4,14}$/;
const idValue = $idInput.value;
const $idChk = document.getElementById('idChk');
if (idValue.trim() === '') {
$idInput.style.borderColor = 'red';
$idChk.innerHTML = '<b class="warning">[아이디는 필수값입니다.]</b>';
} else if (!accountPattern.test(idValue)) {
$idInput.style.borderColor = 'red';
$idChk.innerHTML = '<b class="warning">[아이디는 4~14글자 사이 영문,숫자로 입력하세요]</b>';
} else {
await fetchDuplicateCheck(idValue);
if (idFlag) {
$idInput.style.borderColor = 'red';
$idChk.innerHTML = '<b class="warning">[아이디가 중복되었습니다. 다른 아이디를 사용하세요!]</b>';
} else {
$idInput.style.borderColor = 'skyblue';
$idChk.innerHTML = '<b class="success">[사용가능한 아이디입니다.]</b>';
idValid = true;
}
}
button();
});
const $pwInput = document.getElementById('password');
$pwInput.addEventListener('keyup', async (e) => {
const passwordPattern = /([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/;
const pwValue = $pwInput.value;
const $pwChk = document.getElementById('pwChk');
if (pwValue.trim() === '' ) {
$pwInput.style.borderColor = 'red';
$pwChk.innerHTML = '<b class="warning">[비밀번호는 필수값입니다.]</b>';
} else if (!passwordPattern.test(pwValue)) {
$pwInput.style.borderColor = 'red';
$pwChk.innerHTML = '<b class="warning">[비밀번호는 8글자 이상, 영문,숫자,특수문자가 포함되게 입력하세요]</b>';
} else {
$pwInput.style.borderColor = 'skyblue';
$pwChk.innerHTML = '<b class="success">[사용가능한 비밀번호입니다.]</b>';
passwordValid = true;
}
button();
})
const $pwCheckInput = document.getElementById('password_check');
$pwCheckInput.addEventListener('keyup', async (e) => {
const passwordPattern = /([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/;
const pwValue = $pwInput.value;
const pwCheckValue = $pwCheckInput.value;
const $pwChk2 = document.getElementById('pwChk2');
if (pwValue !== pwCheckValue) {
$pwInput.style.borderColor = 'red';
$pwChk2.innerHTML = '<b class="warning">[비밀번호가 일치하지 않습니다.]</b>';
} else {
$pwInput.style.borderColor = 'skyblue';
$pwChk2.innerHTML = '<b class="success">[비밀번호가 일치합니다.]</b>';
passwordMatchValid = true;
}
button();
})
const $nameInput = document.getElementById('user_name');
$nameInput.addEventListener('keyup', async (e) => {
const namePattern = /^[가-힣]+$/;
const nameValue = $nameInput.value;
const $nameChk = document.getElementById('nameChk');
if (nameValue.trim() === '' ) {
$nameInput.style.borderColor = 'red';
$nameChk.innerHTML = '<b class="warning">[이름은 필수값입니다.]</b>';
} else if (!namePattern.test(nameValue)) {
$nameInput.style.borderColor = 'red';
$nameChk.innerHTML = '<b class="warning">[이름은 한글이여야 합니다.]</b>';
} else {
$nameInput.style.borderColor = 'skyblue';
$nameChk.innerHTML = '<b class="success">[사용가능한 이름입니다.]</b>';
nameValid = true;
}
button();
});
async function fetchDuplicateCheckEmail(emailValue) {
const res = await fetch(`http://localhost:8383/members/check?type=email&keyword=${emailValue}`);
const flag = await res.json();
emailFlag = flag;
}
const $emailInput = document.getElementById('user_email');
let emailFlag = false;
$emailInput.addEventListener('keyup', async (e) => {
const emailPattern = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/;
const emailValue = $emailInput.value;
const $emailChk = document.getElementById('emailChk');
if (emailValue.trim() === '') {
$emailInput.style.borderColor = 'red';
$emailChk.innerHTML = '<b class="warning">[이메일은 필수값입니다.]</b>';
} else if (!emailPattern.test(emailValue)) {
$emailInput.style.borderColor = 'red';
$emailChk.innerHTML = '<b class="warning">[이메일은 내용@내용.내용의 형태여야 합니다. ]</b>';
} else {
await fetchDuplicateCheckEmail(emailValue);
if (emailFlag) {
$emailInput.style.borderColor = 'red';
$emailChk.innerHTML = '<b class="warning">[이메일이 중복되었습니다. 다른 이메일을 사용하세요!]</b>';
} else {
$emailInput.style.borderColor = 'skyblue';
$emailChk.innerHTML = '<b class="success">[사용가능한 이메일입니다.]</b>';
emailValid = true;
}
}
button();
});
선생님 코드
validate.js
export const checkAvailability = async (type, keyword) => {
const response = await fetch(`http://localhost:8383/members/check?type=${type}&keyword=${keyword}`);
const flag = await response.json();
return !flag;
};
const accountPattern = /^[a-zA-Z0-9]{4,14}$/;
const passwordPattern = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*?_~])[A-Za-z\d!@#$%^&*?_~]{8,}$/;
const namePattern = /^[가-힣]+$/;
const emailPattern = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/;
export const validateInput = {
account: async (value) => {
if (!value.trim()) return { valid: false, message: '아이디는 필수값입니다!' };
if (!accountPattern.test(value)) return { valid: false, message: '아이디는 4~14글자의 영문,숫자로 입력하세요.' };
const isAvailable = await checkAvailability('account', value);
return isAvailable ? { valid: true } : { valid: false, message: '아이디가 중복되었습니다.' };
},
password: (value) => {
if (!value.trim()) return { valid: false, message: '비밀번호는 필수값입니다!' };
if (!passwordPattern.test(value)) return { valid: false, message: '비밀번호는 영문, 숫자, 특수문자를 포함하여 8자 이상이어야 합니다!' };
return { valid: true };
},
passwordCheck: (value, password) => {
if (!value.trim()) return { valid: false, message: '비밀번호 확인란은 필수값입니다!' };
if (value !== password) return { valid: false, message: '비밀번호가 일치하지 않습니다!' };
return { valid: true };
},
name: (value) => {
if (!value.trim()) return { valid: false, message: '이름은 필수정보입니다!' };
if (!namePattern.test(value)) return { valid: false, message: '이름은 한글로 입력해주세요.' };
return { valid: true };
},
email: async (value) => {
if (!value.trim()) return { valid: false, message: '이메일은 필수값입니다!' };
if (!emailPattern.test(value)) return { valid: false, message: '이메일 형식을 지켜주세요.' };
const isAvailable = await checkAvailability('email', value);
return isAvailable ? { valid: true } : { valid: false, message: '이메일이 중복되었습니다.' };
}
};
signUp.js
import { validateInput } from './validate.js';
import { debounce } from './util.js';
const form = document.getElementById('signUpForm');
const signupButton = document.getElementById('signup-btn');
const fields = [
{ id: 'user_id', validator: validateInput.account, errorElement: 'idChk', valid: false },
{ id: 'password', validator: validateInput.password, errorElement: 'pwChk', valid: false },
{ id: 'password_check', validator: (value) => validateInput.passwordCheck(value, document.getElementById('password').value), errorElement: 'pwChk2', valid: false },
{ id: 'user_name', validator: validateInput.name, errorElement: 'nameChk', valid: false },
{ id: 'user_email', validator: validateInput.email, errorElement: 'emailChk', valid: false }
];
const updateButtonState = () => {
const isFormValid = fields.every(field => field.valid);
if (isFormValid) {
signupButton.disabled = false;
signupButton.style.backgroundColor = 'orangered';
} else {
signupButton.disabled = true;
signupButton.style.backgroundColor = 'lightgray';
}
};
fields.forEach(field => {
const $input = document.getElementById(field.id);
$input.addEventListener('keyup', debounce(async (e) => {
const isValid = await field.validator($input.value);
const $errorSpan = document.getElementById(field.errorElement);
console.log(isValid);
if (isValid.valid) {
$input.style.borderColor = 'skyblue';
$errorSpan.innerHTML = '<b class="success">[사용가능합니다.]</b>';
field.valid = true;
} else {
$input.style.borderColor = 'red';
$errorSpan.innerHTML = `<b class="warning">[${isValid.message}]</b>`;
field.valid = false;
}
if (field.id === 'password' && document.getElementById('password_check').value.trim()) {
checkPassword([...fields][2]);
}
updateButtonState();
}, 500));
});
signupButton.addEventListener('click', (e) => {
const isFormValid = fields.every(result => result.valid);
if (isFormValid) {
form.submit();
} else {
alert('입력란을 다시 확인하세요!');
}
});
function checkPassword (field){
const $input = document.getElementById(field.id);
const $a = document.getElementById('password')
$a.addEventListener('keyup', debounce(async (e) => {
const isValid = await field.validator($input.value);
const $errorSpan = document.getElementById(field.errorElement);
console.log(isValid);
if (isValid.valid) {
$input.style.borderColor = 'skyblue';
$errorSpan.innerHTML = '<b class="success">[사용가능합니다.]</b>';
field.valid = true;
} else {
$input.style.borderColor = 'red';
$errorSpan.innerHTML = `<b class="warning">[${isValid.message}]</b>`;
field.valid = false;
}
updateButtonState();
}, 500));
}
updateButtonState();
util.js (디바운싱)
export function debounce(callback, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => callback(...args), wait);
};
}