Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
eb18053
💡Refactor: 폰트 유틸리티 제거
HarrySeop Mar 21, 2025
c8db0a2
💡Refactor: 전체적인 css 폴더 구조 변경
HarrySeop Mar 21, 2025
4c58a02
✨Feat: 체크리스트 심화 구현
HarrySeop Mar 21, 2025
0f4b199
💡Refactor: 회원가입, 로그인 스타일 통합 및 수정
HarrySeop Mar 21, 2025
8f290b3
💡Refactor: sns 로고 담당 태그 변경
HarrySeop Mar 21, 2025
e9804bd
♻️Chore: 헤더 로고 변경
HarrySeop Mar 21, 2025
33104fd
🔥Delete: 사용하지 않는 파일 제거
HarrySeop Mar 21, 2025
cbea560
♻️Chore: rem 기준 변경
HarrySeop Mar 21, 2025
207b4fb
💄Style: auth파트 반응형 구현
HarrySeop Mar 21, 2025
30061c7
♻️Chore: 파일명 통일 및 폴더 구조도 수정
HarrySeop Mar 21, 2025
f6d7d01
♻️Chore: banner.css 단위 변경
HarrySeop Mar 21, 2025
ae78424
♻️Chore: info.css 단위 변경
HarrySeop Mar 21, 2025
2df8348
♻️Chore: footer.css 단위 변경
HarrySeop Mar 21, 2025
a272a02
♻️Chore: header.css 단위 변경
HarrySeop Mar 21, 2025
f3d9da8
💄Style: home 화면 반응형 구현(테블릿)
HarrySeop Mar 21, 2025
7d3772e
💄Style: home 화면 반응형 구현(모바일)
HarrySeop Mar 21, 2025
b6d53c5
💄Style: home 화면 반응형 구현(모바일)
HarrySeop Mar 21, 2025
c742e2e
🔥Delete: 사용하지 않는 파일&코드 제거
HarrySeop Mar 21, 2025
5d79ad1
컨플릭트로 인한 머지
HarrySeop Mar 21, 2025
7d15f89
✨Feat: 화면보호기 구현
HarrySeop Mar 21, 2025
1d18116
♻️Chore: 프리뷰 이미지 경로 변경
HarrySeop Mar 21, 2025
5f39d9b
✨Feat: 미션4 시작을 위한 기본 구조도 생성
HarrySeop Mar 22, 2025
3782006
💄Style: assets 추가
HarrySeop Mar 22, 2025
3331c60
✨Feat: 메세지와 정규식을 상수로 구현
HarrySeop Mar 22, 2025
9052253
✨Feat: 각각의 검사를 담당하는 validation 구현
HarrySeop Mar 22, 2025
9d79bba
✨Feat: 로그인 페이지 유효성 검사 구현
HarrySeop Mar 22, 2025
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
3 changes: 3 additions & 0 deletions assets/icons/icon_visible_eye.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions constants/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const EMAIL_VALIDATION = Object.freeze({
invalid_email: '잘못된 이메일 형식입니다.',
required_email: '이메일을 입력해주세요.',
});
Comment on lines +1 to +4
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
EMAIL_VALIDATION 이름보다 EMAIL_ERROR_MESSAGE와 같이 에러문구 객체라는 것을 알 수 있는 이름이 더 좋을 것 같아요~


export const NICKNAME_VALIDATION = Object.freeze({
required_nickname: '닉네임을 입력해주세요.',
});

export const PASSWORD_VALIDATION = Object.freeze({
required_password: '비밀번호를 입력해주세요.',
invalid_password: '비밀번호를 8자 이상 입력해주세요.',
});

export const CONFIRM_PASSWORD_VALIDATION = Object.freeze({
required_confirm_password: '비밀번호를 다시 입력해주세요.',
not_matched_password: '비밀번호가 일치하지 않습니다.',
});
5 changes: 5 additions & 0 deletions constants/symbol.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const SYMBOL = Object.freeze({
email_validate_regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
password_min_length: 8,
empty_message: '',
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
SYMBOL이라는 객체명은 너무 많은 것을 의미해서 해당 값들의 의미를 잘 전달하지 못하는 것 같습니다.
해당 객체는 이메일 유효성 검사 정규식, 비밀번호 최소 길이 등을 포함하고 있으므로 이를 알 수 있는 이름으로 변경하시는 것을 추천드려요!

4 changes: 3 additions & 1 deletion login.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
</div>
</div>

<button type="submit" class="auth__button">로그인</button>
<button type="submit" class="auth__button" disabled>로그인</button>
</form>

<div class="auth__social">
Expand Down Expand Up @@ -68,5 +68,7 @@
375px 이상의 화면에서 이용해 주세요.
</div>
</div>

<script type="module" src="./scripts/login_validation.js"></script>
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
script 태그는 상단에 있는게 구조 파악측면에서 유리하다고 생각해서 큰 이유가 없다면 상단 head 태그에 두시는 것을 추천드려요~
특히 타입이 모듈인 스크립트는 defer 속성을 자동으로 가지기 때문에 따로 하단에 배치하실 이유가 없습니다~

</body>
</html>
74 changes: 74 additions & 0 deletions scripts/login_validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { validateEmail, validatePassword } from './validation/validation.js';

document.addEventListener('DOMContentLoaded', () => {
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
const loginButton = document.querySelector('.auth__button');
const loginForm = document.querySelector('form');
Comment on lines +6 to +7
Copy link
Collaborator

Choose a reason for hiding this comment

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

💬 여담
제 생각에는 파일명을 통해 login 관련이라는 것을 알 수 있으므로 변수명에서 login을 빼도 될 것 같아요.

Suggested change
const loginButton = document.querySelector('.auth__button');
const loginForm = document.querySelector('form');
const submitButton = document.querySelector('.auth__button');
const form = document.querySelector('form');


const showError = (input, message) => {
const wrapper = input.closest('.auth__input');
wrapper.classList.add('auth__input-error');

let errorMessage = wrapper.querySelector('.auth__error-message');
if (!errorMessage) {
errorMessage = document.createElement('span');
errorMessage.className = 'auth__error-message';
wrapper.appendChild(errorMessage);
}
errorMessage.innerText = message;
};

const hideError = input => {
const wrapper = input.closest('.auth__input');
wrapper.classList.remove('auth__input-error');

const errorMessage = wrapper.querySelector('.auth__error-message');
if (errorMessage) {
errorMessage.remove();
}
Comment on lines +26 to +29
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
해당 코드에서는 동적으로 태그를 삽입하면서 에러 메시지를 렌더링하는 방식입니다.
이렇게되면 계속적으로 DOM 조작을 해야하므로 error message가 들어갈 태그를 미리 만들어주고 에러에 따라 innerText만 변경하는 방식으로 수정하시는 것을 추천드려요.

};

const updateButtonState = () => {
const emailValid = !validateEmail(emailInput.value.trim());
const passwordValid = !validatePassword(passwordInput.value.trim());
loginButton.disabled = !(emailValid && passwordValid);
};

const validateEmailInput = () => {
const email = emailInput.value.trim();
hideError(emailInput);

const error = validateEmail(email);
if (error) {
showError(emailInput, error);
}

updateButtonState();
};
Comment on lines +38 to +48
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
해당 함수에 따르면 무조건 hideError 함수가 실행됩니다. showError 함수를 보면 에러메시지를 보여줄 태그가 존재할 경우 재활용을 하기 때문에 hideError도 필요시에만 호출하는 것이 성능과 가독성 측면에서도 좋을 것 같습니다.

Suggested change
const validateEmailInput = () => {
const email = emailInput.value.trim();
hideError(emailInput);
const error = validateEmail(email);
if (error) {
showError(emailInput, error);
}
updateButtonState();
};
const validateEmailInput = () => {
const error = validateEmail(emailInput.value.trim());
if (error) {
showError(emailInput, error);
} else {
hideError(emailInput);
}
updateButtonState();
};


const validatePasswordInput = () => {
const password = passwordInput.value.trim();
hideError(passwordInput);

const error = validatePassword(password);
if (error) {
showError(passwordInput, error);
}

updateButtonState();
};

emailInput.addEventListener('blur', validateEmailInput);
passwordInput.addEventListener('blur', validatePasswordInput);

loginForm.addEventListener('submit', e => {
e.preventDefault();
const emailError = validateEmail(emailInput.value.trim());
const passwordError = validatePassword(passwordInput.value.trim());

if (!emailError && !passwordError) {
window.location.href = '/items.html';
}
Comment on lines +67 to +72
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
폼의 값들이 유효하지 않으면 disabled 속성으로 인해 해당 이벤트가 발생하지 않으므로 여기서 유효성 검사를 할 필요가 없어보여요.

Suggested change
const emailError = validateEmail(emailInput.value.trim());
const passwordError = validatePassword(passwordInput.value.trim());
if (!emailError && !passwordError) {
window.location.href = '/items.html';
}
window.location.href = '/items.html';

});
});
60 changes: 60 additions & 0 deletions scripts/validation/validation.js
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 칭찬
확인할 수는 없지만 회원가입과 로그인에서 공용 로직을 같이 사용하기 위해 따로 빼주신 것 같아요!
중복을 줄이시고 주석도 자세히 달아두신 것 좋습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
EMAIL_VALIDATION,
NICKNAME_VALIDATION,
PASSWORD_VALIDATION,
CONFIRM_PASSWORD_VALIDATION,
} from '../../constants/message.js';

import { SYMBOL } from '../../constants/symbol.js';

/**
* 이메일 유효성 검사
* @param {string} email - 입력된 이메일
* @returns {string} - 에러 메시지 또는 빈 문자열
*/
export const validateEmail = email => {
if (!email) {
return EMAIL_VALIDATION.required_email;
}
return !SYMBOL.email_validate_regex.test(email) ? EMAIL_VALIDATION.invalid_email : SYMBOL.empty_message;
};

/**
* 닉네임 유효성 검사
* @param {string} nickname - 입력된 닉네임
* @returns {string} - 에러 메시지 또는 빈 문자열
*/
export const validateNickname = nickname => {
return !nickname ? NICKNAME_VALIDATION.required_nickname : SYMBOL.empty_message;
};

/**
* 비밀번호 유효성 검사
* @param {string} password - 입력된 비밀번호
* @returns {string} - 에러 메시지 또는 빈 문자열
*/
export const validatePassword = password => {
if (!password) {
return PASSWORD_VALIDATION.required_password;
}
return password.length < SYMBOL.password_min_length ? PASSWORD_VALIDATION.invalid_password : SYMBOL.empty_message;
};

/**
* 비밀번호 확인 유효성 검사
* @param {string} password - 비밀번호
* @param {string} confirmPassword - 비밀번호 확인 값
* @returns {string} - 에러 메시지 또는 빈 문자열
*/
export const validatePasswordConfirm = (password, confirmPassword) => {
if (!confirmPassword) {
return CONFIRM_PASSWORD_VALIDATION.required_confirm_password;
}

// 비밀번호와 같더라도 비밀번호 조건이 통과되어야 함
if (password.length < SYMBOL.password_min_length) {
return PASSWORD_VALIDATION.invalid_password;
}

return password !== confirmPassword ? CONFIRM_PASSWORD_VALIDATION.not_matched_password : SYMBOL.empty_message;
};
21 changes: 20 additions & 1 deletion styles/auth/auth.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ form {
display: flex;
flex-direction: column;
text-align: left;
gap: 1.6rem;
gap: 0.8rem;
}

.auth__input label {
Expand Down Expand Up @@ -87,6 +87,13 @@ form {
justify-content: center;
}

.auth__button:disabled {
background-color: var(--disable-400);
color: var(--secondary-100);
cursor: not-allowed;
pointer-events: none;
}

.auth__button:hover {
background-color: var(--primary-200);
}
Expand Down Expand Up @@ -137,3 +144,15 @@ form {
font-weight: 500;
text-decoration: underline;
}

.auth__input-error input {
border: 1px solid var(--error-500);
}

.auth__error-message {
font-size: 1.4rem;
line-height: 2.4rem;
font-weight: 600;
color: var(--error-500);
margin-left: 1.6rem;
}
Loading