-
Notifications
You must be signed in to change notification settings - Fork 39
[염휘건] Sprint4 #149
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
The head ref may contain hidden characters: "Basic-\uC5FC\uD718\uAC74-sprint4"
[염휘건] Sprint4 #149
Changes from all commits
47b9ca1
1997c7d
b8615bd
65b2bab
f8755d7
46e9486
285c58d
54b9281
e2011da
b9424e5
e246083
3015029
2026936
637381f
cd30e11
e481954
d775268
5f7207b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| // src/js/components/FormValidator.js | ||
| import { InputFieldHandler } from './InputFieldHandler.js'; | ||
| import { validateEmail } from '../validators/emailValidator.js'; | ||
| import { validatePassword } from '../validators/passwordValidator.js'; | ||
|
|
||
| /** | ||
| * FormValidator 클래스 | ||
| * - 로그인/회원가입 폼의 두 개 필드를 관리 | ||
| * - 버튼 활성 토글 | ||
| * - 제출 후 '/items'로 이동 | ||
| */ | ||
| export class FormValidator { | ||
| /** | ||
| * 필드 초기화 및 의존객체인 필드 핸들러를 생성합니다. | ||
| * @param {HTMLFormElement} formEl - 폼 요소 | ||
| */ | ||
| constructor(formEl) { | ||
| this.form = formEl; | ||
| this.submitBtn = this.form.querySelector('button[type=submit]'); | ||
|
|
||
| // 각 InputFieldHander 생성 | ||
| this.emailHandler = new InputFieldHandler( | ||
| this.form.querySelector('#email'), | ||
| validateEmail, | ||
| 'form-field__input--error', | ||
| 'form-field__error-message' | ||
| ); | ||
|
|
||
| this.passwordHandler = new InputFieldHandler( | ||
| this.form.querySelector('#password'), | ||
| validatePassword, | ||
| 'form-field__input--error', | ||
| 'form-field__error-message' | ||
| ); | ||
|
Comment on lines
+21
to
+34
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 클래스 문법을 사용하셨군요! constructor(formEl) {
this.form = formEl;
this.submitBtn = this.form.querySelector('button[type=submit]');
this._initializeFieldHandlers();
this._attachEvents();
this._updateButtonState();
} _initializeFieldHandlers() {
this.emailHandler = new InputFieldHandler(
this.form.querySelector('#email'),
validateEmail,
'form-field__input--error',
'form-field__error-message'
);
this.passwordHandler = new InputFieldHandler(
this.form.querySelector('#password'),
validatePassword,
'form-field__input--error',
'form-field__error-message'
);
}
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 바꿔보면 몇가지 장점이 생깁니다 :)
|
||
|
|
||
| this._attachEvents(); | ||
| this._updateButtonState(); | ||
| } | ||
|
|
||
| /** 이벤트 연결: | ||
| * input -> 버튼토글 | ||
| * submit -> 이동 | ||
| */ | ||
| _attachEvents() { | ||
| const inputs = [this.emailHandler.inputEl, this.passwordHandler.inputEl]; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 함수의 경우에 inputs를 함수 내부에서 변수를 만들어 관리하기보다는 필요한 입력 필드들을 매개변수로 받게끔 만들면 외부에서 의존성을 주입하게되니까 훨씬 변경에 유연한 함수를 만들수있을것같아요. |
||
|
|
||
| inputs.forEach((el) => { | ||
| el.addEventListener('input', () => this._updateButtonState()); | ||
| }); | ||
|
|
||
| this.form.addEventListener('submit', (e) => this._handleSubmit(e)); | ||
| } | ||
|
|
||
| /** 버튼 활성/비활성 상태 업데이트 */ | ||
| _updateButtonState() { | ||
| const validEmail = this.emailHandler.validate(); | ||
| const validPwd = this.passwordHandler.validate(); | ||
| this.submitBtn.disabled = !(validEmail && validPwd); | ||
| } | ||
|
|
||
| /** | ||
| * 폼 제출 핸들러 | ||
| * @param {SubmitEvent} event | ||
| */ | ||
| _handleSubmit(event) { | ||
| event.preventDefault(); | ||
| // 최종 검사 | ||
| if (!this.submitBtn.disabled) { | ||
| window.location.href = '/items'; | ||
| } | ||
| } | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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,68 @@ | ||
| // src/js/components/InputFieldHandler.js | ||
|
|
||
| /** | ||
| * 입력 필드를 관리합니다. | ||
| * - focusout 시 유효성 검사 | ||
| * - 에러 클래스 토글 | ||
| * - 에러 메시지 렌더링 | ||
| */ | ||
| export class InputFieldHandler { | ||
| /** | ||
| * @param {HTMLInputElement} inputEl 입력 요소 | ||
| * @param {function(string): {valid: boolean, message: string}} validateFn 유효성 검사 함수 | ||
| * ex) validateEmail, validatePassword | ||
| * @param {string} errorClass 에러 시 추가할 CSS 클래스 | ||
| * @param {string} errorMsgClass 에러 메시지 요소에 붙일 CSS 클래스 | ||
| */ | ||
| constructor(inputEl, validateFn, errorClass, errorMsgClass) { | ||
| this.inputEl = inputEl; | ||
| this.validateFn = validateFn; | ||
| this.errorClass = errorClass; | ||
| this.errorMsgClass = errorMsgClass; | ||
| this.formField = this.inputEl.closest('.form-field'); //가장 가까운 Form-field를 찾음 | ||
|
|
||
| this.inputEl.addEventListener('focusout', () => this.validate()); | ||
| } | ||
|
|
||
| /** | ||
| * 입력값 유효성을 검사합니다. | ||
| * @returns {boolean} 유효하면 true | ||
| */ | ||
| validate() { | ||
| const value = this.inputEl.value.trim(); | ||
| const { valid, message } = this.validateFn(value); | ||
|
|
||
| if (!valid) { | ||
| this._showError(message); | ||
| return false; | ||
| } | ||
|
|
||
| this._clearError(); | ||
| return true; | ||
| } | ||
|
|
||
| /** 에러 스타일 및 메시지 표시 */ | ||
| _showError(message) { | ||
| //에러 클래스 추가 | ||
| this.inputEl.classList.add(this.errorClass); | ||
| //에러 엘리멘트 없다면 생성 | ||
| let msgEl = this.formField.querySelector(`.${this.errorMsgClass}`); | ||
| if (!msgEl) { | ||
| msgEl = document.createElement('p'); | ||
| msgEl.className = this.errorMsgClass; | ||
| this.formField.appendChild(msgEl); | ||
| } | ||
| //텍스트 갱신 | ||
| msgEl.textContent = message; | ||
| } | ||
|
|
||
| /** 에러 스타일 및 메시지 제거 */ | ||
| _clearError() { | ||
| //css 제거 | ||
| this.inputEl.classList.remove(this.errorClass); | ||
| //요소 제거 | ||
| const msgEl = this.formField.querySelector(`.${this.errorMsgClass}`); | ||
| //에러 요소가 존재한다면 제거합니다. | ||
| if (msgEl) msgEl.remove(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /** | ||
| * PasswordToggle | ||
| * - 버튼 클릭으로 연관된 비밀번호 input의 type을 토글하고 | ||
| * 버튼의 아이콘 클래스를 온/오프 상태로 전환합니다. | ||
| */ | ||
| export class PasswordToggle { | ||
| /** | ||
| * @param {HTMLButtonElement} toggleButton - 눈 모양 버튼 요소 | ||
| */ | ||
| constructor(toggleButton) { | ||
| this.button = toggleButton; | ||
| // 토글 버튼 바로 위에 있는 input 요소를 찾습니다. | ||
| this.input = this.button | ||
| .closest('.form-field__group') | ||
| .querySelector('input[type="password"], input[type="text"]'); | ||
| this._attachEvent(); | ||
| //console.log('이벤트 적용'); | ||
| } | ||
| //이벤트를 적용함 | ||
| _attachEvent() { | ||
| this.button.addEventListener('click', () => this._toggleVisibility()); | ||
| } | ||
| //비밀번호와 아이콘을 토글합니다. | ||
| _toggleVisibility() { | ||
| //console.log('토글 클릭!', this.input.type); | ||
| if (this._isPasswordHidden()) { | ||
| this._showPassword(); | ||
| } else { | ||
| this._hidePassword(); | ||
| } | ||
| } | ||
|
|
||
| /** 현재 입력 타입이 'password'인지 확인 */ | ||
| _isPasswordHidden() { | ||
| return this.input.type === 'password'; | ||
| } | ||
|
|
||
| /** 비밀번호를 표시하고, 버튼 아이콘을 변경 */ | ||
| _showPassword() { | ||
| this.input.type = 'text'; | ||
| this.button.classList.add('form-field__toggle-password--visible'); | ||
| } | ||
|
|
||
| /** 비밀번호를 가리고, 버튼 아이콘을 원래대로 */ | ||
| _hidePassword() { | ||
| this.input.type = 'password'; | ||
| this.button.classList.remove('form-field__toggle-password--visible'); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FormValidator, InputFieldHandler의 경우 클래스 계층간의 위계가 명확하지않아 유지보수에 어려움이 생길것같아요.
이왕 클래스 문법을 사용하셨으니 클래스 상속을 활용해 더 체계적이고 객체지향적인 구조로 리팩토링 해볼까요? 즉, 먼저 기본이 되는 최상위 클래스인 FormField 클래스를 만들고, 이를 상속받는 InputTextField 클래스를 구현하는 방식은 어떨까요?