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
157 changes: 157 additions & 0 deletions auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
document.addEventListener("DOMContentLoaded", () => {
const emailInput = document.getElementById("email");
const usernameInput = document.getElementById("username");
const passwordInput = document.getElementById("password");
const repeatInput = document.getElementById("password-repeat");

const emailError = document.getElementById("email-error");
const usernameError = document.getElementById("username-error");
const passwordError = document.getElementById("password-error");
const repeatError = document.getElementById('repeat-error');
const form = document.querySelector("form");
const loginButton = form.querySelector("button[type='submit']");

const isSignup = usernameInput && repeatInput;
Copy link
Collaborator

Choose a reason for hiding this comment

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

변수명을 isSignup => isSignupPage로 고쳐주시는게 좋을것같고,
usernameInput, repeatInput 두개 다 DOM에서 존재하는지 여부를 가지고 회원가입페이지인지 아닌지 판단하는 로직은 안정적이지 않을것같아요. 안정적이지않다고 생각한 이유는

  1. 로직이 DOM 구조에 의존적이예요.
    디자인이 변경되거나 다른 방식으로라도 마크업이 변경되면 로직이 깨질 수 있어요.

  2. 확장성 문제:
    새로운 필드가 추가되거나 제거될 때마다 로직을 수정해야해요.

따라서 해당 필드가 DOM내부에 있는지 여부보다는,
좀더 직관적이면서도 확실한 방식으로 현재 페이지가 회원가입 페이지인지 구분해줘야해요.

이런 방식은 어떨까요?
페이지 타입을 확인하는 함수를 만들고, 함수의 리턴값을 pageType을 반환받는것이 좀더 확실하고 안정적이겠죠?

/* 페이지 타입 확인 */
const getPageType = () => {
  // URL 경로나 현재 페이지의 특정 요소를 통해 판단
  const path = window.location.pathname;
  const isSignupPage = path.includes('signup') || 
                      document.querySelector('form[action*="signup"]') ||
                      document.querySelector('h1, h2').textContent.toLowerCase().includes('회원가입');
  
  return isSignupPage ? 'signup' : 'login';
};

const pageType = getPageType();
const isSignupPage = pageType === 'signup';


const togglePasswordIcon = document.getElementById("togglePassword");
const togglePasswordRepeatIcon = document.getElementById("togglePassword-repeat");

if (togglePasswordIcon && passwordInput) {
togglePasswordIcon.addEventListener("click", () => {
const isHidden = passwordInput.type === "password";

passwordInput.type = isHidden ? "text" : "password";

togglePasswordIcon.src = isHidden
? "images/btn_visibility.png"
: "images/btn_unvisibility.png";
});
}
if (togglePasswordRepeatIcon && repeatInput) {
togglePasswordRepeatIcon.addEventListener("click", () => {
const isHidden = repeatInput.type === "password";

repeatInput.type = isHidden ? "text" : "password";

togglePasswordRepeatIcon.src = isHidden
? "images/btn_visibility.png"
: "images/btn_unvisibility.png";
});
}

loginButton.disabled = true;
Comment on lines +16 to +42
Copy link
Collaborator

Choose a reason for hiding this comment

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

이런 로직도 위에서 드린 코멘트와 마찬가지로 DOM 종속적이네요!
마크업이 변경되면, 로직이 깨집니다.

아래 예시와 같이 토글과 관련된 값을 상수로 관리하고 data 속성을 추가해서 바꿔주는건 어떨까요?

/* 비밀번호 토글 관련 상수 */
const PASSWORD_TOGGLE_CONFIG = {
  icons: {
    visible: 'images/btn_visibility.png',
    hidden: 'images/btn_unvisibility.png'
  },
  selectors: {
    password: '[data-password-toggle]',
    icon: '[data-password-toggle-icon]'
  }
};

/* 비밀번호 토글 이벤트 핸들러 */
const handlePasswordToggle = (event) => {
  const toggleButton = event.currentTarget;
  const input = toggleButton.closest('.input-group').querySelector('input[type="password"], input[type="text"]');
  const isHidden = input.type === 'password';
  
  input.type = isHidden ? 'text' : 'password';
  toggleButton.src = isHidden ? PASSWORD_TOGGLE_CONFIG.icons.visible : PASSWORD_TOGGLE_CONFIG.icons.hidden;
};

그리고 토글 상태를 초기화하는 코드도 필요해지겠죠?

/* 비밀번호 토글 이벤트 초기화 */
const initPasswordToggle = () => {
  const toggleButtons = document.querySelectorAll(PASSWORD_TOGGLE_CONFIG.selectors.icon);
  toggleButtons.forEach(button => {
    button.addEventListener('click', handlePasswordToggle);
  });
};

// 페이지 로드 시 초기화
document.addEventListener('DOMContentLoaded', () => {
  initPasswordToggle();
  loginButton.disabled = true;
});

Copy link
Collaborator

Choose a reason for hiding this comment

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

코드의 흐름이 훨씬 예측가능하고, 의도가 명확해지고, 간결해지지않았나요?
코멘트 내용을 전부 반영하시기보다는 DOM 종속적으로 코드를 작성하는게 왜 안좋은지 그 이유와 리팩토링 목적정도만 참고해가셔도 좋습니다! 천천히 하나씩 다듬어봐요 :)


const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

const state = {
emailValid : false,
usernameValid: !!usernameInput,
passwordValid: false,
repeatValid: !!repeatInput
}

function validateEmail() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

함수 내부 body를 보면 유효성 검사의 대상만 바뀔뿐 똑같은 내용이 반복되고있습니다.
함수에 검사 대상과 관련된 인자를 전달해주고 같은 내용을 수행할수있게 리팩토링한다면 코드 중복을 크게 줄일수있겠죠?

Copy link
Collaborator

Choose a reason for hiding this comment

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

#133 (comment)
다른 분 PR에 드린 코멘트 예시 참고해보시면 좋을듯합니다! :)

const emailValue = emailInput.value.trim();
emailInput.classList.remove("invalid");

if(!emailValue) {
emailError.innerText = "이메일을 입력해주세요.";
emailInput.classList.add("invalid");
state.emailValid = false;
} else if (!emailRegex.test(emailValue)) {
emailError.innerText = "잘못된 이메일 형식입니다.";
emailInput.classList.add("invalid");
state.emailValid = false;
} else {
emailError.innerText = "";
state.emailValid = true;
}

updateButtonState();
}

function validateUsername() {
const usernameValue = usernameInput.value.trim();
usernameInput.classList.remove("invalid");

if(!usernameValue) {
usernameError.innerText = "닉네임을 입력해주세요.";
usernameInput.classList.add("invalid");
state.usernameValid = false;
} else {
usernameError.innerText = "";
state.usernameValid = true;
}

updateButtonState();

}

function validatePassword() {
const passwordValue = passwordInput.value.trim();
passwordInput.classList.remove("invalid");

if(!passwordValue) {
passwordError.innerText = "비밀번호를 입력해주세요.";
passwordInput.classList.add("invalid");
state.passwordValid = false;
} else if (passwordValue.length < 8) {
passwordError.innerText = "비밀번호를 8자 이상 입력해주세요.";
passwordInput.classList.add("invalid");
state.passwordValid = false;
} else {
passwordError.innerText = "";
state.passwordValid = true;
}

updateButtonState();
}

function validatePasswordRepeat() {
const repeatValue = repeatInput.value.trim();
const passwordValue = passwordInput.value.trim();
repeatInput.classList.remove("invalid");

if(repeatValue !== passwordValue) {
repeatError.innerText = "비밀번호가 일치하지 않습니다.";
repeatInput.classList.add("invalid");
state.repeatValid = false;
} else {
repeatError.innerText = "";
state.repeatValid = true;
}

updateButtonState();
}


function updateButtonState() {
if (isSignup) {
loginButton.disabled = !(state.emailValid && state.passwordValid && state.usernameValid && state.repeatValid);
} else {
loginButton.disabled = !(state.emailValid && state.passwordValid);
}
}

emailInput.addEventListener("focusout", validateEmail);
passwordInput.addEventListener("focusout", validatePassword);

if (usernameInput && repeatInput) {
usernameInput.addEventListener("focusout", validateUsername);
repeatInput.addEventListener("focusout", validatePasswordRepeat);
}


form.addEventListener('submit', (e) => {
e.preventDefault();
updateButtonState();

if (!loginButton.disabled) {
if(isSignup) {
window.location.href = "login.html";
} else {
window.location.href = "/items";
}
}
});
});
Comment on lines +128 to +157
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 함수에서 하는 일이 너무 많고, 네이밍도 모호해서 유지보수하기가 힘들것같아요.
함수가 단일 책임을 가질수있게끔 아래 예시를 기준으로 작업을 나눠보고 필요한 시점에 사용될수있게끔 구조를 바꿔보는건 어떨까요?

  • 이벤트 리스너 설정 및 초기화
  • 유효성 검사
  • UI 상태 업데이트

11 changes: 7 additions & 4 deletions login.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@
<div class="login-content">
<form>
<div>
<label for="login-email">이메일</label>
<input id="login-email" name="email" type="email" placeholder="이메일을 입력해주세요">
<label for="email">이메일</label>
<input id="email" name="email" type="email" placeholder="이메일을 입력해주세요">
<div id="email-error" class="error-message"></div>
</div>
<div>
<label for="login-password">비밀번호</label>
<label for="password">비밀번호</label>
<div class="password-container">
<input id="login-password" name="password" type="password" placeholder="비밀번호를 입력해주세요">
<input id="password" name="password" type="password" placeholder="비밀번호를 입력해주세요">
<img src="images/btn_unvisibility.png" id="togglePassword">
</div>
<div id="password-error" class="error-message"></div>
</div>
<button class="button auth-button" type="submit">
로그인
Expand All @@ -57,5 +59,6 @@
</div>
</div>
</div>
<script src="auth.js"></script>
</body>
</html>
9 changes: 7 additions & 2 deletions signup.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,28 @@
<div>
<label for="email">이메일</label>
<input id="email" name="email" type="email" placeholder="이메일을 입력해주세요">
<div id="email-error" class="error-message"></div>
</div>
<div>
<label for="username">닉네임</label>
<input id="username" name="username" placeholder="닉네임을 입력해주세요">
<div id="username-error" class="error-message"></div>
</div>
<div>
<label for="password">비밀번호</label>
<div class="password-container">
<input id="password" name="password" type="password" placeholder="비밀번호를 입력해주세요">
<img src="images/btn_unvisibility.png" id="togglePassword">
</div>
<div id="password-error" class="error-message"></div>
</div>
<div>
<label for="password-repeat">비밀번호 확인</label>
<div class="password-container">
<input id="password-repeat" name="password-repeat" type="password" placeholder="비밀번호를 다시 한 번 입력해주세요">
<img src="images/btn_visibility.png" id="togglePassword-repeat">
<img src="images/btn_unvisibility.png" id="togglePassword-repeat">
</div>
<div id="repeat-error" class="error-message"></div>
</div>
<button class="button auth-button" type="submit">
회원가입
Expand All @@ -51,7 +55,7 @@
<div class="login-easy">
간편 로그인 하기
<div class="social-login">
<a href="https://www.google.com/," target="_blank">
<a href="https://www.google.com/" target="_blank">
<img src="images/google.png" alt="google" width="42">
</a>
<a href="https://kakaocorp.com/page/" target="_blank">
Expand All @@ -67,5 +71,6 @@
</div>
</div>
</div>
<script src="auth.js"></script>
</body>
</html>
58 changes: 58 additions & 0 deletions styles/auth.css
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,62 @@
.link_to_signup a {
color: #3692FF;
text-decoration: underline;
}

input.invalid {
border-color: red;
}

.password-container input.invalid {
border-color: red;
}

.error-message {
color: red;
font-size: 14px;
margin-top: 4px;
}

@media (min-width: 375px) and (max-width: 767px) {

.auth-contents {
width: 100%;
height: 100%;
padding: 0 16px;
margin: 0 auto;
gap: 24px;
}

.auth-logo {
gap: 8px;
}

.auth-logo img {
width: 40px;
height: 40px;
}

.auth-logo a {
font-size: 45px;
}

.login-content {
width: 100%;
max-width: 400px;
margin: 0 auto;
}

form,
input,
.auth-button,
.other-login {
width: 100%;
max-width: 400px;
gap: 12px;
}

label,
input {
font-size: var(--text-sm);
}
}
36 changes: 36 additions & 0 deletions styles/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,40 @@ h1 {
font-weight: 700;
border-radius: 999px;
padding: 16px 124px;
}

@media (min-width: 375px) and (max-width: 767px) {
header {
padding: 0 16px;
}

footer {
padding: 32px 32px 64px 32px;
}

h1 {
font-size: 32px;
}

.pill-button {
font-size: 20px;
}
}

@media (min-width : 768px) and (max-width: 1199px) {
header {
padding: 0 24px;
}

footer {
padding: 32px 104px 64px 104px;
}

h1 {
font-size: var(--text-3xl);
}

.pill-button {
font-size: 20px;
}
}
Loading
Loading