Skip to content
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
/>
</picture>
</a>
<a class="login" href="login.html">로그인</a>
<a class="login" href="signin.html">로그인</a>
</nav>
</header>

Expand Down
34 changes: 21 additions & 13 deletions login.html → signin.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</head>
<body>
<main>
<div class="login-main">
<div class="auth-main">
<div class="logo">
<a href="index.html">
<img
Expand All @@ -18,10 +18,10 @@
/>
</a>
</div>
<form class="login-form">
<label for="email" class="login-label">
<form class="auth-form">
<label for="email" class="auth-label">
<span>이메일</span>
<div class="login-input-box">
<div class="auth-input-box gray-border">
<input
id="email"
type="text"
Expand All @@ -30,26 +30,33 @@
/>
</div>
</label>
<label for="password" class="login-label">
<label for="password" class="auth-label">
<span>비밀번호</span>
<div class="login-input-box">
<div class="auth-input-box gray-border">
<input
id="password"
type="password"
name="password"
placeholder="비밀번호를 입력해주세요"
/>
<img
src="src/assets/icons/btn_visibility_off_24px.svg"
alt="감은 눈 아이콘"
/>
<div class="eye">
<img
src="src/assets/icons/ic_closed_eye.svg"
alt="감은 눈 아이콘"
/>
<img
class="none"
src="src/assets/icons/ic_opened_eye.svg"
alt="뜬 눈 아이콘"
/>
</div>
</div>
</label>
<button class="login-button">로그인</button>
<button class="auth-button" disabled>로그인</button>
</form>
<div class="convenient-login-box">
<div class="convenient-auth-box">
<span>간편 로그인하기</span>
<div class="convenient-login-icon-box">
<div class="convenient-auth-icon-box">
<a href="https://www.google.com/">
<img src="src/assets/icons/ic_google.svg" alt="구글 아이콘" />
</a>
Expand All @@ -67,4 +74,5 @@
</div>
</main>
</body>
<script type="module" src="src/js/signin.mjs"></script>
</html>
60 changes: 38 additions & 22 deletions signup.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</head>
<body>
<main>
<div class="login-main">
<div class="auth-main">
<div class="logo">
<a href="index.html">
<img
Expand All @@ -18,10 +18,10 @@
/>
</a>
</div>
<form class="login-form">
<label for="email" class="login-label">
<form class="auth-form">
<label for="email" class="auth-label">
<span>이메일</span>
<div class="login-input-box">
<div class="auth-input-box gray-border">
<input
id="email"
type="text"
Expand All @@ -30,9 +30,9 @@
/>
</div>
</label>
<label for="nickname" class="login-label">
<label for="nickname" class="auth-label">
<span>닉네임</span>
<div class="login-input-box">
<div class="auth-input-box gray-border">
<input
id="nickname"
type="text"
Expand All @@ -41,41 +41,56 @@
/>
</div>
</label>
<label for="password" class="login-label">
<label for="password" class="auth-label">
<span>비밀번호</span>
<div class="login-input-box">
<div class="auth-input-box gray-border">
<input
id="password"
type="password"
name="password"
placeholder="비밀번호를 입력해주세요"
/>
<img
src="src/assets/icons/btn_visibility_off_24px.svg"
alt="감은 눈 아이콘"
/>
<div class="eye">
<img
src="src/assets/icons/ic_closed_eye.svg"
alt="감은 눈 아이콘"
/>
<img
class="none"
src="src/assets/icons/ic_opened_eye.svg"
alt="뜬 눈 아이콘"
/>
</div>
</div>
</label>
<label for="repassword" class="login-label">
<label for="repassword" class="auth-label">
<span>비밀번호 확인</span>
<div class="login-input-box">
<div class="auth-input-box gray-border">
<input
id="repassword"
type="password"
name="repassword"
placeholder="비밀번호를 다시 한 번 입력해주세요"
/>
<img
src="src/assets/icons/btn_visibility_off_24px.svg"
alt="감은 눈 아이콘"
/>

<div class="eye">
<img
src="src/assets/icons/ic_closed_eye.svg"
alt="감은 눈 아이콘"
/>
<img
class="none"
src="src/assets/icons/ic_opened_eye.svg"
alt="뜬 눈 아이콘"
/>
</div>
</div>
</label>
<button class="login-button">회원가입</button>
<button class="auth-button" disabled>회원가입</button>
</form>
<div class="convenient-login-box">
<div class="convenient-auth-box">
<span>간편 로그인하기</span>
<div class="convenient-login-icon-box">
<div class="convenient-auth-icon-box">
<a href="https://www.google.com/">
<img src="src/assets/icons/ic_google.svg" alt="구글 아이콘" />
</a>
Expand All @@ -88,9 +103,10 @@
</div>
</div>
<p class="sign-up-paragraph">
이미 회원이신가요? <a href="login.html">로그인</a>
이미 회원이신가요? <a href="signin.html">로그인</a>
</p>
</div>
</main>
</body>
<script type="module" src="src/js/signup.mjs"></script>
</html>
3 changes: 3 additions & 0 deletions src/assets/icons/ic_opened_eye.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/js/common/auth.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function toggleInputType(e) {
const eventCurrentTarget = e.currentTarget;
const inputElement = eventCurrentTarget.parentElement.firstElementChild;
const [closeEye, openEye] = eventCurrentTarget.children;

openEye.classList.toggle("none");
closeEye.classList.toggle("none");
inputElement.setAttribute(
"type",
inputElement.type === "password" ? "text" : "password"
);
}

export { toggleInputType };
65 changes: 65 additions & 0 deletions src/js/common/validate.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
function addErrorMessage(el, message) {
const target = el.parentElement.parentElement;
const pElement = document.createElement("p");
pElement.classList.add("error-message");
pElement.textContent = message;
target.append(pElement);
}

function onInputError(el, { message }) {
const parentElement = el.parentElement;
parentElement.classList.add("red-border");
parentElement.classList.remove("gray-border");

const hasError =
parentElement.parentElement.lastElementChild.classList.contains(
"error-message"
);

if (!hasError) {
addErrorMessage(el, message);
}
}

function offInputError(el) {
el.parentElement.classList.add("gray-border");
el.parentElement.classList.remove("red-border");
}

function checkValidation({ el, isValidate, message }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

함수명에서 에러 UI를 다루는 것을 표현하면 더욱 좋을 거 같아요!

if (isValidate === true) {
offInputError(el);
} else {
onInputError(el, { message });
}
}

function validate(e, { targetEl, message, validator }) {
Copy link
Collaborator

@dongqui dongqui Jan 27, 2025

Choose a reason for hiding this comment

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

함수의 역할을 분명히 하면 더욱 좋겠습니다!

dom 요소를 받아서 유효성을 검사하는 함수인데, 이벤트 객체를 받거나 targetEl이 들어오면서 복잡도가 증가된 거 같습니다! isMatch 때문인 거 같은데 관련 부분은 밖에서 처리 하는 것이 좀 더 나을 거 같아요.

isMatch: (value1) => (value2) => value1 !== "" && value1 === value2,
//...
validate(e.target, {
    validator: authValidator.isMatch($passwordInput.value),
    message: ERROR_MESSAGE.IS_NOT_MATCH_PASSWORD,
  });

이런식으로 할 수도 있겠네요!

(참고만 해주세요! 충분히 잘 하셨습니다~!)

const el = e?.target ? e.target : e;
const value1 = el.value;
const value2 = targetEl?.value;
const isValidate = validator(value1, value2);

checkValidation({ el, isValidate, message });
}

function resetErrorMessage(e) {
const errorCandidateElement =
e.target.parentElement.parentElement.lastElementChild;
const hasError = errorCandidateElement.classList.contains("error-message");

if (hasError) {
errorCandidateElement.remove();
}
}

function changeButtonStatus(el, { checkEmptyInputElements }) {
const isEmpty = checkEmptyInputElements.some(
(inputElement) => inputElement.value === ""
);
const isError = document.querySelectorAll(".error-message").length > 0;

el.disabled = isEmpty || isError;
}

export { resetErrorMessage, validate, changeButtonStatus };
12 changes: 12 additions & 0 deletions src/js/common/validator.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const MINIMUM_PASSWORD_LENGTH = 8;
const EMAIL_REG_EXP =
/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i;
Copy link
Collaborator

Choose a reason for hiding this comment

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

정규식을 써주셨네요! 👍


const authValidator = {
isWrongEmailFormat: (value) => EMAIL_REG_EXP.test(value),
isEmptyInput: (value) => value !== "",
isMoreThanEight: (value) => value.length >= MINIMUM_PASSWORD_LENGTH,
isMatch: (value1, value2) => value1 !== "" && value1 === value2,
};

export { authValidator };
11 changes: 11 additions & 0 deletions src/js/constants/contants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const ERROR_MESSAGE = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

constatns를 따로 정의하셨군요! 👍

IS_EMPTY_EMAIL: "이메일을 입력해 주세요.",
IS_WRONG_EMAIL_FORMAT: "잘못된 이메일입니다.",
IS_EMPTY_NICKNAME: "닉네임을 입력해 주세요.",
IS_EMPTY_PASSWORD: "비밀번호를 입력해 주세요.",
IS_EMPTY_REPASSWORD: "비밀번호 확인을 입력해 주세요.",
IS_MORE_THAN_EIGHT_PASSWORD: "비밀번호를 8자 이상 입력해주세요.",
IS_NOT_MATCH_PASSWORD: "비밀번호가 일치하지 않습니다.",
};

export { ERROR_MESSAGE };
53 changes: 53 additions & 0 deletions src/js/signin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
validate,
resetErrorMessage,
changeButtonStatus,
} from "./common/validate.mjs";
import { toggleInputType } from "./common/auth.mjs";
import { authValidator } from "./common/validator.mjs";
import { ERROR_MESSAGE } from "./constants/contants.mjs";

const $emailInput = document.querySelector("#email");
const $passwordInput = document.querySelector("#password");
const $submitButton = document.querySelector(".auth-button");
const $eyeIcon = document.querySelector(".eye");

const checkEmptyInputElements = [$emailInput, $passwordInput];

function onFocusoutEmail(e) {
resetErrorMessage(e);
Copy link
Collaborator

Choose a reason for hiding this comment

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

같은 목적을 가진 코드는 모아두는 게 좋습니다! (응집도)

에러를 초기화 하는 코드라면 offInputError 쪾으로 가는 게 조금 더 나을 거 같습니다 :)

validate(e, {
validator: authValidator.isEmptyInput,
message: ERROR_MESSAGE.IS_EMPTY_EMAIL,
});
validate(e, {
validator: authValidator.isWrongEmailFormat,
message: ERROR_MESSAGE.IS_WRONG_EMAIL_FORMAT,
});

changeButtonStatus($submitButton, { checkEmptyInputElements });
Copy link
Collaborator

Choose a reason for hiding this comment

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

이벤트 버블링을 활용해보셔도 좋을 거 같습니다 :)

}

function onFocusoutPassword(e) {
resetErrorMessage(e);
validate(e, {
validator: authValidator.isEmptyInput,
message: ERROR_MESSAGE.IS_EMPTY_PASSWORD,
});
validate(e, {
validator: authValidator.isMoreThanEight,
message: ERROR_MESSAGE.IS_MORE_THAN_EIGHT_PASSWORD,
});

changeButtonStatus($submitButton, { checkEmptyInputElements });
}

function onClickButton(e) {
e.preventDefault();
location.href = "/items.html";
}

$emailInput.addEventListener("focusout", onFocusoutEmail);
$passwordInput.addEventListener("focusout", onFocusoutPassword);
$eyeIcon.addEventListener("click", toggleInputType);
$submitButton.addEventListener("click", onClickButton);
Loading
Loading