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

[오정민] Sprint4 #113

Conversation

ojm51
Copy link
Collaborator

@ojm51 ojm51 commented Jun 14, 2024

요구사항

기본

로그인

  • 이메일 input에서 focus out 할 때, 값이 없을 경우 input에 빨강색 테두리와 아래에 “이메일을 입력해주세요.” 빨강색 에러 메세지를 보입니다.
  • 이메일 input에서 focus out 할 때, 이메일 형식에 맞지 않는 경우 input에 빨강색 테두리와 아래에 “잘못된 이메일 형식입니다” 빨강색 에러 메세지를 보입니다.
  • 비밀번호 input에서 focus out 할 때, 값이 없을 경우 아래에 “비밀번호를 입력해주세요.” 에러 메세지를 보입니다
  • 비밀번호 input에서 focus out 할 때, 값이 8자 미만일 경우 아래에 “비밀번호를 8자 이상 입력해주세요.” 에러 메세지를 보입니다.
  • input 에 빈 값이 있거나 에러 메세지가 있으면 ‘로그인’ 버튼은 비활성화 됩니다.
  • input 에 유효한 값을 입력하면 ‘로그인' 버튼이 활성화 됩니다.
  • 활성화된 ‘로그인’ 버튼을 누르면 “/items” 로 이동합니다

회원가입

  • 이메일 input에서 focus out 할 때, 값이 없을 경우 input에 빨강색 테두리와 아래에 “이메일을 입력해주세요.” 빨강색 에러 메세지를 보입니다.
  • 이메일 input에서 focus out 할 때, 이메일 형식에 맞지 않는 경우 input에 빨강색 테두리와 아래에 “잘못된 이메일 형식입니다” 빨강색 에러 메세지를 보입니다.
  • 닉네임 input에서 focus out 할 때, 값이 없을 경우 input에 빨강색 테두리와 아래에 “닉네임을 입력해주세요.” 빨강색 에러 메세지를 보입니다.
  • 비밀번호 input에서 focus out 할 때, 값이 없을 경우 아래에 “비밀번호를 입력해주세요.” 에러 메세지를 보입니다
  • 비밀번호 input에서 focus out 할 때, 값이 8자 미만일 경우 아래에 “비밀번호를 8자 이상 입력해주세요.” 에러 메세지를 보입니다.
  • 비밀번호 input과 비밀번호 확인 input의 값이 다른 경우, 비밀번호 확인 input 아래에 “비밀번호가 일치하지 않습니다..” 에러 메세지를 보입니다.
  • input 에 빈 값이 있거나 에러 메세지가 있으면 ‘회원가입’ 버튼은 비활성화 됩니다.
  • input 에 유효한 값을 입력하면 ‘회원가입' 버튼이 활성화 됩니다.
  • 활성화된 ‘회원가입’ 버튼을 누르면 “/signup” 로 이동합니다

심화

  • 눈 모양 아이콘 클릭시 비밀번호의 문자열이 보이기도 하고, 가려지기도 합니다.
  • 비밀번호의 문자열이 가려질 때는 눈 모양 아이콘에는 사선이 그어져있고, 비밀번호의 문자열이 보일 때는 사선이 없는 눈 모양 아이콘이 보이도록 합니다.

주요 변경사항

  • 각 input들의 유효성을 검사하는 기능 추가
  • 모든 input이 유효할 때만 로그인/회원가입 버튼 활성화
  • 눈 모양 아이콘을 버튼 태그로 감싸 버튼이 눌릴 때마다 동작을 수행하게 함

스크린샷

  • 로그인 화면
    image
    image
    image

  • 회원가입 화면
    image
    image

멘토에게

  • 안녕하세요! part2 첫 PR입니다:)
  • 배포 링크입니다.
  • 로그인과 회원가입 자바스크립트 코드에 겹치는 부분이 많은데, 어떻게 모듈로 만들어야할 지 감이 오지 않아 일단 모두 따로 작성하였습니다. 어떤 식으로 공통 부분을 모듈로 만들어야 하는지 궁금합니다.
  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

@ojm51 ojm51 requested a review from Taero-Kim June 14, 2024 05:57
@ojm51 ojm51 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Jun 14, 2024
@Taero-Kim Taero-Kim assigned Taero-Kim and ojm51 and unassigned ojm51 Jun 14, 2024
@@ -0,0 +1,74 @@
const inputEmail = document.getElementById("input-email");
const errorMsgEmail = document.querySelector(".error-message.email");
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3;
맥락상 inputEmail, errorMsgEmail 변수명 보다는
emailInput, emailErrorMessage 와 같은 표현이 조금 더 적절할 것 같아요!

const errorMsgEmail = document.querySelector(".error-message.email");
let flagEmail = 0;
inputEmail.addEventListener("focusout", ({ target }) => {
const pattern = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-za-z0-9\-]+/;
Copy link
Collaborator

Choose a reason for hiding this comment

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

p4;
요런 이메일 정규식 패턴 같은 경우에는 일반적으로 불변하고, 여러곳에서 재사용하기 때문에
특정 함수 스코프 내에 가두기 보다는 따로 외부에서 상수로 정의하여 가져다 사용하면 더 좋을 것 같아요!

const VALID_EMAIL_PATTERN = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-za-z0-9\-]+/;

const inputEmail = document.getElementById("input-email");
const errorMsgEmail = document.querySelector(".error-message.email");
let flagEmail = 0;
inputEmail.addEventListener("focusout", ({ target }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

객체 구조 분해할당을 사용하셔서 target을 잘 가져오셨네요👍

flagEmail = 1;
}
checkInputs();
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3;
focusout 되는 시점에 유효성을 검사하고, 그에 따른 로그인 버튼 활성화를 잘 구현하신 것 같아요!

하지만, 아래 사항들을 고려하여, 조금 더 가독성과 유지보수성을 향상시킬 수 있을 것 같아요.

  1. addEventListener에 전달되는 함수는 별도로 분리해서 작성
    -> 기능상 차이는 없지만, 어떤 이벤트에 어떤 함수가 동작하는지 1차적으로 함수명으로 유추하기 용이할 것 같아요.

  2. 공통된 부분들을 별도의 함수로 정의
    -> 중복되는 부분들이 보이는 것 같아요. 예를들어 아래와 같은 부분이요!

    target.classList.add("error");
    errorMsgEmail.style.display = "block";

요런 부분들은 어떻게 하면 재사용할 수 있을지 고민을 해보고, 별도의 함수로 쪼개는 연습을 하면 좋을 것 같아요!

  1. 변수명을 더 직관적으로 작성하기
    -> 예를들어 flagEmail이라는 변수명 보다는 isEmailValid와 같은 직관적인 변수명이 더 읽기 좋을 것 같아요!

  2. 추가로 flag에 0, 1 값을 할당하는 대신 명확하게 불리언으로 표시하는게 좋을 것 같아요!

아래는 재구성한 예시입니다. 보시고, 이런 느낌으로도 작성할 수 있구나를 생각하시면서 password 관련한 함수도 한 번 개선해보시면 좋을 것 같아요!

// 이메일 값 검증 
const validateEmail = (emailValue = "") => {
  const validEmailPattern = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-za-z0-9\-]+/;

  if (emailValue === '') return {isValid: false, errorMessage: '이메일을 입력해주세요.'};
  if (!validEmailPattern.test(emailValue)) return {isValid: false, errorMessage: '잘못된 이메일 형식입니다.'};

  return {isValid: true, errorMessage: null};
}

// 보통 이벤트리스너 핸들러에 아래와 같이 handle을 붙이곤 합니다!
const handleFocusOutEmailInput = ({target}) => {
  const emailValue = target.value;
  const { isValid, errorMessage } = validateEmail(emailValue);

  isEmailValid = isValid;

  if (!isValid) {
    target.classList.add("error");
    errorMsgEmail.style.display = "block";
    errorMsgEmail.textContent = errorMessage;

    disableLoginButton();  --> 여기서는 이메일이 유효하지 않으니, checkInputs와 같이 모든 인풋 항목을 검사하지 않고 곧바로 로그인 버튼을 비활성화 할 수 있을 것 같아요!
    return;
  }

  target.classList.remove("error");
  errorMsgEmail.style.display = "none";
  handleLoginButtonActivate();
}

inputEmail.addEventListener("focusout", handleFocusOutEmailInput)


// 로그인 버튼 비활성화
const disableLoginButton = () => {
  loginBtn.disabled = true;
  loginBtn.classList.remove("enabled");
}

// 로그인 버튼 활성화
const activateLoginButton = () => {
  loginBtn.disabled = false;
  loginBtn.classList.add("enabled");
  loginBtn.addEventListener("click", () => {
    window.location.href = "../items/index.html";
  });
}


// 기존 checkInputs()
const handleLoginButtonActivate = () => {
  if (isEmailValid && isPasswordValid) {
    activateLoginButton();
    return;
  }

  disableLoginButton();
}

if (flagEmail && flagPassword) {
loginBtn.disabled = false;
loginBtn.classList.add("enabled");
loginBtn.addEventListener("click", () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

p4;
개인적인 의견으로는 애초에 로그인 버튼 자체에 disabled가 걸리면, 동작을 하지 않으니
로그인 버튼 자체에 기본적으로 클릭시 window.location.href = "../items/index.html"; 할 수 있도록 정의 해놓고

여기서 동적으로 addEventListener하는 부분을 제거해도 괜찮을 것 같아요!

"../asset/icon/btn_visibility_off.png";
flagVisibility = 0;
}
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3;
요 부분도 기능을 잘 구현하셨는데, 삼항 연산자와 변수 통합을 잘 활용하시면
조건문 없이도 더 깔끔한 코드로 개선이 가능할 것 같아요!

이 부분도 마찬가지로 리스너에 함수 본문을 다 작성하시는 대신, 별도의 함수로 선언한뒤 가져다가 사용하시는게 더 깔끔할 것 같아요!

let isPasswordVisible = false;   --> 요렇게 변수를 불리언으로 개선하면 주석에 표시하지 않고, 보기만 해도 명확할 것 같아요!

const handleClickTogglePasswordVisibilityButton = () => {
  const inputIcon = document.querySelector(".password-show-icon");
  const inputType = isPasswordVisible ? 'password' : 'text';
  const iconType = isPasswordVisible ? 'off' : 'on';

  passwordInput.type = inputType;
  inputIcon.src = `../asset/icon/btn_visibility_${iconType}.png`
  isPasswordVisible = !isPasswordVisible;  --> visible 버튼은 무조건 현재 상태의 반대만 될 것 같아요!
}

pwVisibilityIcon.addEventListener('click', handleClickTogglePasswordVisibilityButton)

@@ -0,0 +1,126 @@
const inputEmail = document.getElementById("input-email");
const errorMsgEmail = document.querySelector(".error-message.email");
let flagEmail = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3;
사실상 login에 있던 로직들과 signup에 있는 로직은 대부분 모두, 재사용이 가능할 것 같아요!

다만, �요렇게 flagEmail과 같이 해당 스크립트 내에서 초기화되는 변수들 때문에, 현재 재사용이 어려우실 것 같아요.
이런 이유들 때문에, 사실 저렇게 스크립트 내에서 전역변수로 선언하는 건 부작용이 많아요!

따라서 이렇게 flag 등의 전역변수로 접근하는 것보다는
해당 인풋들 요소 내에 적절한 어트리뷰트를 설정하여 해당 어트리뷰트를 조작하거나,
혹은 is-error is-valid 와 같은 클래스명을 통해 인풋들의 유효성 여부를 판단하는게 더 좋은 방법일 것 같아요!

ex)

기본 마크업
<input id="input-email" data-valid="false">

// 유효한 경우 어트리뷰트 변경
�email.setAttribute("data-valid", "true");

@Taero-Kim
Copy link
Collaborator

고생 많으셨어요, 정민님!
전반적으로 기능을 잘 구현하셨어요!
코드에서 이런저런 시도를 해보시려 했던 흔적이 느껴져요!

공통되는 부분들을 따로 모듈화하기 위해서는
먼저 함수를 더 작은 단위로 쪼개 보는게 좋아요!
쪼개는 과정에서 함수에 적절하게 인자를 전달해야하는 경우가 많이 생길거에요.
그런 다음 함수의 기존 내용들을 쪼갠 함수들로 대체해보세요.

그러면 분명히 공통적으로, 사용하기 애매한 부분들이 보일거에요.
공통적으로 사용하기 애매한 경우는, 대부분 해당 스크립트 파일 내에서 초기화되는 전역변수가 이유일거에요.
(정민님의 경우 flagEmail flagPassword 요런 부분)
요런 부분들은 전역변수를 사용하는 대신, 적절하게 요소의 속성이나 클래스명 등을 조작하는 방식으로 변경을 하나 하나 해보는게 좋을 것 같아요!

사실 정답은 없습니다! 앞으로 계속 여러 변수를 선언하고, 그것들을 함수로 재구성하는 과정을 반복하다 보면
정민님이 자주 불편함을 느끼는 지점들이 생길 것이고, 그때마다 이런 저런 방식으로 해결하다 보면
각 해결 방식의 장단점을 스스로 느끼실거에요! 그러니 지금 완벽하게 모듈화된 깔끔한 코드가 아니라 생각이 들더라도,
천천히 개선해 나가면 좋을 것 같아요!

이외에 코드를 보며 몇가지 개선하면 좋을 점에 대해서 간단한 리뷰들 남겼습니다!
제가 남긴 리뷰에 대한 질문이나 기타 질문이 있으시면 언제든 편하게 말씀해주세요!

+) 리뷰를 남긴 코멘트 위의 p(1~5); 마크의 의미는 아래와 같습니다! 참고하면 좋을 것 같아요!

p1; 꼭 반영해주세요 (Request changes)
리뷰어는 PR의 내용이 서비스에 중대한 오류를 발생할 수 있는 가능성을 잠재하고 있는 등 중대한 코드 수정이 반드시 필요하다고 판단되는 경우, P1 태그를 통해 리뷰 요청자에게 수정을 요청합니다. 리뷰 요청자는 p1 태그에 대해 리뷰어의 요청을 반영하거나, 반영할 수 없는 합리적인 의견을 통해 리뷰어를 설득할 수 있어야 합니다.

p2; 적극적으로 고려해주세요 (Request changes)
작성자는 P2에 대해 수용하거나 만약 수용할 수 없는 상황이라면 적합한 의견을 들어 토론할 것을 권장합니다.

p3; 웬만하면 반영해 주세요 (Comment)
작성자는 P3에 대해 수용하거나 만약 수용할 수 없는 상황이라면 반영할 수 없는 이유를 들어 설명하거나 다음에 반영할 계획을 명시적으로(JIRA 티켓 등으로) 표현할 것을 권장합니다. Request changes 가 아닌 Comment 와 함께 사용됩니다.

p4; 반영해도 좋고 넘어가도 좋습니다 (Approve)
작성자는 P4에 대해서는 아무런 의견을 달지 않고 무시해도 괜찮습니다. 해당 의견을 반영하는 게 좋을지 고민해 보는 정도면 충분합니다.

p5; 그냥 사소한 의견입니다 (Approve)
작성자는 P5에 대해 아무런 의견을 달지 않고 무시해도 괜찮습니다.

@Taero-Kim Taero-Kim merged commit 268881a into codeit-bootcamp-frontend:Basic-오정민 Jun 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants