Skip to content
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

[강성구] Sprint4 #110

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a08f7ff
fix: 메타 태그 수정 / 코드 리뷰 반영
L1m3Kun Jun 12, 2024
aacd04a
fix: 비밀번호 아이콘 위치 position 기준 수정
L1m3Kun Jun 12, 2024
b03d1c0
feat: 오류 메세지 입력창 (p태그) 추가
L1m3Kun Jun 12, 2024
5e37688
feat: 로그인 페이지 검증 로직 구현
L1m3Kun Jun 13, 2024
e155004
fix: 회원가입 페이지 비밀번호, 비밀번호 확인 아이콘 위치 position 기준 수정
L1m3Kun Jun 13, 2024
b6677d2
feat: 회원가입 페이지 오류 메세지 태그(p tag) 추가
L1m3Kun Jun 13, 2024
7ad917d
fix: 회원가입 페이지 비밀번호 숨기기/나타내기 아이콘 위치 오류 수정
L1m3Kun Jun 13, 2024
221c10c
fix: 회원가입 페이지 변수 이름 변경 signin -> signup
L1m3Kun Jun 13, 2024
97cebcf
feat: 회원가입 페이지 입력 검증 기능 추가
L1m3Kun Jun 13, 2024
393372b
feat: 로그인/회원가입 페이지 버튼 클릭 경로 설정
L1m3Kun Jun 13, 2024
43897bc
feat: 로그인/회원가입 페이지 비밀번호 숨기기/나타내기 버튼 기능 추가
L1m3Kun Jun 13, 2024
294f1a6
refactor: 로그인/회원가입 버튼 및 비밀번호 상태 class로 분리
L1m3Kun Jun 13, 2024
66e63da
chore: 변수명 오타 수정 vaild -> valid
L1m3Kun Jun 13, 2024
6654a3b
refactor: 입력 검증 로직 모듈화
L1m3Kun Jun 13, 2024
8d14f93
refactor: 패스워드 숨기기/나타내기 로직 모듈화
L1m3Kun Jun 13, 2024
003ab14
refactor: form 태그 핸들링(로그인/회원가입 버튼 기능) 모듈화
L1m3Kun Jun 13, 2024
e6b95cf
refactor: 버튼 활성화 기능 모듈화 및 선언 위치 변경
L1m3Kun Jun 13, 2024
0dca6d5
docs: README updated
L1m3Kun Jun 13, 2024
81bd955
docs: README 회원가입 페이지 이미지 추가
L1m3Kun Jun 13, 2024
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
76 changes: 41 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,54 @@
## 요구사항

### 기본

- [x] Netlify에 파일 배포가 아닌 포크한 Github 레포지토리로 연결합니다.
- [x] 피그마 디자인에 맞게 페이지를 만들어 주세요.
- [x] React와 같은 UI 라이브러리를 사용하지 않고 진행합니다.

### 공통
- [x] 브라우저에 현재 보이는 화면의 영역(viewport) 너비를 기준으로 분기되는 반응형 디자인을 적용합니다.
* PC: 1200px 이상
* Tablet: 768px 이상 ~ 1199px 이하
* Mobile: 375px 이상 ~ 767px 이하
* 375px 미만 사이즈의 디자인은 고려하지 않습니다

### 랜딩 페이지
- [x] Tablet 사이즈로 작아질 때 “판다마켓” 로고의 왼쪽에 여백 24px, “로그인” 버튼 오른쪽 여백 24px을 유지할 수 있도록 “판다마켓” 로고와 “로그인" 버튼의 간격이 가까워집니다.
- [x] Mobile 사이즈로 작아질 때 “판다마켓” 로고의 왼쪽에 여백 16px, “로그인” 버튼 오른쪽 여백 16px을 유지할 수 있도록 “판다마켓” 로고와 “로그인" 버튼의 간격이 가까워집니다.
- [x] 화면 영역이 줄어들면 “Privacy Policy”, “FAQ”, “codeit-2024”이 있는 영역과 SNS 아이콘들이 있는 영역의 간격이 줄어듭니다.
로그인, 회원가입 페이지 공통
- [x] Tablet 사이즈에서 내부 디자인은 PC사이즈와 동일합니다.
- [x] Mobile 사이즈에서 좌우 여백 16px 제외하고 내부 요소들이 너비를 모두 차지합니다.
- [x] Mobile 사이즈에서 내부 요소들의 너비는 기기의 너비가 커지는 만큼 커지지만 400px을 넘지 않습니다.

### 체크리스트 [심화]
- [x] 페이스북, 카카오톡, 디스코드, 트위터 등 SNS에서 Linkbrary 랜딩 페이지(“/”) 공유 시 좌측 예시와 같은 미리보기를 볼 수 있도록 랜딩 페이지 메타 태그를 설정해 주세요.
- [x] 미리보기에서 제목은 “판다 마켓”, 설명은 “일상의 모든 물건을 거래해보세요”로 설정합니다.
- [x] 주소와 이미지는 자유롭게 설정하세요
### 기본 요구사항
- Github에 PR(Pull Request)을 만들어서 미션을 제출합니다.
- 피그마 디자인에 맞게 페이지를 만들어 주세요.
- React와 같은 UI 라이브러리를 사용하지 않고 진행합니다.

### 체크리스트 [기본]
#### 로그인

- [x] 이메일 input에서 focus out 할 때, 값이 없을 경우 input에 빨강색 테두리와 아래에 “이메일을 입력해주세요.” 빨강색 에러 메세지를 보입니다.
- [x] 이메일 input에서 focus out 할 때, 이메일 형식에 맞지 않는 경우 input에 빨강색 테두리와 아래에 “잘못된 이메일 형식입니다” 빨강색 에러 메세지를 보입니다.
- [x] 비밀번호 input에서 focus out 할 때, 값이 없을 경우 아래에 “비밀번호를 입력해주세요.” 에러 메세지를 보입니다
- [x] 비밀번호 input에서 focus out 할 때, 값이 8자 미만일 경우 아래에 “비밀번호를 8자 이상 입력해주세요.” 에러 메세지를 보입니다.
- [x] input 에 빈 값이 있거나 에러 메세지가 있으면 ‘로그인’ 버튼은 비활성화 됩니다.
- [x] input 에 유효한 값을 입력하면 ‘로그인' 버튼이 활성화 됩니다.
- [x] 활성화된 ‘로그인’ 버튼을 누르면 “/items” 로 이동합니다

#### 회원가입

- [x] 이메일 input에서 focus out 할 때, 값이 없을 경우 input에 빨강색 테두리와 아래에 “이메일을 입력해주세요.” 빨강색 에러 메세지를 보입니다.
- [x] 이메일 input에서 focus out 할 때, 이메일 형식에 맞지 않는 경우 input에 빨강색 테두리와 아래에 “잘못된 이메일 형식입니다” 빨강색 에러 메세지를 보입니다.
- [x] 닉네임 input에서 focus out 할 때, 값이 없을 경우 input에 빨강색 테두리와 아래에 “닉네임을 입력해주세요.” 빨강색 에러 메세지를 보입니다.
- [x] 비밀번호 input에서 focus out 할 때, 값이 없을 경우 아래에 “비밀번호를 입력해주세요.” 에러 메세지를 보입니다
- [x] 비밀번호 input에서 focus out 할 때, 값이 8자 미만일 경우 아래에 “비밀번호를 8자 이상 입력해주세요.” 에러 메세지를 보입니다.
- [x] 비밀번호 input과 비밀번호 확인 input의 값이 다른 경우, 비밀번호 확인 input 아래에 “비밀번호가 일치하지 않습니다..” 에러 메세지를 보입니다.
- [x] input 에 빈 값이 있거나 에러 메세지가 있으면 ‘회원가입’ 버튼은 비활성화 됩니다.
- [x] input 에 유효한 값을 입력하면 ‘회원가입' 버튼이 활성화 됩니다.
- [x] 활성화된 ‘회원가입’ 버튼을 누르면 “/signup” 로 이동합니다

#### 체크리스트 [심화]
- [x] 눈 모양 아이콘 클릭시 비밀번호의 문자열이 보이기도 하고, 가려지기도 합니다.
- [x] 비밀번호의 문자열이 가려질 때는 눈 모양 아이콘에는 사선이 그어져있고, 비밀번호의 문자열이 보일 때는 사선이 없는 눈 모양 아이콘이 보이도록 합니다.


## 스크린샷
- ![랜딩 페이지 pc](./readmeSource/landing-pc_version.png)
- ![랜딩 페이지 tablet](./readmeSource/landing-tablet_version.png)
- ![랜딩 페이지 mobile](./readmeSource/landing-mobile_version.png)
![login Error1](./readmeSource/loginError1.png)
![login Error2](./readmeSource/loginError2.png)
![login Allowed](./readmeSource/loginAllowed.png)
![login visibility](./readmeSource/loginVisibility.png)

- ![랜딩 페이지 pc](./readmeSource/login-pc_version.png)
- ![랜딩 페이지 mobile](./readmeSource/login-mobile_version.png)

- ![랜딩 페이지 pc](./readmeSource/signin-pc_version.png)
- ![랜딩 페이지 mobile](./readmeSource/signin-mobile_version.png)
![login Error1](./readmeSource/loginError1.png)
![login Error2](./readmeSource/loginError2.png)
![login Allowed](./readmeSource/loginAllowed.png)
![login visibility](./readmeSource/loginVisibility.png)


## 사이트 주소
- https://storepanda.netlify.app

## 멘토에게
- 메타 태그 설정 시 공유 되는 이미지는 어떻게 설정하나요?
- 리팩토링을 나름대로 했는데 피드백 부탁드립니다!
- 보통은 어디까지 모듈화하는지 궁금합니다.

7 changes: 5 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
<!-- 링크 공유 설정 -->
<meta property="og:title" content="판다마켓" />
<meta property="og:description" content="일상의 모든 물건을 거래해보세요" />
<meta property="og:image" content="./source/images/logo/logo.png" />
<meta property="og:image" content="source/images/headline/Img_home_top.png" />
<meta property="og:url" content="https://storepanda.netlify.app" />

<meta property="twitter:title" content="판다마켓"/>
<meta property="twitter:description" content="일상의 모든 물건을 거래해보세요" />
<meta property="twitter:image" content="./source/images/logo/logo.png" />
<meta property="twitter:image" content="source/images/headline/Img_home_top.png" />
<meta property="twitter:url" content="https://storepanda.netlify.app" />
<meta property="twitter:card" content="summary" />

<link rel="apple-touch-icon" sizes="180x180" href="source/icons/favicon/apple-touch-icon.png" />
Expand Down
17 changes: 13 additions & 4 deletions login/login.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,35 @@ form input:focus {
border: var(--blue);
}

.password {
.input-wrap {
position: relative;
}

.hide-icon {
.error-message {
visibility: hidden;
margin: 0 0 0 1.6rem;
color: red;
font-size: 1.5rem;
font-weight: 600;
line-height: 1.79rem;
}

.visibility-icon {
position: absolute;
right: 1.6rem;
bottom: 1.6rem;
margin: auto 0.2rem;
cursor: pointer;
}

#hidden {
#visibility {
background-image: url(../source/icons/password/btn_visibility_on_24px.svg);
width: 2.4rem;
height: 2.4rem;
cursor: pointer;
}

#hidden:active {
#visibility.active {
background-image: url(../source/icons/password/btn_visibility_on_24pxshow.svg);
width: 2.4rem;
height: 2.4rem;
Expand Down
17 changes: 11 additions & 6 deletions login/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,23 @@
</header>
<main>
<article>
<form action="/index.html">
<form action="/items" id="login-form" method="POST">
<div class="email">
<label for="email">이메일</label>
<input type="email" name="email" placeholder="이메일을 입력해주세요">
<input type="email" id="email" name="email" placeholder="이메일을 입력해주세요">
<p class="error-message" id="email-error-message"></p>
</div>
<div class="password">
<label for="password">비밀번호</label>
<input type="password" id="password" name="password" placeholder="비밀번호를 입력해주세요">
<div class="hide-icon">
<div id="hidden"></div>
<div class="input-wrap">
<input type="password" id="password" name="password" placeholder="비밀번호를 입력해주세요">
<div class="visibility-icon">
<div id="visibility"></div>
</div>
</div>
<p class="error-message" id="password-error-message"></p>
</div>
<button class="login-btn">로그인</button>
<button class="login-btn" id="login-btn" disabled>로그인</button>
</form>
<div class="oauth-login">
<p>간편 로그인하기</p>
Expand All @@ -45,5 +49,6 @@
</div>
</article>
</main>
<script type="module" src="login.js"></script>
</body>
</html>
22 changes: 22 additions & 0 deletions login/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// import handleBtnActive from '../modules/handleBtnActive.js';
import handleError from '../modules/handleError.js';
import handleFormTag from '../modules/handleForm.js';
import handlePasswordVisiblity from '../modules/passwordVisibility.js';
import { loginValidationState } from '../modules/state.js';

const validation = new loginValidationState();

handleError({
inputType:'이메일',
btnName:'login',
validation,
});
handleError({
inputType:'비밀번호',
btnName:'login',
validation,
});

handlePasswordVisiblity();

handleFormTag('login','/items');
12 changes: 12 additions & 0 deletions modules/handleBtnActive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const handleBtnActive = (btnName, validation) => {
const btn = document.getElementById(`${btnName}-btn`);
if ( validation.isValid() ) {
btn.style.backgroundColor = 'var(--blue)';
btn.disabled = false;
} else {
btn.style.backgroundColor = 'var(--gray-400)';
btn.disabled = true;
}
};

export default handleBtnActive;
110 changes: 110 additions & 0 deletions modules/handleError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import handleBtnActive from './handleBtnActive.js';
import { isValidEmail, isValidNickname, isValidPassword, isValidPasswordConfirm } from './validations.js';

const handleErrorMessage = (inputType, errorType) => {
let errorMessage = '';
switch (errorType) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

swtich문을 사용할때 default 절도 꼭 함께 사용해주세요. 핸들링되지 않는 case에 대해서 에러메세지를 던지거나 다른 처리를 함으로써 좀 더 안정적인 코드가 됩니다.

Copy link
Collaborator Author

@L1m3Kun L1m3Kun Jun 15, 2024

Choose a reason for hiding this comment

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

리뷰 감사합니다 :)

다음부턴 default도 고려하여 구현하겠습니다 :)

case 'empty':
errorMessage = `${inputType}을 입력해주세요.`;
break;

case 'less length':
errorMessage = `비밀번호를 8자 이상 입력해주세요.`;
break;

case 'invalid':
errorMessage = `잘못된 이메일 형식입니다.`;
break;

case 'differ':
errorMessage = `비밀번호가 일치하지 않습니다.`;
break;
}
return errorMessage;
};


const handleType = (inputType) => {
switch (inputType) {
case '이메일':
return ['email', isValidEmail];
case '닉네임':
return ['nickname', isValidNickname];
case '비밀번호':
return ['password', isValidPassword];
case '비밀번호 확인':
return ['password-confirm', isValidPasswordConfirm];
}
}

const camelization = (typeText) => {
return typeText
.split('-')
.map(
(word, index) => index===0 ? word : word[0].toUpperCase()+word.slice(1))
.join('');
}


const handleError = ({
inputType,
btnName,
validation,
password=undefined,
}) => {
const [changedInputType, isValid] = handleType(inputType);
const inputTag = document.getElementById(changedInputType);
const errorTag = document.getElementById(`${changedInputType}-error-message`);

const handleFocusOutEvent = (event) => {
const validate = isValid(event.target.value);

if (password){
password.setContent(`${camelization(changedInputType)}Content`, event.target.value);
}
if (validate.isvalid) {
validation.setValid(changedInputType, true);
event.target.style.border = '0.1rem solid var(--blue)';
errorTag.style.visibility = 'hidden';
errorTag.textContent = '';
} else {
validation.setValid(changedInputType, false);
const errorMessage = handleErrorMessage(inputType, validate.message);
event.target.style.border = '0.1rem solid red';
errorTag.style.visibility='visible';
errorTag.textContent = errorMessage;
}
if (password && password.passwordConfirmContent!== ''){
const validate = isValidPasswordConfirm(password.passwordContent, password.passwordConfirmContent);
const passwordConfirmTag = document.getElementById("password-confirm");
const passwordConfirmErrorTag = document.getElementById("password-confirm-error-message");
if (validate.isvalid) {
validation.setValid("passwordConfirm", true);
passwordConfirmTag.style.border = "0.1rem solid var(--blue)";
passwordConfirmErrorTag.style.visibility = "hidden";
passwordConfirmErrorTag.textContent = '';
} else {
const errorMessage = handleErrorMessage("비밀번호 확인", validate.message);
validation.setValid("passwordConfirm", false);
passwordConfirmTag.style.border = '0.1rem solid red';
passwordConfirmErrorTag.style.visibility='visible';
passwordConfirmErrorTag.textContent = errorMessage;
}
}
handleBtnActive(btnName, validation)
}

const handleFocusInEvent = (event) => {
event.target.style.border = '0.1rem solid var(--blue)';
errorTag.style.visibility = 'hidden';
errorTag.textContent = '';
}


inputTag.addEventListener('focusout', handleFocusOutEvent);
inputTag.addEventListener('focusin', handleFocusInEvent);

return ;
}

export default handleError;
13 changes: 13 additions & 0 deletions modules/handleForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const goOtherPage = (src) => {
location.href = src;
};

const handleFormTag = (id, src) => {
const formTag = document.getElementById(`${id}-form`);
formTag.addEventListener('submit', (event) => {
event.preventDefault();
goOtherPage(src);
});
};

export default handleFormTag;
15 changes: 15 additions & 0 deletions modules/passwordVisibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const handlePasswordVisiblity = (tagName='') => {
const visibleTag = document.getElementById(tagName?`${tagName}-visibility`:'visibility');
const passwordTag = document.getElementById(tagName?'password-confirm':'password');

visibleTag.addEventListener('click', (event) => {
event.target.classList.toggle("active");

if (event.target.classList.contains("active")) {
passwordTag.type = 'text';
} else {
passwordTag.type = 'password';
}
});
}
export default handlePasswordVisiblity;
Loading
Loading