diff --git a/.vscode/settings.json b/.vscode/settings.json
index 6b665aaa..97b4e259 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,9 @@
{
- "liveServer.settings.port": 5501
+ "liveServer.settings.port": 5501,
+ "github.copilot.enable": {
+ "*": false,
+ "plaintext": false,
+ "markdown": false,
+ "scminput": false
+ }
}
diff --git a/src/css/auth.css b/src/css/auth.css
index 391ebd82..d6789151 100644
--- a/src/css/auth.css
+++ b/src/css/auth.css
@@ -51,21 +51,36 @@ body {
font-style: normal;
font-weight: 400;
line-height: 26px;
+ width: 100%;
+}
+
+.auth__input.error {
+ border: 1px solid red;
+}
+
+.auth__error-message {
+ font-size: 14px;
+ color: red;
+}
+
+.auth__input.success {
+ border: 1px solid var(--blue);
}
-.auth__input:focus {
+.hidden {
+ display: none;
+}
+
+/* .auth__input:focus {
border: 2px solid #3692ff;
outline: none;
-}
+} */
.auth__visible {
- width: 24px;
- height: 24px;
position: absolute;
- right: 24px;
- top: 71%;
- transform: translateY(-50%);
+ right: 12px;
cursor: pointer;
+ top: 58px;
}
.auth__button {
@@ -88,6 +103,10 @@ body {
cursor: pointer;
}
+.auth__button:disabled {
+ background: var(--gray-400);
+}
+
.auth__social {
margin-top: 24px;
width: 100%;
@@ -155,4 +174,8 @@ body {
padding: 0 16px;
max-width: 400px;
}
+
+ .auth__label {
+ font-size: 14px;
+ }
}
diff --git a/src/css/index.css b/src/css/index.css
index 35748061..38e67c9a 100644
--- a/src/css/index.css
+++ b/src/css/index.css
@@ -317,6 +317,11 @@
.banner__container--bottom {
gap: 0;
}
+
+ .highlight:nth-child(3) .highlight__container {
+ flex-direction: column;
+ align-items: flex-end;
+ }
}
/* 태블릿 */
@@ -337,9 +342,12 @@
padding: 24px;
}
- .highlight:nth-child(3) .highlight__container {
- flex-direction: column;
- align-items: flex-end;
+ .highlight__title {
+ font-size: 32px;
+ }
+
+ .highlight__subtitle {
+ font-size: 18px;
}
.feature-content {
@@ -370,9 +378,12 @@
padding: 52px 15px;
}
- .highlight:nth-child(3) .highlight__container {
- flex-direction: column;
- align-items: flex-end;
+ .highlight__title {
+ font-size: 24px;
+ }
+
+ .highlight__subtitle {
+ font-size: 18px;
}
.br-highlight {
@@ -380,7 +391,7 @@
}
.footer {
- padding: 32px 100px;
+ padding: 32px;
}
.footer__section {
diff --git a/src/html/items.html b/src/html/items.html
index e69de29b..1ea20598 100644
--- a/src/html/items.html
+++ b/src/html/items.html
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/src/html/signup.html b/src/html/signup.html
index 6b7920e8..a4f85c01 100644
--- a/src/html/signup.html
+++ b/src/html/signup.html
@@ -7,6 +7,8 @@
+
+
@@ -30,6 +32,7 @@
placeholder="이메일을 입력해주세요"
required
/>
+
에러 메시지
@@ -42,6 +45,7 @@
placeholder="닉네임을 입력해주세요"
required
/>
+
에러 메시지
@@ -54,6 +58,7 @@
placeholder="비밀번호를 입력해주세요"
required
/>
+
에러 메시지
+
에러 메시지
-
+
diff --git a/src/js/formvalidation.js b/src/js/formvalidation.js
new file mode 100644
index 00000000..abfd587e
--- /dev/null
+++ b/src/js/formvalidation.js
@@ -0,0 +1,67 @@
+export const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+/* 에러 메시지 */
+export function showError(input, error, message) {
+ input.classList.remove('success');
+ input.classList.add('error');
+ error.textContent = message;
+ error.classList.remove('hidden');
+}
+
+/* 성공 메시지 */
+export function showSuccess(input, error) {
+ input.classList.add('success');
+ input.classList.remove('error');
+ error.classList.add('hidden');
+}
+
+/* 이메일 유효성 검사 */
+export function validationEmail(input, error) {
+ const value = input.value.trim();
+
+ if (value === '') {
+ showError(input, error, '이메일을 입력해주세요');
+ } else if (!emailRegex.test(value)) {
+ showError(input, error, '잘못된 이메일 형식입니다');
+ } else {
+ showSuccess(input, error);
+ }
+}
+
+/* 비밀번호 유효성 검사 */
+export function validationPassword(input, error) {
+ const value = input.value.trim();
+
+ if (value === '') {
+ showError(input, error, '비밀번호를 입력해주세요');
+ } else if (value.length < 8) {
+ showError(input, error, '비밀번호를 8자 이상 입력해주세요');
+ } else {
+ showSuccess(input, error);
+ }
+}
+
+/* 모든 입력값이 유효한지 확인 */
+export function inputsValid(input) {
+ for (let i = 0; i < input.length; i++) {
+ if (!input[i].classList.contains('success')) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* 버튼 활성화 */
+export function updateButtonState(input, button) {
+ button.disabled = !inputsValid(input);
+}
+
+/* 버튼 누르면 페이지 이동 */
+export function goToPage(input, button, url) {
+ button.addEventListener('click', (e) => {
+ e.preventDefault();
+ if (inputsValid(input)) {
+ window.location.href = url;
+ }
+ });
+}
diff --git a/src/js/login.js b/src/js/login.js
new file mode 100644
index 00000000..1acec93f
--- /dev/null
+++ b/src/js/login.js
@@ -0,0 +1,33 @@
+import {
+ validationEmail,
+ validationPassword,
+ updateButtonState,
+ goToPage,
+} from './formvalidation.js';
+const emailInput = document.getElementById('email');
+const pwInput = document.getElementById('password');
+const emailError = emailInput.nextElementSibling;
+const pwError = pwInput.nextElementSibling;
+const loginButton = document.querySelector('.auth__button');
+
+const inputs = [emailInput, pwInput];
+
+/* 이메일 검증 */
+function validateEmail() {
+ validationEmail(emailInput, emailError);
+ updateButtonState(inputs, loginButton);
+}
+
+/* 비밀번호 검증 */
+function validatePassword() {
+ validationPassword(pwInput, pwError);
+ updateButtonState(inputs, loginButton);
+}
+
+/* 페이지 이동 */
+goToPage(inputs, loginButton, '../html/items.html');
+
+emailInput.addEventListener('blur', validateEmail);
+emailInput.addEventListener('input', validateEmail);
+pwInput.addEventListener('blur', validatePassword);
+pwInput.addEventListener('input', validatePassword);
diff --git a/src/js/signup.js b/src/js/signup.js
new file mode 100644
index 00000000..f27c9a64
--- /dev/null
+++ b/src/js/signup.js
@@ -0,0 +1,72 @@
+import {
+ validationEmail,
+ validationPassword,
+ updateButtonState,
+ goToPage,
+ showError,
+ showSuccess,
+} from './formvalidation.js';
+const emailInput = document.getElementById('email');
+const nicknameInput = document.getElementById('nickname');
+const pwInput = document.getElementById('password');
+const pwcheckInput = document.getElementById('passwordcheck');
+
+const emailError = emailInput.nextElementSibling;
+const nicknameError = nicknameInput.nextElementSibling;
+const pwError = pwInput.nextElementSibling;
+const pwcheckError = pwcheckInput.nextElementSibling;
+
+const signupButton = document.querySelector('.auth__button');
+
+const inputs = [emailInput, nicknameInput, pwInput, pwcheckInput];
+
+/* 이메일 검증 */
+function validateEmail() {
+ validationEmail(emailInput, emailError);
+ updateButtonState(inputs, signupButton);
+}
+
+/* 비밀번호 검증 */
+function validatePassword() {
+ validationPassword(pwInput, pwError);
+ updateButtonState(inputs, signupButton);
+}
+
+/* 닉네임 유효성 검사 */
+function validateNickname() {
+ const value = nicknameInput.value.trim();
+
+ if (value === '') {
+ showError(nicknameInput, nicknameError, '닉네임을 입력해주세요');
+ } else {
+ showSuccess(nicknameInput, nicknameError);
+ }
+ updateButtonState(inputs, signupButton);
+}
+
+/* 비밀번호 확인 유효성 검사 */
+function validatePasswordcheck() {
+ const pwValue = pwInput.value.trim();
+ const pwcheckValue = pwcheckInput.value.trim();
+
+ if (pwcheckValue === '') {
+ showError(pwcheckInput, pwcheckError, '비밀번호를 다시 입력해주세요');
+ } else if (!(pwValue === pwcheckValue)) {
+ showError(pwcheckInput, pwcheckError, '비밀번호가 일치하지 않습니다.');
+ } else {
+ showSuccess(pwcheckInput, pwcheckError);
+ }
+ updateButtonState(inputs, signupButton);
+}
+
+/* 페이지 이동 */
+goToPage(inputs, signupButton, '../html/login.html');
+
+emailInput.addEventListener('blur', validateEmail);
+emailInput.addEventListener('input', validateEmail);
+nicknameInput.addEventListener('blur', validateNickname);
+nicknameInput.addEventListener('input', validateNickname);
+pwInput.addEventListener('blur', validatePassword);
+pwInput.addEventListener('input', validatePassword);
+pwcheckInput.addEventListener('blur', validatePasswordcheck);
+pwcheckInput.addEventListener('input', validatePasswordcheck);
diff --git a/src/js/toggle.js b/src/js/toggle.js
new file mode 100644
index 00000000..63e5eb53
--- /dev/null
+++ b/src/js/toggle.js
@@ -0,0 +1,19 @@
+const toggleButtons = document.querySelectorAll('.auth__visible');
+
+for (let i = 0; i < toggleButtons.length; i++) {
+ const toggleBtn = toggleButtons[i];
+ const wrapper = toggleBtn.parentElement;
+ const input = wrapper.querySelector('input');
+ const icon = toggleBtn.querySelector('img');
+ let isVisible = false;
+
+ toggleBtn.addEventListener('click', () => {
+ isVisible = !isVisible;
+
+ input.type = isVisible ? 'text' : 'password';
+ icon.src = isVisible
+ ? '../assets/images/visibility_on.svg'
+ : '../assets/images/visibility_off.svg';
+ icon.alt = isVisible ? '비밀번호 보기' : '비밀번호 숨김';
+ });
+}