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

[함헌규] sprint3 #34

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
89e4d71
docs: 미션 요구사항 문서 작성
heonq Sep 30, 2024
c52b985
style: 로그인,회원가입 페이지 스타일 변경 및 태그 추가
heonq Sep 30, 2024
a61f3a1
feat: 유효성 검사를 담당하는 validator 객체 구현
heonq Sep 30, 2024
b1f04dc
feat: querySelector 유틸 함수 구현
heonq Sep 30, 2024
5abc028
feat: 인풋 오류 상태를 처리하는 함수 구현
heonq Sep 30, 2024
bec555f
feat: 로그인 페이지 input창 유효성 검사 기능 구현
heonq Sep 30, 2024
9d963cf
fix: 닉네임 유효성 검사 기능 수정
heonq Sep 30, 2024
317a2e4
refactor: 로그인 페이지 기능 리팩토링
heonq Sep 30, 2024
b2c9c00
feat: 로그인 페이지 기능 추가
heonq Sep 30, 2024
9cc4d47
style: 로그인/회원가입 페이지 스타일 변경
heonq Sep 30, 2024
2a56f37
feat: 회원가입 페이지 유효성 검사 기능 구현
heonq Sep 30, 2024
82e4dc5
fix: 로그인/회원가입 페이지 오류 수정
heonq Sep 30, 2024
e3b39c2
feat: 로그인/회원가입 페이지 기능 구현
heonq Sep 30, 2024
1143857
feat: 로그인/회원가입 페이지 모달 기능 구현
heonq Sep 30, 2024
af9afbc
style: 반응형 디자인 구현
heonq Oct 1, 2024
3f145ba
feat: 로그인/회원가입 페이지 기능 구현
heonq Oct 1, 2024
3a4284f
refactor: 로그인,회원가입 페이지 기능 리팩토링
heonq Oct 1, 2024
f6f2b77
style: 랜딩페이지 반응형 스타일 작성
heonq Oct 1, 2024
fab2a2b
chore: 공유 시 미리보기를 설정할 메타 태그 작성
heonq Oct 1, 2024
f22ed98
style: 랜딩페이지 스타일 변경
heonq Oct 1, 2024
c3713df
style: 랜딩페이지 태그 및 스타일 변경
heonq Oct 2, 2024
05d4e18
style: 랜딩페이지 스타일 변경
heonq Oct 2, 2024
d567ce8
fix: 코드 오타 수정
heonq Oct 5, 2024
3f1f036
feat: 로그인/회원가입 인풋 관리 기능
heonq Oct 5, 2024
8d67925
fix:로그인/회원가입 페이지 오류 수정
heonq Oct 6, 2024
6a41751
style: 로그인/회원가입 페이지 스타일 수정
heonq Oct 6, 2024
2bfc1e0
style: 랜딩페이지 스타일 변경
heonq Oct 6, 2024
4230149
style: 랜딩 페이지 스타일 수정
heonq Oct 6, 2024
b50523c
refactor: sign 클래스 리팩토링
heonq Oct 10, 2024
5342893
refactor: Sign 클래스 리팩토링
heonq Oct 10, 2024
c639bf5
refactor: 로그인,회원가입 페이지 리팩토링
heonq Oct 10, 2024
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
11 changes: 11 additions & 0 deletions assets/scripts/constants/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { VALIDATION_VALUES } from "./validationValues.js";

export const ERROR_MESSAGES = {
inputEmail: "이메일을 입력해주세요.",
invalidEmail: "잘못된 이메일 형식입니다",
inputPassword: "비밀번호를 입력해주세요.",
inputValidLengthPassword: `비밀번호를 ${VALIDATION_VALUES.passwordLength}자 이상 입력해주세요.`,
passwordNotMatch: "비밀번호가 일치하지 않습니다.",
emailAlreadyInUse: "사용 중인 이메일입니다",
inputNickname: "닉네임을 입력해주세요.",
};
6 changes: 6 additions & 0 deletions assets/scripts/constants/names.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const KEYS = {
email: "email",
nickname: "nickname",
password: "password",
confirmPassword: "confirmPassword",
};
8 changes: 8 additions & 0 deletions assets/scripts/constants/userData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const USER_DATA = [
{ email: "codeit1@codeit.com", password: "codeit101!" },
{ email: "codeit2@codeit.com", password: "codeit202!" },
{ email: "codeit3@codeit.com", password: "codeit303!" },
{ email: "codeit4@codeit.com", password: "codeit404!" },
{ email: "codeit5@codeit.com", password: "codeit505!" },
{ email: "codeit6@codeit.com", password: "codeit606!" },
];
3 changes: 3 additions & 0 deletions assets/scripts/constants/validationValues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const VALIDATION_VALUES = {
passwordLength: 8,
};
101 changes: 101 additions & 0 deletions assets/scripts/core/sign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import $ from "../utils/query.js";

class Sign {
constructor() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

생성자에서 만들어주신 class 내부 각 필드들이 어떠한 역활을 하는지 주석으로 적어주시면 좋을것 같아요

// 인풋 값의 유효성 상태를 저장하는 객체
this.inputValidState = {};
// 페이지의 인풋 DOM요소
this.inputs = {};
// 인풋 값의 유효성검사 메소드
this.validateMethods = {};
}

init() {
this.handleInputFocusout();
this.setFormSubmit();
this.setCloseButton();
this.handleToggleButton();
}

setState(newState) {
this.inputValidState = {
...this.inputValidState,
...newState,
};
$("#submit-button").disabled = Object.values(this.inputValidState).some((state) => !state);
}

toggleInputError(e, message = "") {
const errorMessageNode = e.target.closest("section").querySelector(".message");
errorMessageNode.innerText = message;
if (message.length) {
e.target.classList.add("error");
errorMessageNode.setAttribute("aria-hidden", false);
return errorMessageNode.classList.add("show");
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

특정 요소에 display:none으로 설정 후 show클래스에 display:block을 설정해서 요소를 표시할 때 show 클래스를 추가하는 식으로 구현했는데
해당 요소는 p 요소라 innerText를 빈 문자열로 설정해서 안보이게 했다가 innerText에 메시지를 입력해서 표시 할 수 있을 것 같은데 둘 중 어떤 방식이 바람직한지 궁금합니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

방식은 여러가지가 있을수 있겠으나 에러메세지가 있거나, 없거나를 생각한다면 innerText를 빈문자열로, 혹은 에러메세지로 설정해서 표시해주시는게 좋을것 같아요! 현재는 error를 보여주는 텍스트의 레이아웃(크기)이 잡혀있기에 이런식으로 처리해도 무방할 것 같습니다

}
e.target.classList.remove("error");
errorMessageNode.setAttribute("aria-hidden", true);
errorMessageNode.classList.remove("show");
}

handleInputFocusout() {
for (const key in this.inputs) {
this.inputs[key].addEventListener("focusout", (e) => {
const { value } = e.target;
const message = this.validateMethods[key](value);
this.toggleInputError(e, message);
this.setState({ [key]: message.length === 0 });
});
}
}

setFormSubmit() {
$("form").addEventListener("submit", (e) => {
e.preventDefault();
this.onSubmit();
});
}

showModal(message) {
$("#modal-container").classList.add("show");
$("#error-message").innerText = message;
}

setCloseButton() {
$("#modal-close-button").addEventListener("click", () => {
$("#modal-container").classList.remove("show");
});
}

getValues() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

아래 리뷰하다가 다시 돌아왔는데,

입력필드의 어떠한 값을 가져올때 항상 전체를 가져와서 하나를 사용하는식으로 사용될것 같아요.
현재는 배열 형태로 구성하고 있지만 이럴때 key, value쌍으로 저장되어있는 자료구조를 사용한다면 특정 값만 접근하는 방법을 사용할 수도 있을것 같습니다.

return Object.values(this.inputs).reduce((acc, input, index) => {
return { ...acc, [Object.keys(this.inputs)[index]]: input.value };
}, {});
}

onSubmit() {}

handleSubmitSuccess() {
window.location.href = "../items/index.html";
}

handleSubmitFailure(message) {
this.showModal(message);
}

togglePasswordVisibility(e) {
const { target } = e;
target.classList.toggle("fa-eye-slash");
target.classList.toggle("fa-eye");
const input = target.closest("div").querySelector("input");
input.type === "text" ? (input.type = "password") : (input.type = "text");
}

handleToggleButton() {
document
.querySelectorAll(".eye-icon")
.forEach((icon) => icon.addEventListener("click", this.togglePasswordVisibility));
}
}

export default Sign;
43 changes: 43 additions & 0 deletions assets/scripts/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import $ from "./utils/query.js";
import { USER_DATA } from "./constants/userData.js";
import { ERROR_MESSAGES } from "./constants/messages.js";
import Sign from "./core/sign.js";
import { validator } from "./utils/validator.js";
import { KEYS } from "./constants/names.js";

class LoginForm extends Sign {
Copy link
Collaborator

Choose a reason for hiding this comment

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

상속을 이용해서 처리해주신 부분 굉장히 좋은것 같습니다!

constructor() {
super();
this.inputValidState = {
[KEYS.email]: false,
[KEYS.password]: false,
};
this.inputs = {
[KEYS.email]: $("#email"),
[KEYS.password]: $("#password"),
};
this.validateMethods = {
[KEYS.email]: validator.validateEmail,
[KEYS.password]: validator.validatePassword,
};
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

입력창 DOM 요소와 입력창의 값에 대한 유효성 검사 함수를 이렇게 배열형태로 필드로 저장하고 사용하는 방식은 적절한 방식인지 궁금합니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

작성해주신대로 어떠한 비즈니스로직을 구성할 때 미리 필요한 항목들을 배열형태로 작성하는 방식은 많이 사용하는 방식입니다! 그리고 KEYS.email, KEYS.password처럼 명시적인 이름을 붙혀주셧기에 확인하기도 쉬울 것 같아요.

하지만 이러한 경우의수가 굉장히 많을때는 조금 경계해야 할 필요도 있습니다. 성능상의 문제가 발생하거나, 잘 작성된 주석이 없다면 다른사람이 코드를 읽기 불편한 상황이 생길수 있습니다.
그 예로 위에 Sign클래스의 setInput함수를 보면 배열의 인덱스 순으로 값을 검증하는걸 볼 수 있는데, 이럴때 순서에 의해서도 오류가 발생할 수도 있습니다. 따라서 각각의 필드를 따로 관리하거나, 다른 자료구조를 사용해 볼 수 있다면 이러한 문제를 해결할 수도 있을것 같아요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

동일한 key를 가진 객체형태로 변경하고 key를 통해 참조하도록 변경했습니다!


onSubmit() {
const values = this.getValues();
const matchingAccount = USER_DATA.find((data) => data.email === values.email);
matchingAccount?.password === values.password
? this.handleLoginSuccess()
: this.handleLoginFailure(ERROR_MESSAGES.passwordNotMatch);
}

handleLoginSuccess() {
super.handleSubmitSuccess();
// 이후 추가 로직 작성
}
handleLoginFailure(message) {
super.handleSubmitFailure(message);
// 이후 추가 로직 작성
}
}

new LoginForm().init();
52 changes: 52 additions & 0 deletions assets/scripts/signup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import $ from "./utils/query.js";
import { ERROR_MESSAGES } from "./constants/messages.js";
import { USER_DATA } from "./constants/userData.js";
import Sign from "./core/sign.js";
import { validator } from "./utils/validator.js";
import { KEYS } from "./constants/names.js";

class signupForm extends Sign {
constructor() {
super();
this.inputValidState = {
[KEYS.email]: false,
[KEYS.nickname]: false,
[KEYS.password]: false,
[KEYS.confirmPassword]: false,
};
this.inputs = {
[KEYS.email]: $("#email"),
[KEYS.nickname]: $("#nickname"),
[KEYS.password]: $("#password"),
[KEYS.confirmPassword]: $("#confirm-password"),
};
this.validateMethods = {
[KEYS.email]: validator.validateEmail,
[KEYS.nickname]: validator.validateNickname,
[KEYS.password]: validator.validatePassword,
[KEYS.confirmPassword]: validator.validatePassword,
};
}

onSubmit() {
const values = this.getValues();
const emailAlreadyInUse = USER_DATA.find((data) => data.email === values.email);
const passwordMatch = values.password === values.confirmPassword;
emailAlreadyInUse
? this.handleSignupFailure(ERROR_MESSAGES.emailAlreadyInUse)
: !passwordMatch
? this.handleSignupFailure(ERROR_MESSAGES.passwordNotMatch)
: this.handleSignupSuccess();
}

handleSignupSuccess() {
super.handleSubmitSuccess();
// 이후 추가 로직 작성
}
handleSignupFailure(message) {
super.handleSubmitFailure(message);
// 이후 추가 로직 작성
}
}

new signupForm().init();
3 changes: 3 additions & 0 deletions assets/scripts/utils/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const $ = (query) => document.querySelector(query);

export default $;
27 changes: 27 additions & 0 deletions assets/scripts/utils/validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { VALIDATION_VALUES } from "../constants/validationValues.js";
import { ERROR_MESSAGES } from "../constants/messages.js";

const emailRegex = /^[a-z0-9]+@[a-z]+\.[a-z]{2,}(\.[a-z]{2,})?$/;

export const validator = {
validateEmail(email) {
if (!email.length) return ERROR_MESSAGES.inputEmail;
if (!emailRegex.test(email)) return ERROR_MESSAGES.invalidEmail;
return "";
},
validatePassword(password) {
if (!password.length) return ERROR_MESSAGES.inputPassword;
if (password.length < VALIDATION_VALUES.passwordLength)
return ERROR_MESSAGES.inputValidLengthPassword;
return "";
},
validateNickname(nickname) {
if (!nickname.length) return ERROR_MESSAGES.inputNickname;
return "";
},

validatePasswordConfirm(password, confirmPassword) {
if (password !== confirmPassword) return ERROR_MESSAGES.passwordNotMatch;
return "";
},
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

파일전체적으로, 정규식을 통해서 값을 검증하는것, 얼리리턴 사용해주신것, 에러의 메세지를 상수값으로 미리 선언해두신것 모두 좋은것 같습니다.

Loading