Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
13 changes: 8 additions & 5 deletions login/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<link rel="icon" href="../images/favi.ico">
<link rel="stylesheet" href="../reset.css">
<link rel="stylesheet" href="./login.css" />
<script src="./login-main.js" type="module"></script>
</head>
<body>
<main>
Expand All @@ -16,14 +17,16 @@
<img src="../images/logo-title.png" alt="판다마켓 로고" />
</a>
</div>
<form class="login__form" method="POST">
<form class="login__form" method="get" action="../items/">
<fieldset>
<label for="user-email">이메일</label>
<input type="text" id="user-email" name="user-email" placeholder="이메일을 입력해주세요"/>
<label for="user-password">비밀번호</label>
<input id="user-password" type="password" name="user-password" placeholder="비밀번호를 입력해주세요"/>
<input id="toggle-visibility-pw" type="checkbox"/>
<label aria-label="비밀번호 표시 여부" aria-checked="false" for="toggle-visibility-pw"></label>
<div class="container__position-relative">
<label for="user-password">비밀번호</label>
<input id="user-password" type="password" name="user-password" placeholder="비밀번호를 입력해주세요"/>
<input id="toggle-visibility-pw" type="checkbox"/>
<label aria-label="비밀번호 표시 여부" aria-checked="false" for="toggle-visibility-pw"></label>
</div>
<button type="submit">로그인</button>
</fieldset>
<div class="login__alert">
Expand Down
7 changes: 7 additions & 0 deletions login/login-main.js
Copy link
Collaborator

Choose a reason for hiding this comment

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

함수를 적절한 단위의 작업으로 나누고 모듈화하신 시도 너무 좋습니다! 👍

여기서 더 개선해보자면,
login 파일에 사용된 네이밍과 구조를 아래 예시와 같이 좀 더 페이지&이벤트 기반으로 바꿔보는건 어떨까요?

// Initialize form validation
const signInValidationState = createValidState({ emailInput, passwordInput }, ruleObj)

initValidation(signInValidationState);

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createValidRule, ruleObj, emailInput, passwordInput } from '../modules/validation-rule.mjs';
import { initValidation } from '../modules/validate.mjs';
import { initPasswordVisibility, visibilityPw } from '../modules/toggle-visibility-pw.mjs';

const validRule = createValidRule({ emailInput, passwordInput }, ruleObj);
initValidation(validRule);
initPasswordVisibility(visibilityPw);
41 changes: 33 additions & 8 deletions login/login.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@import url(../global.css);

.container__position-relative {
position: relative;
}

main {
padding: 0 16px;
}
Expand Down Expand Up @@ -28,12 +32,11 @@ fieldset {
flex-direction: column;
width: 100%;
border: none;
position: relative;
}

label[for="toggle-visibility-pw"] {
position: absolute;
top: 152px;
top: 52px;
right: 24px;
display: inline-block;
width: 24px;
Expand Down Expand Up @@ -67,22 +70,19 @@ input {
background-color: var(--secondary-gray100);
height: 56px;
padding: 12px 24px;
width: 100%;
}

button {
height: 56px;
padding: 16px 124px;
border-radius: 40px;
background-color: var(--primary-color);
background-color: var(--secondary-gray400);
color: var(--white-color);
margin-bottom: 24px;
font-size: 20px;
}

button:hover {
background-color: var(--primary-hover);
}

.login__alert {
height: 74px;
background-color: #e6f2ff;
Expand Down Expand Up @@ -119,6 +119,31 @@ button:hover {
color: var(--primary-color);
}

/* validation err 관련 스타일 */
.error-message {
/* err메세지 */
color: red;
font-size: 14px;
line-height: 26px;
padding-left: 24px;
display: block;
margin-top: -16px;
margin-bottom: 16px;
}

.error-Line {
/* 인풋 빨간 라인*/
border: 1px solid red;
}

.pass-button {
background-color: var(--primary-color);
}

.pass-button:hover {
background-color: var(--primary-hover);
}

@media screen and (min-width: 768px) {
.main__login {
max-width: 640px;
Expand All @@ -131,7 +156,7 @@ button:hover {
}

label[for="toggle-visibility-pw"] {
top: 180px;
top: 60px;
}

form label {
Expand Down
36 changes: 36 additions & 0 deletions modules/toggle-visibility-pw.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// #toggle-visibility-pw 관련 변수
export const visibilityPw = document.querySelector("#toggle-visibility-pw");
const visibilityPwLabel = document.querySelector("label[for='toggle-visibility-pw']");
const passwordInput = document.querySelector("#user-password");

// #toggle-visibility-pwcheck 관련 변수
export const visibilityPwCheck = document.querySelector("#toggle-visibility-pwcheck");
const visibilityPwCheckLabel = document.querySelector("label[for='toggle-visibility-pwcheck']");
const pwCheckInput = document.querySelector("#user-password-check");

function toggleVisibilityPw() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

aria-checked도 js로 잘 토글해주고계시네요! 굳굳 👍

Copy link
Collaborator

Choose a reason for hiding this comment

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

다만 현재 이 함수는 DOM에 의존적이라 따로 로직만 테스트하기도 어렵고, 재사용성하기 어렵습니다.
아래 예시처럼 토글 로직은 DOM에 의존하지않게끔 분리해보면 어떨까요?

function togglePasswordVisibility(input, toggleBtn) {
  const isPassword = input.type === "password";
  input.type = isPassword ? "text" : "password";
  toggleBtn.classList.toggle(VISIBLE_CLASS, isPassword);
}

const isShowing = visibilityPw.checked;
if (isShowing) {
passwordInput.type = "text";
visibilityPwLabel.setAttribute('aria-checked', 'true');
} else {
passwordInput.type = "password";
visibilityPwLabel.setAttribute('aria-checked', 'false');
}
}

function toggleVisibilityPwCheck() {
const isShowing = visibilityPwCheck.checked;
if (isShowing) {
pwCheckInput.type = "text";
visibilityPwCheckLabel.setAttribute('aria-checked', 'true');
} else {
pwCheckInput.type = "password";
visibilityPwCheckLabel.setAttribute('aria-checked', 'false');
}
}

export function initPasswordVisibility(visibilityPw, visibilityPwCheck){
Copy link
Collaborator

Choose a reason for hiding this comment

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

그리고 이런 초기화를 담당하는 함수의 경우에도, 위처럼 토글 로직을 분리한 형태에 맞춰 리팩토링하게되면

export function initPasswordVisibility({ visibilityPw, visibilityPwCheck }) {
  if (!visibilityPw) return;

  const passwordInput = visibilityPw.previousElementSibling;
  if (!passwordInput || passwordInput.type !== "password") return;

  // 비밀번호 토글 이벤트 설정
  visibilityPw.addEventListener("click", () => {
    togglePasswordVisibility(passwordInput, visibilityPw);
  });

  // 비밀번호 확인 토글 이벤트 설정
  if (visibilityPwCheck) {
    const passwordCheckInput = visibilityPwCheck.previousElementSibling;
    if (passwordCheckInput && passwordCheckInput.type === "password") {
      visibilityPwCheck.addEventListener("click", () => {
        togglePasswordCheckVisibility(passwordCheckInput, visibilityPwCheck);
      });
    }
  }
}

이렇게 토글 로직을 언제 사용해야할지에 대한 사용 의도와 시점이 좀 더 명확해질수있겠죠?

Copy link
Collaborator

Choose a reason for hiding this comment

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

if (!visibilityPw) return; 과 같이 조건에 맞지않는 경우 다음 줄을 실행하지않고 바로 리턴하는 기법을 early return이라고합니다.

early return을 잘 사용하면, 예외 상황을 먼저 처리하게끔 만들어 핵심 로직에 더욱 집중할수있게 만들어줄수있어 디버깅이 용이해지는 장점이 있고, 성능적으로도 불필요한 연산을 줄일 수 있어 메모리 사용량 또한 감소된답니다 👍

visibilityPw.addEventListener("click", toggleVisibilityPw);
if (visibilityPwCheck) visibilityPwCheck.addEventListener("click", toggleVisibilityPwCheck);
}
78 changes: 78 additions & 0 deletions modules/validate.mjs
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 파일에서도 토글 로직과 UI를 분리해 코드를 더 간결하게 만들었던것처럼 개선할만한 여지가 있는지 한번 더 고민해볼까요? :)

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const userForm = document.querySelector("form");
const submitBtn = document.querySelector("button");

// 검사 통과 못한 거 있나요?
function hasInvalidInput(validRule) {
return Object.values(validRule).some(validator => validator.passed === false);
}

//폼 전송 막기
function preventInvalidSubmit(e, validRule) {
//서브밋 누르면 통과 못한 인풋에 경고 보여줘
Object.values(validRule).forEach((validator) => {
updateValidation({ currentTarget: validator.input }, validRule);
});

if (hasInvalidInput(validRule)) {
e.preventDefault();
}
}

//검사 실패 시 스타일 추가
function updateValidation(e, validRule) {
const { name } = e.currentTarget;
const validator = validRule[name];

removeMessage(name)

if (!validator.condition()) {
appendErr(validator);
validator.passed = false;
} else {
clearErr(validator);
validator.passed = true;
}

updateButton(validRule);
}

//존재하는 실패 메세지 삭제
function removeMessage(name) {
const msg = document.querySelector(`#${name}+.error-message`);
if (msg) msg.remove();
}

// 검사 실패 메세지 추가
function appendErr(validator) {
validator.input.classList.add("error-Line");
const err = document.createElement("span");
err.classList.add("error-message");
err.textContent = validator.createMsg();
validator.input.insertAdjacentElement("afterend", err);
}

// 검사 실패 스타일 삭제
function clearErr(validator) {
validator.input.classList.remove("error-Line");
}

// 버튼 스타일 추가
function updateButton(validRule) {
submitBtn.classList.toggle("pass-button", !hasInvalidInput(validRule));
}

//리스너 추가
export function initValidation(validRule) {
for (let validator in validRule) {
validRule[validator].input.addEventListener("focusout", e => updateValidation(e, validRule));
}

userForm.addEventListener("submit", e => preventInvalidSubmit(e, validRule));

if (validRule['user-password-check']) {
validRule['user-password'].input.addEventListener("focusout", () => {
// 비밀번호 변경되었을 때 비밀번호 확인도 같이
updateValidation({ currentTarget: validRule['user-password-check'].input }, validRule);
});
}
}
60 changes: 60 additions & 0 deletions modules/validation-rule.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export const emailInput = document.querySelector("#user-email");
export const passwordInput = document.querySelector("#user-password");
export const nameInput = document.querySelector("#user-name");
export const passwordCheckInput = document.querySelector("#user-password-check");

// 전체 검사 조건
export const ruleObj = {
'user-email': {
input: emailInput,
isValid() {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.input.value);
},
getErrorMessage() {
return this.input.value ? "잘못된 이메일입니다." : "이메일을 입력해주세요"
},
},
'user-password': {
input: passwordInput,
isValid() {
return this.input.value.length >= 8;
},
getErrorMessage() {
return (this.input.value.length == 0) ? "비밀번호를 입력해주세요." : "비밀번호를 8자 이상 입력해주세요."
},
},
'user-name': {
input: nameInput,
isValid() {
return this.input.value;
},
getErrorMessage() {
return "닉네임을 입력해주세요";
},
},
'user-password-check': {
input: passwordCheckInput,
isValid() {
return (passwordInput.value === this.input.value) && passwordCheckInput.value.length != 0;
},
getErrorMessage() {
return "비밀번호가 일치하지 않습니다."
},
}
}

// 해당 페이지에 있는 인풋만 규칙 만들기
export function createValidRule(inputElement, ruleObj) {
const validRule = {}

Object.values(inputElement).forEach((input) => {
validRule[input.name] = {
input,
condition: ruleObj[input.name].isValid,
createMsg: ruleObj[input.name].getErrorMessage,
passed: false,
}
});

return validRule;
}
23 changes: 14 additions & 9 deletions sign_up/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<link rel="icon" href="../images/favi.ico">
<link rel="stylesheet" href="../reset.css">
<link rel="stylesheet" href="./sign-up.css" />
<script src="./sign-up-main.js" type="module"></script>
</head>
<body>
<main>
Expand All @@ -16,20 +17,24 @@
<img src="../images/logo-title.png" alt="판다마켓 로고" />
</a>
</div>
<form class="sign-up__form" method="POST">
<form class="sign-up__form" method="get" action="../login/">
<fieldset>
<label for="user-email">이메일</label>
<input type="text" id="user-email" name="user-email" placeholder="이메일을 입력해주세요"/>
<label for="user-name">닉네임</label>
<input id="user-name" type="text" name="user-name" placeholder="닉네임을 입력해주세요"/>
<label for="user-password">비밀번호</label>
<input id="user-password" type="password" name="user-password" placeholder="비밀번호를 입력해주세요"/>
<label for="user-password">비밀번호 확인</label>
<input id="user-password-check" type="password" name="user-password-check" placeholder="비밀번호를 다시 한 번 입력해주세요"/>
<input id="toggle-visibility-pw" type="checkbox"/>
<label aria-label="비밀번호 표시 여부" aria-checked="false" for="toggle-visibility-pw"></label>
<input id="toggle-visibility-pwcheck" type="checkbox"/>
<label aria-label="비밀번호 확인 표시 여부" aria-checked="false" for="toggle-visibility-pwcheck"></label>
<div class="container__position-relative">
<label for="user-password">비밀번호</label>
<input id="user-password" type="password" name="user-password" placeholder="비밀번호를 입력해주세요"/>
<input id="toggle-visibility-pw" type="checkbox"/>
<label aria-label="비밀번호 표시 여부" aria-checked="false" for="toggle-visibility-pw"></label>
</div>
<div class="container__position-relative">
<label for="user-password">비밀번호 확인</label>
<input id="user-password-check" type="password" name="user-password-check" placeholder="비밀번호를 다시 한 번 입력해주세요"/>
<input id="toggle-visibility-pwcheck" type="checkbox"/>
<label aria-label="비밀번호 확인 표시 여부" aria-checked="false" for="toggle-visibility-pwcheck"></label>
</div>
<button type="submit">회원가입</button>
</fieldset>
<div class="sign-up__alert">
Expand Down
7 changes: 7 additions & 0 deletions sign_up/sign-up-main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createValidRule, ruleObj, emailInput, passwordInput, nameInput, passwordCheckInput } from '../modules/validation-rule.mjs';
import { initValidation } from '../modules/validate.mjs';
import { initPasswordVisibility, visibilityPw, visibilityPwCheck } from '../modules/toggle-visibility-pw.mjs';

const validRule = createValidRule({ emailInput, passwordInput, nameInput, passwordCheckInput }, ruleObj);
initValidation(validRule);
initPasswordVisibility(visibilityPw, visibilityPwCheck);
14 changes: 4 additions & 10 deletions sign_up/sign-up.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,9 @@ label[for="toggle-visibility-pwcheck"] {
cursor: pointer;
}

label[for="toggle-visibility-pw"] {
top: 258px;
}

label[for="toggle-visibility-pw"],
label[for="toggle-visibility-pwcheck"] {
top: 360px;
top: 46px;
}

#toggle-visibility-pwcheck {
Expand Down Expand Up @@ -90,12 +87,9 @@ label[for="toggle-visibility-pwcheck"] {
width: 396px;
}

label[for="toggle-visibility-pw"] {
top: 304px;
}

label[for="toggle-visibility-pw"],
label[for="toggle-visibility-pwcheck"] {
top: 424px;
top: 58px;
}
}

Expand Down