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
29 changes: 29 additions & 0 deletions html/global.css
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 global.css가 정말 유용하겠군요 !

훌륭합니다 ! 이제 서비스 전반적으로 사용되는 스타일은 global.css에 적용하면 되겠군요 ! 👍

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@font-face {
font-family: "ROKAF Sans";
src: url("font/ROKAF\ Sans\ Medium.otf") format(".otf");
}

:root {
--blue: #3692ff;
--lightblue: #cfe5ff;
--gray50: #f9fafb;
--gray100: #f3f4f6;
--gray200: #e5e7eb;
--gray400: #9ca3af;
--gray500: #6b7280;
--gray600: #4b5563;
--gray700: #374151;
--gray800: #1f2937;
--gray900: #111827;
}

* {
box-sizing: border-box;
}

a:link,
a:visited,
a:hover,
a:active {
text-decoration: none;
}
File renamed without changes
3 changes: 3 additions & 0 deletions html/img/pwVisibilityOn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 32 additions & 32 deletions html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,80 +20,80 @@

<body>
<header>
<nav>
<a href="/">
<img src="img/pandaface.svg" />
<span>판다마켓</span>
<nav class="header-nav">
<a class="header-logo-shopname" href="/">
<img class="header-logo" src="img/pandaface.svg" />
<span class="header-shopname">판다마켓</span>
</a>
<a href="/login">
<button>로그인</button>
<button class="header-btn">로그인</button>
</a>
</nav>
</header>
<main>
<div class="hero">
<div class="heromain">
<img src="img/Img_home_top.svg" />
<h1>일상의 모든 물건을 <br />거래해 보세요</h1>
<a href="/items"><button>구경하러 가기</button></a>
<img class="heromain-img" src="img/Img_home_top.svg" />
<h1 class="heromain-phrase">일상의 모든 물건을 <br />거래해 보세요</h1>
<a class="heromain-link" href="/items"><button class="heromain-btn">구경하러 가기</button></a>
</div>
</div>
<section class="hotitem">
<img class="box-img" src="img/Img_home_01.svg" />
<div class="box-txt">
<h2>Hot Item</h2>
<h3>인기 상품을 <br />확인해 보세요</h3>
<p>가장 HOT한 중고거래 물품을<br />판다 마켓에서 확인해 보세요</p>
<h2 class="box-txt-1">Hot Item</h2>
<h3 class="box-txt-2">인기 상품을 <br />확인해 보세요</h3>
<p class="box-txt-3">가장 HOT한 중고거래 물품을<br />판다 마켓에서 확인해 보세요</p>
</div>
</section>
<section class="search">
<img class="box-img" src="img/Img_home_02.svg" />
<div class="box-txt">
<h2>Search</h2>
<h3>구매를 원하는 <br />상품을 검색하세요</h3>
<p>구매하고 싶은 물품을 검색해서<br />쉽게 찾아보세요</p>
<h2 class="box-txt-1">Search</h2>
<h3 class="box-txt-2">구매를 원하는 <br />상품을 검색하세요</h3>
<p class="box-txt-3">구매하고 싶은 물품을 검색해서<br />쉽게 찾아보세요</p>
</div>
</section>
<section class="register">
<img class="box-img" src="img/Img_home_03.svg" />
<div class="box-txt">
<h2>Register</h2>
<h3>판매를 원하는 <br />상품을 등록하세요</h3>
<p>어떤 물건이든 판매하고 싶은 상품을<br />쉽게 등록하세요</p>
<h2 class="box-txt-1">Register</h2>
<h3 class="box-txt-2">판매를 원하는 <br />상품을 등록하세요</h3>
<p class="box-txt-3">어떤 물건이든 판매하고 싶은 상품을<br />쉽게 등록하세요</p>
</div>
</section>
<section class="bottom">
<div>
<h2>믿을 수 있는<br />판다마켓 중고 거래</h2>
<img src="img/Img_home_bottom.svg" />
<div class="bottom-wrap">
<h2 class="bottom-phrase">믿을 수 있는<br />판다마켓 중고 거래</h2>
<img class="bottom-img" src="img/Img_home_bottom.svg" />
</div>
</section>
<footer>
<div>
<div class="footer-wrap">
<p class="copyright">©codeit - 2024</p>
<div class="footer-nav">
<a href="/privacy">Privacy Policy</a>
<a href="/faq">FAQ</a>
<a class="footer-nav-a" href="/privacy">Privacy Policy</a>
<a class="footer-nav-a" href="/faq">FAQ</a>
</div>
<ul class="snsicons">
<li>
<ul class="footer-sns">
<li class="footer-sns-li">
<a href="https://www.facebook.com" target="_blank">
<img src="img/ic_facebook.svg" />
<img class="footer-sns-ico" src="img/ic_facebook.svg" />
</a>
</li>
<li>
<li class="footer-sns-li">
<a href="https://www.x.com" target="_blank">
<img src="img/ic_twitter.svg" />
<img class="footer-sns-ico" src="img/ic_twitter.svg" />
</a>
</li>
<li>
<li class="footer-sns-li">
<a href="https://www.youtube.com" target="_blank">
<img src="img/ic_youtube.svg" />
<img class="footer-sns-ico" src="img/ic_youtube.svg" />
</a>
</li>
<li>
<li class="footer-sns-li">
<a href="https://www.instagram.com" target="_blank">
<img src="img/ic_instagram.svg"
<img class="footer-sns-ico" src="img/ic_instagram.svg"
/></a>
</li>
</ul>
Expand Down
193 changes: 193 additions & 0 deletions html/js/functions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
function checkEmail() {
const emailInput = document.getElementsByClassName("sign-email")[0];
const emailError = document.getElementsByClassName("sign-email-error")[0];

emailInput.classList.remove("error");

if (!emailInput.value) {
emailInput.style.border = "1px solid red";
emailError.textContent = "이메일을 입력해주세요.";
} else if (!emailInput.checkValidity()) {
emailInput.style.border = "1px solid red";
emailError.textContent = "잘못된 이메일 형식입니다.";
} else {
emailInput.style.border = "";
emailError.textContent = "";
}
toggleLoginBtn();
toggleSignupBtn();
}
Comment on lines +1 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿굿 ! 하나의 이메일 유효성 검사와 관련된 함수를 작성하셨군요 👍

Copy link
Collaborator

@kiJu2 kiJu2 Dec 30, 2024

Choose a reason for hiding this comment

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

(심화/생각해보기/제안/선택) 단일 책임 원칙 적용하기

단일 책임 원칙?: 하나의 클래스(함수)는 하나의 기능 담당하여 하나의 책임을 수행하는데 집중되어야 있어야 한다는 의미이다.

현재 checkEmail 함수는:

  1. document를 통해 element를 받아오고 있습니다
  2. 엘리먼트를 변경하고 있습니다.
  3. 유효성 검사를 하고 있습니다.

하나의 함수에 여러 목적을 달성하고 있어요 !

다음과 같이 책임을 분리할 수 있습니다:

Suggested change
function checkEmail() {
const emailInput = document.getElementsByClassName("sign-email")[0];
const emailError = document.getElementsByClassName("sign-email-error")[0];
emailInput.classList.remove("error");
if (!emailInput.value) {
emailInput.style.border = "1px solid red";
emailError.textContent = "이메일을 입력해주세요.";
} else if (!emailInput.checkValidity()) {
emailInput.style.border = "1px solid red";
emailError.textContent = "잘못된 이메일 형식입니다.";
} else {
emailInput.style.border = "";
emailError.textContent = "";
}
toggleLoginBtn();
toggleSignupBtn();
}
function validateEmail(emailInput) {
if (!emailInput.value) {
return { isValid: false, error: "이메일을 입력해주세요." };
}
if (!emailInput.checkValidity()) {
return { isValid: false, error: "잘못된 이메일 형식입니다." };
}
return { isValid: true, error: "" };
}
function applyEmailFeedback(emailInput, emailError, validationResult) {
if (validationResult.isValid) {
emailInput.style.border = "";
emailError.textContent = "";
emailInput.classList.remove("error");
} else {
emailInput.style.border = "1px solid red";
emailError.textContent = validationResult.error;
emailInput.classList.add("error");
}
}
function toggleButtons() {
toggleLoginBtn();
toggleSignupBtn();
}
function checkEmail() {
const emailInput = document.getElementsByClassName("sign-email")[0];
const emailError = document.getElementsByClassName("sign-email-error")[0];
const validationResult = validateEmail(emailInput);
applyEmailFeedback(emailInput, emailError, validationResult);
toggleButtons();
}

Copy link
Collaborator

@kiJu2 kiJu2 Dec 30, 2024

Choose a reason for hiding this comment

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

(더 더 더 나아가서/심화/선택) 추가로 email 리소스에만 국한하지 않고 여러 필드도 적용해볼 수 있어요.

다음과 같이 스키마를 작성하여 관심사를 분리할 수 있습니다:

const schema = {
  email: {
    target: document.getElementsByClassName("sign-email")[0],
    validators: [
      { check: (v) => v.trim() !== "", error: "이메일을 입력해주세요." },
      { check: (v) => /\S+@\S+\.\S+/.test(v), error: "잘못된 이메일 형식입니다." },
    ],
  },
  password: {
    target: document.getElementsByClassName("sign-password")[0],
    validators: [
      { check: (v) => v.trim() !== "", error: "비밀번호를 입력해주세요." },
      { check: (v) => v.length >= 8, error: "비밀번호는 최소 8자 이상이어야 합니다." },
    ],
  },
};

스키마를 활용하여 각 리소스에 따라 유효성 검사를 하면 확장에 용이한 패턴이 됩니다 😊


function checkNickname() {
const nicknameInput = document.getElementsByClassName("sign-nickname")[0];
const nicknameError = document.getElementsByClassName(
"sign-nickname-error"
)[0];

nicknameInput.classList.remove("error");

if (!nicknameInput.value) {
nicknameInput.style.border = "1px solid red";
nicknameError.textContent = "닉네임을 입력해주세요.";
} else if (nicknameInput.value.length < 3) {
nicknameInput.style.border = "1px solid red";
nicknameError.textContent = "닉네임을 3자 이상 입력해주세요.";
} else {
nicknameInput.style.border = "";
nicknameError.textContent = "";
}
toggleLoginBtn();
toggleSignupBtn();
}

function checkPW() {
const pwInput = document.getElementsByClassName("sign-pw")[0];
const pwError = document.getElementsByClassName("sign-pw-error")[0];

pwInput.classList.remove("error");

if (!pwInput.value) {
pwInput.style.border = "1px solid red";
pwError.textContent = "비밀번호를 입력해주세요.";
} else if (pwInput.value.length < 8) {
pwInput.style.border = "1px solid red";
pwError.textContent = "비밀번호를 8자 이상 입력해주세요.";
} else {
pwInput.style.border = "";
pwError.textContent = "";
}
toggleLoginBtn();
}

function checkPWCheck() {
const pwInput = document.getElementsByClassName("sign-pw")[0];
const pwError = document.getElementsByClassName("sign-pw-error")[0];

pwInput.classList.remove("error");

if (!pwInput.value) {
pwInput.style.border = "1px solid red";
pwError.textContent = "비밀번호를 입력해주세요.";
} else if (pwInput.value.length < 8) {
pwInput.style.border = "1px solid red";
pwError.textContent = "비밀번호를 8자 이상 입력해주세요.";
} else {
pwInput.style.border = "";
pwError.textContent = "";
}

const pwcheckInput = document.getElementsByClassName("sign-pwcheck")[0];
const pwcheckError = document.getElementsByClassName("sign-pwcheck-error")[0];

pwcheckInput.classList.remove("error");

if (!pwcheckInput.value) {
pwcheckInput.style.border = "1px solid red";
pwcheckError.textContent = "비밀번호를 입력해주세요.";
} else if (pwcheckInput.value.length < 8) {
pwcheckInput.style.border = "1px solid red";
pwcheckError.textContent = "비밀번호를 8자 이상 입력해주세요.";
} else if (pwcheckInput.value !== pwInput.value) {
pwcheckInput.style.border = "1px solid red";
pwcheckError.textContent = "비밀번호가 일치하지 않습니다.";
} else {
pwcheckInput.style.border = "";
pwcheckError.textContent = "";
}

toggleLoginBtn();
toggleSignupBtn();
}

function toggleLoginBtn() {
const emailInput = document.getElementById("login-email");
const pwInput = document.getElementById("login-pw");
const submitBtn = document.getElementsByClassName("sign-box-submit")[0];

if (
emailInput.value &&
pwInput.value.length >= 8 &&
emailInput.checkValidity()
) {
submitBtn.disabled = false;
} else {
submitBtn.disabled = true;
}
}

function toggleSignupBtn() {
const emailInput = document.getElementById("signup-email");
const nicknameInput = document.getElementById("signup-nickname");
const pwInput = document.getElementById("signup-pw");
const pwcheckInput = document.getElementById("signup-pwcheck");
const submitBtn = document.getElementsByClassName("sign-box-submit")[0];

if (
emailInput.checkValidity() &&
nicknameInput.value.length >= 3 &&
pwInput.value.length >= 8 &&
pwcheckInput.value === pwInput.value
) {
submitBtn.disabled = false;
} else {
submitBtn.disabled = true;
}
Comment on lines +125 to +134
Copy link
Collaborator

Choose a reason for hiding this comment

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

다음과 같이 조건문 값을 그대로 사용할 수 있습니다:

Suggested change
if (
emailInput.checkValidity() &&
nicknameInput.value.length >= 3 &&
pwInput.value.length >= 8 &&
pwcheckInput.value === pwInput.value
) {
submitBtn.disabled = false;
} else {
submitBtn.disabled = true;
}
const isValid = emailInput.checkValidity() &&
nicknameInput.value.length >= 3 &&
pwInput.value.length >= 8 &&
pwcheckInput.value === pwInput.value
submitBtn.disabled = !isValid;

이렇게 하면 조건문이 어떤 의미를 가지는지 알 수 있기에 가독성이 좋아질 수 있습니다 😊

}

function login(event) {
event.preventDefault();
window.location.href = "/items";
}

function signup(event) {
event.preventDefault();
window.location.href = "/login";
}

function togglePWVisibility(status) {
let pw = document.getElementsByClassName("sign-pw")[0];
const off = document.getElementsByClassName("pw-eye-icon-off")[0];
const on = document.getElementsByClassName("pw-eye-icon-on")[0];

if (status === "off") {
pw.type = "text";
off.style.display = "none";
on.style.display = "block";
} else {
pw.type = "password";
off.style.display = "block";
on.style.display = "none";
}
}

function togglePWCheckVisibility(status) {
let pw1 = document.getElementById("signup-pw");
let pw2 = document.getElementById("signup-pwcheck");
const imgOff1 = document.getElementsByClassName("pw-eye-icon-off")[0];
const imgOff2 = document.getElementsByClassName("pw-eye-icon-off")[1];
const imgOn1 = document.getElementsByClassName("pw-eye-icon-on")[0];
const imgOn2 = document.getElementsByClassName("pw-eye-icon-on")[1];

switch (status) {
case "off1":
pw1.type = "text";
imgOff1.style.display = "none";
imgOn1.style.display = "block";
break;
case "off2":
pw2.type = "text";
imgOff2.style.display = "none";
imgOn2.style.display = "block";
break;
case "on1":
pw1.type = "password";
imgOff1.style.display = "block";
imgOn1.style.display = "none";
break;
case "on2":
pw2.type = "password";
imgOff2.style.display = "block";
imgOn2.style.display = "none";
break;
}
}
Loading
Loading