Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Binary file added sprint1/images/google_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sprint1/images/join_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sprint1/images/kakao_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion sprint1/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>판다마켓켓</title>
<title>판다마켓</title>
<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="main.css">
</head>
Expand Down
50 changes: 50 additions & 0 deletions sprint1/join.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>판다마켓 회원가입</title>
<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="sub.css">
<script src="./js/input.js"></script>
</head>
<body>
<div class="join_box">
<h1 class="logo">
<img src="./images/join_logo.png" alt="판다마켓">
</h1>
<form action="join">
<div class="form_box">
<label for="email">이메일</label>
<input id="email" type="email" name="email" placeholder="[email protected]" required>
</div>
<p class="wraing">이메일을 입력해주세요</p>
<div class="form_box">
<label for="name">닉네임</label>
<input type="text" name="name">
</div>
<div class="form_box">
<label for="password">비밀번호</label>
<input id="password" type="password" name="password">
</div>
<p class="wraing">비밀번호를 입력해주세요</p>
<div class="form_box">
<label for="password">비밀번호 확인</label>
<input id="password2" type="password" name="password">
</div>
<p class="wraing">비밀번호를 입력해주세요</p>
<button id="login_btn" class="join_btn gray">회원가입</button>
</form>
<div class="login_banner">
<p>간편 로그인하기</p>
<a href="https://www.google.com/" class="google_login">
<img src="./images/google_icon.png" alt="구글 로그인하기">
</a>
<a href="https://www.kakaocorp.com/page/" class="kakao_login">
<img src="./images/kakao_icon.png" alt="카카오 로그인하기">
</a>
</div>
<p class="login_txt">이미 회원이신가요? <a href="login.html">로그인</a></p>
</div>
</body>
</html>
104 changes: 104 additions & 0 deletions sprint1/js/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
document.addEventListener("DOMContentLoaded", function () {
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 함수에서 너무 많은 일을 하고있어 가독성 측면에서 좋지않고, 유지보수가 힘들어질거예요.

기존의 이벤트 리스너 연결 방식은 그대로 유지하되, 콜백함수에서 모든 일을 처리하는게 아니라 함수가 하나의 일만 담당할수있게끔 분리해보는것부터 시작할까요?

  • 유효성 검사 수행
  • 유효성 검사 결과에 따른 UI 업데이트

const emailInput = document.getElementById("email");
const passWord = document.getElementById("password");
const passWord2 = document.getElementById("password2");
const loginBtn = document.getElementById("login_btn");
const wraingAll = document.querySelectorAll(".wraing");

emailInput.addEventListener("focusout",function(){

let par = emailInput.parentNode
let wraing = par.nextElementSibling;
let emailValue = emailInput.value
let emailVaild = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

if(emailValue.length === 0){
emailInput.classList.add("on");
loginBtn.classList.add("gray");
wraing.classList.add("on");
wraing.textContent = "이메일을 입력해주세요.";
}else if(!emailVaild.test(emailValue)){
emailInput.classList.add("on");
loginBtn.classList.add("gray");
wraing.classList.add("on");
wraing.textContent = "잘못된 이메일 형식입니다.";
}else{
emailInput.classList.remove("on");
wraing.classList.remove("on");

let hasOnClass = false;
wraingAll.forEach(function (el) {
if (el.classList.contains("on")) {
hasOnClass = true;
}
});

if (!hasOnClass) {
loginBtn.classList.remove("gray");
}
}
Comment on lines +15 to +39
Copy link
Collaborator

Choose a reason for hiding this comment

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

if문 뎁스가 깊어요.
자세히 보면 유효성 평가를 if 조건문 체이닝으로 써주지않아도 UI의 상태에 대한 객체를 만들고, 유효성 검사를 수행한 결과를 업데이트하는 맵핑 구조로 만들어주면 코드의 흐름과 의도가 좀 더 명확해질것같아요.

예시를 보여드릴게요 :)

// 상태 관리 객체
const loginState = {
  email: {
    value: '',
    isValid: false,
    errors: {
      empty: false,
      invalid: false
    }
  },
  password: {
    value: '',
    isValid: false,
    errors: {
      empty: false,
      invalid: false
    }
  },
  isPasswordVisible: false,
  isFormValid: false
};

Copy link
Collaborator

Choose a reason for hiding this comment

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

function validateForm() {
  // 이메일 검증
  const emailValue = emailInput.value.trim();
  loginState.email.errors.empty = emailValue === '';
  loginState.email.errors.invalid = !loginState.email.errors.empty && !isValidEmail(emailValue);
  loginState.email.isValid = !loginState.email.errors.empty && !loginState.email.errors.invalid;

  // 비밀번호 검증
  const passwordValue = passwordInput.value;
  loginState.password.errors.empty = passwordValue === '';
  loginState.password.errors.invalid = !loginState.password.errors.empty && passwordValue.length < 8;
  loginState.password.isValid = !loginState.password.errors.empty && !loginState.password.errors.invalid;

  // 전체 폼 유효성
  loginState.isFormValid = loginState.email.isValid && loginState.password.isValid;

  // UI 업데이트
  updateUI();
  
  return loginState.isFormValid;
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

유효성 검사 수행의 경우에도 관련있는 로직끼리 모아주기위해 이런식으로 구조를 바꿔보면 어떨까요?
이 유효성 검사 관련 코드들은 프로그램 내부 여기저기서 활용될수있는 비즈니스 로직이다보니, 모듈화해서 재사용하는게 좋겠죠?

export const validators = {
  email: (value) => {
    if (!value) return { isValid: false, message: "이메일을 입력해주세요" };
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
      return { isValid: false, message: "올바른 이메일 형식이 아닙니다" };
    }
    return { isValid: true, message: "" };
  },

  password: (value) => {
    if (!value) return { isValid: false, message: "비밀번호를 입력해주세요" };
    if (value.length < 8) {
      return { isValid: false, message: "비밀번호는 8자 이상이어야 합니다" };
    }
    return { isValid: true, message: "" };
  },
};

Copy link
Collaborator

Choose a reason for hiding this comment

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

이렇게 구조를 바꿔보면 이런 장점이 생기겠죠?

  • 각 함수가 { isValid, message } 형태로 일관된 형태로 결과를 반환해 코드를 좀 더 예측이 쉽고 안정적인 흐름을 가질수있도록 만들수있습니다.
  • 객체를 통한 명확한 매핑을 사용해 조건문을 사용하는것보다 훨씬 코드의 의도를 명확히 드러낼 수 있습니다.
  • 각 함수가 독립적이어서 개별적인 동작을 테스트하기쉽고, 수정 및 확장에 용이합니다.
  • 재사용 가능한 로직을 쉽게 분리할 수 있습니다.


});

passWord.addEventListener("focusout",function(){
let loginBtn = document.getElementById("login_btn");
let par = passWord.parentNode
let wraing = par.nextElementSibling;
if(passWord.value.length == 0){
passWord.classList.add("on");
wraing.classList.add("on");
Comment on lines +47 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

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

이런식으로 UI를 업데이트하는것도, 매번 이런식으로 직접적으로 DOM에 접근해 조작하는것보다, 상태 관리 목적으로 쓰이는 객체의 값에 맞춰 DOM을 조작하는식으로 바꿔보면 추후에 코드를 수정하거나 확장할때 용이해지겠죠? :)

wraing.textContent = "비밀번호를 입력해주세요";
loginBtn.classList.add("gray");
}else if(passWord.value.length < 8){
passWord.classList.add("on");
wraing.classList.add("on");
wraing.textContent = "비밀번호를 8자 이상 입력해주세요.";
loginBtn.classList.add("gray");
}else{
passWord.classList.remove("on");
wraing.classList.remove("on");

let hasOnClass = false;
wraingAll.forEach(function (el) {
if (el.classList.contains("on")) {
hasOnClass = true;
}
});
if (!hasOnClass) {
loginBtn.classList.remove("gray");
}
}
});

passWord2.addEventListener("focusout",function(){
let loginBtn = document.getElementById("login_btn");
let par = passWord2.parentNode
let wraing = par.nextElementSibling;
if(passWord2.value.length == 0){
passWord2.classList.add("on");
wraing.classList.add("on");
wraing.textContent = "비밀번호를 입력해주세요";
loginBtn.classList.add("gray");
}else if(passWord.value == passWord2.value){
passWord2.classList.add("on");
wraing.classList.add("on");
wraing.textContent = "“비밀번호가 일치하지 않습니다.";
loginBtn.classList.add("gray");
}else{
passWord2.classList.remove("on");
wraing.classList.remove("on");

let hasOnClass = false;
wraingAll.forEach(function (el) {
if (el.classList.contains("on")) {
hasOnClass = true;
}
});
if (!hasOnClass) {
loginBtn.classList.remove("gray");
}
}
});


});
41 changes: 41 additions & 0 deletions sprint1/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>판다마켓 로그인</title>
<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="sub.css">
<script src="./js/input.js"></script>
</head>
<body>
<div class="join_box">
<h1 class="logo">
<img src="./images/join_logo.png" alt="판다마켓">
</h1>
<form action="join">
<div class="form_box">
<label for="email">이메일</label>
<input id="email" type="email" name="email" placeholder="[email protected]" required>
</div>
<p class="wraing">이메일을 입력해주세요</p>
<div class="form_box">
<label for="password">비밀번호</label>
<input id="password" type="password" name="password">
</div>
<p class="wraing">비밀번호를 입력해주세요</p>
<button id="login_btn" class="join_btn gray">로그인</button>
</form>
<div class="login_banner">
<p>간편 로그인하기</p>
<a href="https://www.google.com/" class="google_login">
<img src="./images/google_icon.png" alt="구글 로그인하기">
</a>
<a href="https://www.kakaocorp.com/page/" class="kakao_login">
<img src="./images/kakao_icon.png" alt="카카오 로그인하기">
</a>
</div>
<p class="login_txt">이미 회원이신가요? <a href="login.html">로그인</a></p>
</div>
</body>
</html>
Loading
Loading