Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

회원가입 유효성 검사 #96

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions public/css/auth.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
:root {
--font-size-large: 20px;
--font-size-default: 16px;
--font-size-medium: 14px;
--font-size-small: 12px;
--font-size-x-small: 9px;
/* form */
--input-width: 240px;
--input-height: 40px;
Expand Down Expand Up @@ -66,10 +71,20 @@
.sign-up-form__input {
width: var(--input-width);
height: var(--input-height);
margin: 5% 0;
}

.sign-up-form__submit {
width: var(--submit-width);
height: var(--submit-height);
margin: var(--submit-margin);
}

.sign-up-form__email,
.sign-up-form__pwd,
.sign-up-form__pwd2,
.sign-up-form__name {
min-height: 10px;
font-size: var(--font-size-x-small);
color: red;
}
132 changes: 132 additions & 0 deletions public/js/sign-up.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
const form = document.querySelector('.sign-up-form');
const emailInput = document.querySelector('.sign-up-form__input[name=email]');
const pwdInput = document.querySelector('.sign-up-form__input[name=pwd]');
const pwd2Input = document.querySelector('.sign-up-form__input[name=pwd2]');
const nameInput = document.querySelector('.sign-up-form__input[name=nickname]');
const submitBtn = document.querySelector('.sign-up-form__submit');

const emailValidDiv = document.querySelector('.sign-up-form__email');
const pwdValidDiv = document.querySelector('.sign-up-form__pwd');
const pwd2ValidDiv = document.querySelector('.sign-up-form__pwd2');
const nameValidDiv = document.querySelector('.sign-up-form__name');

function showIsValid(element, text) {
element.innerText = text;
}

async function checkDuplicateEmail() {
const userInput = emailInput.value;
const res = await fetch(`http://localhost:3000/auth/check-duplicate-email`, {
method: 'POST',
body: userInput
});
const isValid = await res.json();
if (!isValid) {
showIsValid(emailValidDiv, '이미 사용 중인 이메일입니다.');
}
return isValid;
}

async function checkDuplicateName() {
const userInput = nameInput.value;
const res = await fetch(
`http://localhost:3000/auth/check-duplicate-nickname`,
{
method: 'POST',
body: userInput
}
);
const isValid = await res.json();
if (!isValid) {
showIsValid(nameValidDiv, '이미 사용 중인 닉네임입니다.');
}
return isValid;
}
Comment on lines +17 to +44
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isValidtrue/false가 아닌 Promise { <pending> }인 상태입니다.

Copy link
Collaborator

@42KIM 42KIM Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async가 붙은 함수는 무조건 프로미스를 반환합니다. return 으로 프로미스가 아닌 일반값을 반환하더라도 해당 값은 프로미스로 감싸집니다.
상우님이 위에서 return isValid를 하고계시기 때문에 그 값이 true던지 false던지 모두 프로미스로 담겨서 return 됩니다.
따라서 아래 checkValidAll 함수 내부에서 이 함수에 기대하는 값인 true나 false를 반환하기 위해서는 .then을 통해 settled를 시켜야 할 것 같습니다.

그게 아니라면 애초에 return 값에 await fetch(~~~)를 할당하면 서버에서 넘겨주는 값이 바로 settled 되어서 return 될 거예요. 그게 await의 특징이니까.

자바스크립트는 await 키워드를 만나면 프라미스가 처리(settled)될 때까지 기다립니다. 결과는 그 이후 반환됩니다.

하지만 위 함수의 경우는 isValid를 통해 showIsValid를 실행시켜야 하니 바로 return await fetch(~~~)를 하게 되면 저 부분을 따로 빼서 실행하거나, 아니면 제가 위에 말한대로 checkValidAll 함수에서 resolve 하여 쓰시면 되지 않을까 싶습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

설명 감사합니다. async가 무조건 프로미스를 반환하는지 이번에 알게 되었네요. 함수를 따로 빼는 것보다는 checkValidAll 함수에서 resolve 하는 것이 맞다고 판단해서 해당 부분 수정했습니다!

그리고 지금 에러 처리가 하나도 안 되어 있는데, 에러 처리랑 프로미스로 리팩토링은 주말에 진행하겠습니다~


function checkValidEmail() {
const regExp =
/^[\w!#$%&'*+/=?^_{|}~-]+(?:\.[\w!#$%&'*+/=?^_{|}~-]+)*@(?:\w+\.)+\w+$/;

if (emailInput.value === '' || regExp.test(emailInput.value)) {
showIsValid(emailValidDiv, '');
return true;
} else {
showIsValid(emailValidDiv, '올바른 이메일 주소를 입력해 주세요.');
return false;
}
}

function checkStrongPwd() {
const regExp =
/^(?=.*\d)(?=.*[a-zA-Z])(?=.*[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]).{8,16}$/;

if (pwdInput.value === '' || regExp.test(pwdInput.value)) {
showIsValid(pwdValidDiv, '');
return true;
} else {
showIsValid(
pwdValidDiv,
'8~16자 영문 대 소문자, 숫자, 특수문자를 사용해 주세요.'
);
return false;
}
}

function checkEqualPwd() {
if (pwd2Input.value !== '' && pwdInput.value === pwd2Input.value) {
showIsValid(pwd2ValidDiv, '');
return true;
} else {
showIsValid(pwd2ValidDiv, '비밀번호가 일치하지 않습니다.');
return false;
}
}

function checkValidName() {
if (nameInput.value === '' || nameInput.value.length <= 10) {
showIsValid(nameValidDiv, '');
return true;
} else {
showIsValid(nameValidDiv, '10자 이하를 입력해 주세요.');
return false;
}
}

async function checkValidAll() {
const inputs = [emailInput, pwdInput, pwd2Input, nameInput];
const divs = [emailValidDiv, pwdValidDiv, pwd2ValidDiv, nameValidDiv];

inputs.forEach((Item, i) => {
if (!Item.value) {
showIsValid(divs[i], '필수 정보입니다.');
}
});

const isDuplicateEmail = await checkDuplicateEmail();
const isDuplicateName = await checkDuplicateName();

if (
checkValidEmail() &&
checkStrongPwd() &&
checkEqualPwd() &&
isDuplicateEmail &&
isDuplicateName
) {
form.submit();
} else {
alert('입력하신 정보를 확인하세요.');
}
}

function init() {
emailInput.addEventListener('keyup', checkValidEmail);
emailInput.addEventListener('focusout', checkDuplicateEmail);
pwdInput.addEventListener('keyup', checkStrongPwd);
pwdInput.addEventListener('keyup', checkEqualPwd);
pwd2Input.addEventListener('keyup', checkEqualPwd);
nameInput.addEventListener('keyup', checkValidName);
nameInput.addEventListener('focusout', checkDuplicateName);
submitBtn.addEventListener('click', checkValidAll);
}

init();
124 changes: 104 additions & 20 deletions routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,31 +190,115 @@ router.get('/sign-up', (req, res, next) => {
res.render('sign-up');
});

// 회원가입 유효성 검증
function checkValidEmail(userInput) {
const regExp =
/^[\w!#$%&'*+/=?^_{|}~-]+(?:\.[\w!#$%&'*+/=?^_{|}~-]+)*@(?:\w+\.)+\w+$/;

return regExp.test(userInput) ? true : false;
}

function checkStrongPwd(userInput) {
const regExp =
/^(?=.*\d)(?=.*[a-zA-Z])(?=.*[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]).{8,16}$/;

return regExp.test(userInput) ? true : false;
}

function checkEqualPwd(userInputA, userInputB) {
return userInputA === userInputB ? true : false;
}

function checkValidName(userInput) {
return userInput.length <= 10 ? true : false;
}

function checkDuplicateEmail(userInput) {
const sql_user = 'SELECT * FROM user WHERE email=?';
db.query(sql_user, [userInput], (err, results) => {
if (err) {
next(err);
} else if (results[0]) {
return false;
} else {
return true;
}
});
}

function checkDuplicateName(userInput) {
const sql_user = 'SELECT * FROM user WHERE nickname=?';
db.query(sql_user, [userInput], (err, results) => {
if (err) {
next(err);
} else if (results[0]) {
return false;
} else {
return true;
}
});
}

router.post('/check-duplicate-email', (req, res, next) => {
const email = req.body;
const sql_user = 'SELECT * FROM user WHERE email=?';
db.query(sql_user, [email], (err, results) => {
if (err) {
next(err);
} else if (results[0]) {
res.send(false);
} else {
res.send(true);
}
});
});

router.post('/check-duplicate-nickname', (req, res, next) => {
const nickname = req.body;
const sql_user = 'SELECT * FROM user WHERE nickname=?';
db.query(sql_user, [nickname], (err, results) => {
if (err) {
next(err);
} else if (results[0]) {
res.send(false);
} else {
res.send(true);
}
});
});

// Sign-up_process Route
// POST request 암호화 필요
router.post('/sign-up_process', (req, res, next) => {
const post = req.body;
const id = nanoid();
const { email } = post;
const { pwd } = post;
const { pwd2 } = post;
const { nickname } = post;
if (pwd !== pwd2) {
console.log('비밀번호가 일치하지 않습니다');
} else {
const salt = randomBytes(64).toString('base64');
const secret = pbkdf2Sync(pwd, salt, 100000, 64, 'sha512').toString(
'base64'
);
const sql =
'INSERT INTO user (id, email, secret, salt, nickname, create_date) VALUES (?, ?, ?, ?, ?, NOW())';
db.query(sql, [id, email, secret, salt, nickname], err => {
if (err) {
next(err);
}
res.redirect('/auth/sign-in');
});
const { email, pwd, pwd2, nickname } = req.body;

if (
!checkValidEmail(email) ||
!checkEqualPwd(pwd, pwd2) ||
!checkStrongPwd(pwd) ||
!checkValidName(nickname)
) {
console.log('Invalid User Input');
return;
}

if (!checkDuplicateEmail(email) || !checkDuplicateName(nickname)) {
console.log('Duplicate User Input');
return;
}

const salt = randomBytes(64).toString('base64');
const secret = pbkdf2Sync(pwd, salt, 100000, 64, 'sha512').toString('base64');
const sql =
'INSERT INTO user (id, email, secret, salt, nickname, create_date) VALUES (?, ?, ?, ?, ?, NOW())';

db.query(sql, [id, email, secret, salt, nickname], err => {
if (err) {
next(err);
}
res.redirect('/auth/sign-in');
});
});

// Sign-in Route
Expand Down
64 changes: 33 additions & 31 deletions views/includes/auth/sign-up-container.ejs
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
<form action="/auth/sign-up_process" method="post" class="sign-up-form">
<input
class="sign-up-form__input"
type="text"
name="email"
placeholder="이메일"
/>
<input
class="sign-up-form__input"
type="password"
name="pwd"
placeholder="비밀번호"
/>
<input
class="sign-up-form__input"
type="password"
name="pwd2"
placeholder="비밀번호 확인"
/>
<input
class="sign-up-form__input"
type="text"
name="nickname"
placeholder="닉네임"
/>
<input
class="sign-up-form__submit"
type="submit"
value="이메일 회원가입"
/>
</form>
<form action="/auth/sign-up_process" method="post" class="sign-up-form">
<input
class="sign-up-form__input"
type="text"
name="email"
placeholder="이메일"
/>
<div class="sign-up-form__email"></div>
<input
class="sign-up-form__input"
type="password"
name="pwd"
placeholder="비밀번호"
autocomplete="off"
/>
<div class="sign-up-form__pwd"></div>
<input
class="sign-up-form__input"
type="password"
name="pwd2"
placeholder="비밀번호 확인"
autocomplete="off"
/>
<div class="sign-up-form__pwd2"></div>
<input
class="sign-up-form__input"
type="text"
name="nickname"
placeholder="닉네임"
/>
<div class="sign-up-form__name"></div>
<button class="sign-up-form__submit" type="button">이메일 회원가입</button>
</form>
Loading