08.23 - 08.27
정규표현식으로 아이디와 비밀번호의 유효성을 검증하는 기능이다. 유효할 시 테두리가 초록색으로 경된다.
비밀번호 hide-show 기능을 구현했다.
플렉스와 미디어쿼리로 반응형을 고려했다.
좋아요 버튼을 html과 css만으로 만들었다.
반응형으로 만들었고, 특정 스크린 크기에서는 세로로 플렉스의 배열이 바뀌도록 설정하고 태그의 순서를 바꿨다. 반응형은 리팩토링 때 추가했기 때문에 태그의 순서를 전부 바꾸는 작업 대신 이미지를 추가해 display: none으로 설정해놓고 미디어 쿼리로 display: inline-block이 되도록 설정했다.
댓글 추가와 삭제 기능을 구현했다. 리팩토링을 마치고 테스트 하던 중 input에 아무 것도 입력하지 않아도 포스팅 되는 것을 발견했는데, 이 부분은 코드를 수정해야 한다.
<form class="loginForm">
<input class="box" id="user" type="text" placeholder="전화번호, 사용자 이름 또는 이메일" required>
<div class="pwdBox">
<input class="box" id="pwd" type="password" placeholder="비밀번호" required>
<div class="icon">
<i class="far fa-eye" id="eye"></i>
</div>
</div>
<button class="box loginBtn">로그인</button>
<a class="forgetPwd" href="">비밀번호를 잊으셨나요?</a>
</form>
</section>
form은 입력 양식 전체를 감싸는 태그다. 로그인 창, 회원가입 폼 등이 해당된다. 제출을 하면 백엔드 서버에 전달되게 되는데, 백엔드 코드와 함께 사용하기 위해 name, action, method 등의 속성들이 사용된다. form은 추상적 태그이며, 입력을 위해 input 등의 태그를 사용한다.
<select>
<option>짜장면</option>
<option>짬뽕</option>
<option>탕수육</option>
<select>
<legend>
를 사용하면 fieldset 요소의 캡션을 정의할 수 있다.<form>
<fieldset>
<legend>개인정보</legend>
<label>이름</label>
<input type="text">
<label>이메일</label>
<input type="email">
</fieldset>
</form>
label 태그는 UI 요소의 라벨을 정의할 때 사용하며, 폼의 양식에 이름을 붙이는 태그이다. for 속성을 사용하여 다른 요소와 결합할 수 있다는 것이 특징이다. for 속성 값은 결합하고자 하는 요소의 id 속성값과 같아야 한다. 또한 label 요소를 결합하자 하는 요소 내부에 두면 for을 사용하지 않고 해당 요소와 결합할 수 있다. 사용자가 마우스로 라벨의 텍스트를 클릭할 경우 label요소와 연결된 요소를 곧바로 선택할 수 있다.
//Text를 클릭하면 input 작성칸이 선택된다.
<label for="a">Text</label>
<input type="text" id="a">
input등의 양식을 라벨로 감싸면 id/for 없이 같은 결과를 얻을 수 있다.
<label class="checkbox-wrap">
<input type="checkbox">
<i class="far fa-heart"></i>
<i class="fas fa-heart heartChecked"></i>
</label>
http://tcpschool.com/html-tags/label
이 밖에도 form 태그에서 활용되는 태그로는 textarea, button, optgroup, fieldset 등이 있다.
<input type="submit/reset/button">
과 <button>
이 다른 점 : button 요소 안에는 텍스트나 이미지를 넣을 수 있다. 주의할 점은 button태그 사용시 type을 정해야 한다는 것이다. 정하지 않으면 브라우저마다 다른 기본값을 사용하게 된다. 따라서 form 양식에서는 되도록 input type=""을 사용하도록 하자. button태그에는 disabled를 지정해 비활성화 시킬 수도 있다.<select>
<optgroup>
<option></option>
</optgroup>
</select>
반응형 웹에서 크기를 조절할 때 사용한다. 최대, 최솟값을 정할 수 있다. 예를 들어 이미지가 작아지다 어느 시점(화면크기)에서 더이상 작아지는 것을 원하지 않을 때 사용하면 된다.
width: 20%;
min-width: 200px;
max-width는 어떤 이미지가 부모 요소보다 커서 라인 밖으로 나갈 때 유용하게 쓸 수 있다.
ul {
width: 100px;
}
li {
width: 100px;
}
더 큰 z-index 값을 가진 요소가 작은 값의 요소 위를 덮는다. 즉, 가장 위로 올라오게 할 태그에 z-index 값을 높게 주면 되는 것!
https://developer.mozilla.org/ko/docs/Web/CSS/z-index
아래 JS 파트에 작성한 것과 마찬가지로 JS를 통해 좋아요 버튼을 구현했으나, input 태그의 checkbox type과 CSS로 이 기능을 구현할 수 있다는 말을 듣고 너무 놀랍고 충격이길래 바로 달려가서 코드를 수정했다.
<label class="checkboxWrap">
<input type="checkbox">
<i class="far fa-heart"></i>
<i class="fas fa-heart heartChecked"></i>
</label>
label 태그 안에 input 태그를 담아 label이 클릭되었을 시 input에도 접근이 가능하도록 해주고, 사용할 두개의 아이콘을 같은 label 안에 담아 둔다.
.checkboxWrap {
color: rgb(126, 125, 125);
cursor: pointer;
font-size: 35px;
}
input[type='checkbox'] {
display: none;
}
.heartChecked {
opacity: 0;
}
label,.checkbox,.fa-heart {
position: absolute;
right: 0;
}
label>input[type="checkbox"]:checked~.heartChecked {
display: block;
opacity: 1;
}
input 태그는 브라우저에 따라 아웃라인(테두리) 디폴트 컬러를 갖는다. chrome 브라우저에서는 blue로 설정이 되어 있었는데, outline:none을 지정하면 input 창을 클릭해도 테두리 색이 변경되지 않는다.
min-width를 사용하는 경우는 주로 스마트폰 등 작은 스크린을 기준으로 레이아웃을 작성하고 점차 확장되는 형태로 작성할 때 사용하고, max-width는 pc 등 큰 화면을 기준으로 레이아웃을 작성하고 점차 축소하는 형태로 레이아웃을 작성한다.
@media screen and (max-width: 800px) {
li {
font-size: smaller;
}
.category {
flex-wrap: wrap;
}
.coffeeList {
grid-template-columns: 1fr 1fr ;
}
}
[미디어 쿼리 구문]
@media media-type and (media-feature-rule) {
/* CSS rules go here */
}
대표적인 중앙정렬 속성이다. 위 아래 여백이 없이 가로 중앙에 배치된다. inline 속성의 태그는 block으로 만들어준 뒤 사용해야 적용이 된다. 또는 span 대신 div 태그를 사용해준다.
<div class="coffeeImg">
<img alt="오늘의 커피" src="">
</div>
.coffeeImg {
position: relative;
width: 100%;
padding-bottom: 100%;
overflow: hidden;
}
.coffee img {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
.coffeeImg img {
transition: all 0.7s;
transform: scale(1);
}
.coffeeImg:hover img {
transform: scale(1.1);
}
const validateEmail = (id) => {
let letters = /^([a-z0-9_]){6,}$/;
if (letters.test(id)) {
return true;
} else {
return false;
}
};
정규표현식은 문자열을 특정 문자 조합과 대응시키기 위해 사용되는 패턴(&객체)이다. 이 패턴은 exec, test, match, replace, search, split 메소드와 함께 쓰인다. 정규식은 두 가지 방법으로 만들 수 있다.
const re = /ab+c/; //슬래쉬로 감싸는 방법
const re = new RegExp("ab+c"); //RegExp 객체 생성자 함수를 호출하는 방법
정규식의 패턴이 변경될 수 있는 경우나 다른 출처로부터 패턴을 가져와야 하는 경우에는 생성자 함수를 사용하면 된다.
[메서드 종류]
const enableButton = (arr) => {
if (arr[0] && arr[1]) {
btn.removeAttribute("disabled");
btn.style.backgroundColor = "#2F8BE9";
} else {
btn.setAttribute("disabled", true);
btn.style.backgroundColor = "#add3ea";
}
};
요소에서 주어진 이름의 특성을 이용한 메서드에는 여러가지가 있다.
삼항연산자 사용법을 먼저 보자.
삼항연산자를 사용한 코드는 아래 코드와 동일하다.
[조건 ? true일 때 return값 : false일 때 return 값]
아래 코드는 위벅스 로그인 페이지에서 삼항 연산자를 사용해 작성한 코드이다.
document.addEventListener('input', () => {
isValid[0] = validateEmail(user.value) ? true : false;
isValid[1] = validatePassword(pwd.value) ? true : false;
enableButton(isValid);
green(isValid[0], user);
green(isValid[1], pwd);
});
addEventListener에는 input 이벤트도 있다. input 이벤트는 input, select, textarea 요소의 value 속성이 바뀔 때마다 발생한다.
btn.addEventListener('click',(e)=>{
e.preventDefault();
window.location.href = "/list.html";
});
버튼 태그에는 button/submit/reset의 속성을 지정할 수 있으며 기본 값은 submit이다. form 태그 안에서 이 속성을 지정하지 않으면 버튼 사용시 자동으로 submit이 된다. 따라서 클릭 시 페이지 이동을 하기 위해서는 .preventDefault();로 디폴트 값을 막고, 원하는 동작의 코드를 넣어준다.
또한 .location.href보다 .location.replace를 쓰는 방법이 권장되는데, href를 사용하는 경우 이미 접속했던 페이지일 때 cache된 페이지를 보여줄 가능성이 있기 때문이다. href를 사용할 때 최신의 정보가 저장되지 않을 가능성이 있으니 되도록 replace를 사용하자. 가능한 경우 html에서 a태그를 사용해 페이지 이동을 하게 만들자.
html/css만을 이용해서 버튼을 바꿀 수 있지만, 이 방법을 몰랐을 때 JS로 이 기능을 구현했다.
const icon = document.querySelector('.icon');
let click = 0;
icon.addEventListener('click', () => {
const eye = document.querySelector('#eye');
click += 1;
if (pwd.type === "password") {
pwd.setAttribute("type", "text");
eye.classList.remove('fa-eye');
eye.classList.add('fa-eye-slash');
} else {
pwd.setAttribute("type", "password");
eye.classList.add('fa-eye');
eye.classList.remove('fa-eye-slash');
}
});
remove와 add를 합친 toggle 메서드를 사용할 수도 있다. toggle()은 클래스값이 있는지 체크하고 없으면 더하고 있으면 제거한다.
document.querySelector('').innerHTML = `<span><li><i></i></li></span>`
//댓글 입력 + 삭제 기능
const reviewText = document.querySelector('.reviewText');
const pushBtn = document.querySelector('.push');
const commentsBox = document.querySelector('.commentsBox');
const posting = () => {
//입력된 댓글 : string length가 0이면 delete버튼 안눌리게 만들어야 함!(미완)
const text = reviewText.value;
if (reviewText.value === '') {
reviewText.focus();
return;
};
//댓글창 리스트로
const comment = document.createElement('li');
comment.setAttribute('class', 'comment');
//스타일
comment.style.display = "flex";
comment.style.marginBottom = "10px"
//댓글창
const commentText = document.createElement('span');
commentText.setAttribute('class', 'commentText');
commentText.innerHTML = text;
//스타일
commentText.style.width = "80%";
commentText.style.padding = "10px";
//삭제 버튼
const deleteComment = document.createElement('button');
deleteComment.setAttribute('class', 'deteleComment');
deleteComment.innerHTML = 'DELETE';
deleteComment.addEventListener('click', () => {
commentsBox.removeChild(comment);
});
//스타일
deleteComment.style.borderStyle = "none";
deleteComment.style.cursor = "pointer";
deleteComment.style.borderRadius = "3px";
deleteComment.style.padding = "5px";
deleteComment.style.width = "20%";
commentsBox.appendChild(comment);
comment.appendChild(commentText);
comment.appendChild(deleteComment);
reviewText.value = " ";
reviewText.focus();
};
//클릭
pushBtn.addEventListener('click', () => {
posting();
});
//엔터
reviewText.addEventListener('keypress', (event) => {
if (event.key === 'Enter') {
posting();
}
return;
});
로그인/패스워드 감지 코드
const emailInput = document.getElementsByClassName(emailInput)[0];
const passwordInput = document.getElementsByClassName(passwordInput)[0];
const loginButton = document.getElementsByClassName(loginButton)[0];
const isValidInput = () => {
return emailInput.value.includes('@') && passwordInput.value.length >= 8
}
const changeButtonColor = () => {
const isValid = isValidInput()
if(isValid) {
loginButton.style.backgroundColor = "blue";
} else {
loginButton.style.backgroundColor = "gray";
}
emailInput.addEventListener('input', changeButtonColor)
passwordInput.addEventListener('input', changeButtonColor)
//자바스크립트에서 색상을 변경하는 경우 유지 보수가 복집해진다. 이 경우 css에 class 식별자를 따로 만들고 속성 부여를 하면 된다.
button {
backgroundColor = "blue";
}
button.active {
backgroundColor = "gray";
}
//html
if(isValid) {
loginButton.classList.add('active');
} else {
loginButton.classList.remove('active');
}
제대로 확실히 알고 있지 않으면 뭐하나 제대로 작동하는 것이 없었다. 모르는 기능을 검색해가며 얻은게 정말 많고, 한 가지 기능을 구현하기 위해 정말 다양한 방법이 있다는 것을 알게 되었다. 리팩토링을 혼자 진행하며 놓친 부분도 많은데, 앞으로 코드명을 누구나 알아볼 수 있도록 쉽게 작성하고 함수 하나가 하나의 기능만 담당하도록 열심히 달려보즈앗 🏃♀️ 레플릿만 풀던 지난주에 비해 너무너무너무너무 힘들지만 재밌고 값진 한주를 보냈다. 리팩토링 & 리액트 공부하러 꼬 -!