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

[Week4] 권부근 #133

Merged
14 changes: 14 additions & 0 deletions .github/workflows/delete-merged-branch-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: delete branch on close pr

on:
pull_request:
types: [closed]

jobs:
delete-branch:
runs-on: ubuntu-latest
steps:
- name: delete branch
uses: SvanBoxel/delete-merged-branch@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
94 changes: 94 additions & 0 deletions auth/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
displayError,
resetError,
validateInput,
validationState,
} from "./utill/validation.js";
import { ERROR_MESSAGE } from "./utill/constant.js";
import { user } from "./utill/db.js";

const form = document.querySelector(".sign-form");
const inputs = form.querySelectorAll("input");
Copy link
Collaborator

Choose a reason for hiding this comment

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

input 태그로만 요소를 선택하면 추후에 다른 input 요소가 문서에 추가되었을 때 의도하지 않은 동작이 발생할 가능성이 있어 보입니다.
조금 더 구체적인 셀렉터를 사용하는건 어떨까요?


const submit = document.querySelector(".cta");
soonoo27 marked this conversation as resolved.
Show resolved Hide resolved
const authType = submit.dataset.auth;

const emailInput = document.querySelector('.sign-input[type="email"]');
const passwordInput = document.querySelector('.sign-input[type="password"]');
const confirmPasswordInput = document.querySelector(".confirm-password");

const focusHandler = (e) => {
if (e.target.tagName === "INPUT") {
const errorMessage = validateInput(e.target);
displayError(e.target, errorMessage);
}
};
Comment on lines +20 to +25
Copy link
Collaborator

Choose a reason for hiding this comment

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

저는 mousedown, focusout, mouseup, click 이벤트의 처리 순서를 이용한 방법을 생각해봤습니다. (링크)

focusout 이벤트를 핸들러를 폼에 추가하고, 타겟이 input일 때만 처리하도록 구현하셨군요 👍
포커스를 받을 수 없는 form 요소에 focusout 핸들러가 추가되어 있다는게 조금은 어색해 보이지만 제가 생각한 방법보다 훨씬 간결하다는 장점이 있네요!


const submitHandler = (e) => {
e.preventDefault();
resetError();
let isValid = true;
let account = {};

inputs.forEach((input) => {
const { type, value, name } = input;
if (validationState[name] === undefined) {
const errorMessage = validateInput(input);
if (errorMessage) {
displayError(input, errorMessage);
isValid = false;
}
} else if (!validationState[name]) {
displayError(
input,
ERROR_MESSAGE[authType]["INVALID_" + type.toUpperCase()]
);
isValid = false;
}
account[name] = value;
});

let isSubmit = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

(지극히 개인적인 의견) shouldSubmit이 의미를 더 명확하게 나타내는 이름 같아요.

boolean 변수 네이밍 관련해서 읽어보면 좋은 글:


if (authType === "signin" && isValid) {
if (account.email !== user.email) {
displayError(emailInput, ERROR_MESSAGE[authType]["INVALID_EMAIL"]);
isValid = false;
} else if (account.password !== user.password) {
console.log(account, user);
displayError(passwordInput, ERROR_MESSAGE[authType]["INVALID_PASSWORD"]);
isValid = false;
} else {
isSubmit = true;
}
}

if (authType === "signup" && isValid) {
if (account.email === user.email) {
displayError(emailInput, ERROR_MESSAGE[authType]["EXIST_EMAIL"]);
isValid = false;
} else if (passwordInput.value !== confirmPasswordInput.value) {
displayError(
confirmPasswordInput,
ERROR_MESSAGE[authType]["PASSWORD_EQUAL"]
);
isValid = false;
}
if (isValid) {
isSubmit = true;
}
}
if (isSubmit) {
form.submit();
}
};

const keyupHandler = (e) => {
if (e.key === "Enter") {
submit.click();
}
Comment on lines +87 to +89
Copy link
Collaborator

@soonoo27 soonoo27 Nov 11, 2023

Choose a reason for hiding this comment

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

사용성을 고려한 구현 좋네요 💯
다만 확인해보니 form 내에 type="submit"인 버튼이 있을 때 input 요소에서 엔터키를 누르면 브라우저가 알아서 폼을 전송해주긴 하는데, 관련하여 표준 문서 등을 찾기가 어렵네요.

그리고 keyupHandler가 document에 추가되어 있어서 아무데서나 엔터 키를 눌러도 폼이 전송되네요. 이부분은 개선될 여지가 있어 보입니다! (의도하신게 아니라면)

};

form.addEventListener("focusout", focusHandler);
Copy link
Collaborator

Choose a reason for hiding this comment

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

focusoutHandler로 조금 더 명확하게 이름을 지어보면 어떨까요?
(지금 상황에서는 focusout 이벤트만 처리하고 있으니 크게 이해하는데 어려움이 없지만 엄밀히 말하면 focus, focusout 이벤트는 별도의 이벤트이니깐요)

form.addEventListener("submit", submitHandler);
document.addEventListener("keyup", keyupHandler);
22 changes: 14 additions & 8 deletions auth/signin.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,25 @@
</p>
</header>
<div class="sign-box">
<form class="sign-form">
<form action="/folder.html" class="sign-form" novalidate>
Copy link
Collaborator

Choose a reason for hiding this comment

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

자체 유효성 검사를 사용하고 있으니 novalidate 속성 사용 좋네요!

<div class="sign-inputs">
<div class="sign-input-box">
<label class="sign-input-label">이메일</label>
<input class="sign-input" />
<input class="sign-input" type="email" name="email" />
</div>
<div class="sign-input-box sign-password">
<div class="error-message"></div>
<div class="sign-input-box">
<label class="sign-input-label">비밀번호</label>
<input class="sign-input" type="password" />
<button class="eye-button" type="button">
<img src="../images/eye-off.svg" />
</button>
<div class="sign-password">
<input class="sign-input" type="password" name="password" />
<button class="eye-button" type="button">
<img src="../images/eye-off.svg" />
</button>
</div>
<div class="error-message"></div>
</div>
</div>
<button class="cta" type="submit">로그인</button>
<button class="cta" type="submit" data-auth="signin">로그인</button>
</form>
<div class="sns-box">
<span class="sns-text">소셜 로그인</span>
Expand All @@ -54,5 +58,7 @@
</div>
</div>
</div>
<script type="module" src="main.js"></script>
<script src="./utill/toggle.js"></script>
</body>
</html>
41 changes: 28 additions & 13 deletions auth/signup.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,41 @@
</p>
</header>
<div class="sign-box">
<form class="sign-form">
<form action="/folder.html" class="sign-form" novalidate>
<div class="sign-inputs">
<div class="sign-input-box">
<label class="sign-input-label">이메일</label>
<input class="sign-input" />
<div class="sign-email">
<input class="sign-input" type="email" name="email" />
</div>
<div class="error-message"></div>
</div>
<div class="sign-input-box sign-password">
<div class="sign-input-box">
<label class="sign-input-label">비밀번호</label>
<input class="sign-input" type="password" />
<button class="eye-button" type="button">
<img src="../images/eye-off.svg" />
</button>
<div class="sign-password">
<input class="sign-input" type="password" name="password" />
<button class="eye-button" type="button">
<img src="../images/eye-off.svg" />
</button>
</div>
<div class="error-message"></div>
</div>
<div class="sign-input-box sign-password">
<div class="sign-input-box">
<label class="sign-input-label">비밀번호 확인</label>
<input class="sign-input" type="password" />
<button class="eye-button" type="button">
<img src="../images/eye-off.svg" />
</button>
<div class="sign-password">
<input
class="sign-input confirm-password"
type="password"
name="confirm-password"
/>
<button class="eye-button" type="button">
<img src="../images/eye-off.svg" />
</button>
</div>
<div class="error-message"></div>
</div>
</div>
<button class="cta" type="submit">회원가입</button>
<button class="cta" type="submit" data-auth="signup">회원가입</button>
</form>
<div class="sns-box">
<span class="sns-text">다른 방식으로 가입하기</span>
Expand All @@ -61,5 +74,7 @@
</div>
</div>
</div>
<script type="module" src="main.js"></script>
<script src="./utill/toggle.js"></script>
</body>
</html>
9 changes: 8 additions & 1 deletion auth/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ header {
.sign-input {
padding: 1.8rem 1.5rem;
border-radius: 0.8rem;
width: 100%;
border: 0.1rem solid var(--gray-3);
font-size: 1.6rem;
line-height: 150%;
Expand All @@ -89,9 +90,15 @@ header {
border-color: var(--primary);
}

.error-message {
font-size: 1.4rem;
color: var(--Red);
}

.eye-button {
position: absolute;
bottom: 2.2rem;
top: 50%;
transform: translateY(-50%);
right: 1.5rem;
width: 1.6rem;
height: 1.6rem;
Expand Down
16 changes: 16 additions & 0 deletions auth/utill/constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const ERROR_MESSAGE = {
signin: {
EMPTY_EMAIL: "이메일을 입력해주세요.",
EMPTY_PASSWORD: "비밀번호를 입력해주세요.",
INVALID_EMAIL: "이메일을 확인해주세요",
INVALID_PASSWORD: "비밀번호를 확인해주세요.",
},
signup: {
EMPTY_EMAIL: "이메일을 입력해주세요.",
EMPTY_PASSWORD: "비밀번호를 입력해주세요.",
INVALID_EMAIL: "올바른 이메일 주소가 아닙니다.",
INVALID_PASSWORD: "비밀번호는 영문, 숫자 조합 8자 이상 입력해 주세요.",
EXIST_EMAIL: "이미 존재하는 이메일입니다.",
PASSWORD_EQUAL: "비밀번호가 일치하지 않아요.",
},
};
4 changes: 4 additions & 0 deletions auth/utill/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const user = {
email: "[email protected]",
password: "codeit101",
};
14 changes: 14 additions & 0 deletions auth/utill/toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
document.querySelectorAll(".eye-button").forEach((button) => {
button.addEventListener("click", function () {
const input = this.previousElementSibling;
const img = this.querySelector("img");

if (input.type === "password") {
input.type = "text";
img.src = "../images/eye-on.svg";
} else {
input.type = "password";
img.src = "../images/eye-off.svg";
Comment on lines +6 to +11
Copy link
Collaborator

@soonoo27 soonoo27 Nov 11, 2023

Choose a reason for hiding this comment

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

요구사항 잘 구현해주셨습니다 👍

다만... type=password인 input 요소에는 영어 알파벳만 입력할 수 있습니다.
패스워드를 보여주기 위해 type=text로 변경하면 일시적으로 영어 이외의 문자도 입력할 수 있게 됩니다.
한글 문자열은 정의하신 비밀번호 패턴에 맞지 않기도 하고, 일반적으로도 영어 문자열만 패스워드로 사용하니 개선할 여지가 있어 보입니다!

}
});
});
50 changes: 50 additions & 0 deletions auth/utill/validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// validation.js
import { ERROR_MESSAGE } from "./constant.js";
import { user } from "./db.js";

const submit = document.querySelector(".cta");
const authType = submit.dataset.auth;

export const validationState = {};

const PATTERN = {
email:
/^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i,
password: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
"confirm-password": /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
};

const getErrorMessage = (name, condition) => {
const errorType = condition ? "EMPTY_" : "INVALID_";
return ERROR_MESSAGE[authType][errorType + name.toUpperCase()];
};

export function validateInput(input) {
const { name, value } = input;
let errorMessage = "";

if (value === "") {
errorMessage = getErrorMessage(name, true);
} else if (!PATTERN[name].test(value)) {
errorMessage = getErrorMessage(name, false);
} else if (
authType === "signup" &&
name === "email" &&
value === user.email
) {
errorMessage = ERROR_MESSAGE[authType]["EXIST_EMAIL"];
}

validationState[name] = !errorMessage;

return errorMessage;
}

export function resetError() {
const errorMessages = document.querySelectorAll(".error-message");
errorMessages.forEach((message) => (message.textContent = ""));
}
export function displayError(input, errorMessage) {
const errorDisplay = input.parentNode.nextElementSibling;
errorDisplay.textContent = errorMessage;
}
11 changes: 11 additions & 0 deletions folder.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div>folder 페이지</div>
</body>
</html>