지금 수강하고 있는 패스트캠퍼스 X 야놀자: 프론트엔드 개발 부트캠프에서 첫 과제로 HTML과 CSS만을 이용한 클론 코딩을 하게되었다.
원하는 사이트(페이지)를 자유롭게 선택하고 레이아웃만 클론 코딩하면 되는 과제이다.
내가 주로 사용하는 사이트나 서비스를 클론해보자고 생각하여 스마트폰을 보던 중에 넷플릭스, 카카오톡, 패스트캠퍼스, 네이버 등 많은 것들이 있었지만 중복되는 요소가 많았고 CSS만으로 구현하기에는 어려워보였다.
적당한 수준의 사이트를 찾던 중에 최근에 가입한 디즈니 플러스 홈페이지를 들어갔는데 제일 상단 영역을 제외하고는 수업에서 배운 Flex를 적용하기도 괜찮아 보이고 중복되는 영역도 적어 다양한 레이아웃을 구현할 수 있을 것이라고 생각되어 디즈니 플러스를 클론하기로 결정했다.
README.md
파일을 제공하세요!<header>
, <section>
등 시멘틱 태그를 최대한 활용해보세요.2023.07.24(월) ~ 2023.07.28(금)
Visual Studio Code, Git, Github
HTML,CSS,JavaScript
HTML, CSS만을 사용하여 완성하는 것이 주된 과제의 내용이었지만 간단한 기능을 구현하기 위해서 JS를 약간 사용했다.
헤더 + 메인 | 인기 콘텐츠 |
---|---|
특징 + 배너 | 기기 |
질문 | 헤더 + 푸터 |
<header>
,<nav>
, <main>
, <section>
, <footer>
등 시멘틱 태그를 최대한 활용하였다.반응형
CSS의 Media Query를 이용하여 모바일, 테블릿, 데스크탑으로 나누어 구현했습니다.
헤더 투명화
JavaScript와 CSS를 이용하여 특정 스크롤 영역에서 헤더와 버튼이 투명하도록 처리했습니다.
메인 이미지 전환 기능
<input>
, <label>
과 CSS만을 이용하여 메인 이미지 밑에 버튼을 클릭하면 콘텐츠 이미지가 전환되도록 구현했습니다.
질문 아코디언(펼침/닫힘) 기능
JavaScript와 CSS를 이용하여 질문 영역을 클릭하면 질문 상자가 열리거나 닫히도록 처리했습니다.
헤더 투명화
자바스크립트의 addEventListener
의 scroll
과 window.scrollY
를 사용하여 스크롤을 인식하여 상단 0~330px까지에서는 특정 클래스를 추가하여 CSS에 특정 클래스가 있다면 헤더와 버튼을 투명화 하도록 구현하였다.
let header = document.getElementById("header");
let logo = document.getElementById("header-logo");
let signup = document.getElementById("header-signup");
window.addEventListener("scroll", function () {
if (window.scrollY < 330) {
header.classList.add("hidden-header");
logo.classList.add("hidden-item");
signup.classList.add("hidden-item");
} else {
header.classList.remove("hidden-header");
logo.classList.remove("hidden-item");
signup.classList.remove("hidden-item");
}
});
질문 아코디언 기능
자바스크립트의 getElementById
와 addEventListener
중 click
을 사용하여 질문 영역을 클릭하면 질문 <div>
태그 클래스 리스트에 open
이 토글되어 클래스명에 open
이 있다면 질문 아코디언을 펼치고 없다면 질문 아코디언을 닫는 기능을 구현하였습니다.
let question1 = document.getElementById("question1");
question1.addEventListener("click", () => click(question1));
function click(question) {
question.classList.toggle("open");
}
이미지 자동 전환 기능
이미지가 시간이 지남에 따라 자동으로 전환되고 재생 / 일시정지 버튼을 클릭하면 이미지 자동 전환이 멈추거나 재생되어야 한다.
이미지가 좌우로 정렬되어 있다면 CSS만으로 구현할 수 있겠지만 투명화로 전환해야하기 때문에 자바스크립트가 필요하다고 판단되어 구현하지 않았다.
배포환경, 개발환경에 따라 경로가 제대로 적용되지 않는 경우가 발생하니 상대경로로 작성하는 습관을 가지라는 피드백
전
<link
rel="icon"
href="/assets/icons/favicon-32x32.3699...2fb883f.png"
/>
후
<link
rel="icon"
href="./assets/icons/favicon-32x32.3699...2fb883f.png"
/>
<div>
태그 제거<input>
, <button>
태그의 디자인이 원하는대로 되지 않고
<div>
태그로 한번 더 감싸야한다고 생각했는데 불필요하다는 피드백을 받고 수정했다.
전
<form class="email-form">
<div>
<input class="email-input" type="email" placeholder="이메일 주소" />
</div>
<div>
<button class="email-button">구독하기</button>
</div>
</form>
후
<form class="email-form">
<input class="email-input" type="email" placeholder="이메일 주소" />
<button class="email-button">구독하기</button>
</form>
클래스 네이밍이 구체적이지 않아서 코드만 보고 구별이 되지 않거나 비슷한 요소가 있다면 겹칠 수 있지 때문에 네이밍은 중요하다.
역할과 위치 등을 고려한 네이밍을 짓어야 한다.
이번 클론 코딩 프로젝트에서 가격은 한번만 나오기 때문에 문제가 되지 않지만 이후에 문제가 생길 수 있기 때문에 구체적인 네이밍 짓는 연습을 위해 수정했다.
전
<p class="price">
*유료 멤버십 월 9,900원 / 연 99,000원<br />
*연간 구독 시 최대 16% 할인된 가격
</p>
후
<p class="main-price">
*유료 멤버십 월 9,900원 / 연 99,000원<br />
*연간 구독 시 최대 16% 할인된 가격
</p>
무분별한 <div>
태그 사용은 편하지만 나중에 볼 때는 불편 요소가 된다.
시멘틱을 위해서도 텍스트는 <span>
, <p>
태그를 이용하는 것이 좋다.
공통 CSS로 <p>
에 margin-bottom: 20px
으로 정했기 때문에 <div>
를 사용했는데 부적절한 사용이라고 생각했다. 따라서 CSS 적용을 위해 클래스를 만들어 CSS를 적용해주었다.
전
<div class="slide-text-box">
<div>아바타: 물의 길</div>
<div>지금 스트리밍 중</div>
</div>
후
<div class="slide-text-box">
<p class="slide-text">아바타: 물의 길</p>
<p class="slide-text">지금 스트리밍 중</p>
</div>
변할 가능성이 있는 값과 변하지 않는 값을 구분하여 const
와 let
으로 선언해주어야 한다.
전
let header = document.getElementById("header");
후
const header = document.getElementById("header");
querySelectorAll을 사용하서 찾으면 하나의 요소를 누르면 querySelectorAll로 찾아낸 모든 요소가 펼쳐질 것이라고 생각해서 전 코드로 작성했는데 멘토님의 코드 리뷰로 이 방법이 가능하다는 것을 알게되었다.
전
let question1 = document.getElementById("question1");
let question2 = document.getElementById("question2");
let question3 = document.getElementById("question3");
let question4 = document.getElementById("question4");
let question5 = document.getElementById("question5");
question1.addEventListener("click", () => click(question1));
question2.addEventListener("click", () => click(question2));
question3.addEventListener("click", () => click(question3));
question4.addEventListener("click", () => click(question4));
question5.addEventListener("click", () => click(question5));
후
const questions = document.querySelectorAll(".accordion-container");
for (const question of questions) {
question.addEventListener("click", () => click(question));
}
CSS를 브라우저가 파싱할 때, 선택자를 우측에서부터 읽는다. 그럼 모든 img 태그를 모두 찾은 다음에 적용을 하는 순서로 가기 때문에 태그 선택자를 지양해야 한다.
따라서, 아래와 같이 태그 선택자를 적절한 클래스를 만들어 클래스 선택자로 적용시켜 주었다.
전
.banner-box img {
...생략
}
후
.banner-img {
...생략
}
공통 CSS에 reset용으로 color: white
로 선언했는데 멘토님께서 reset용 코드라면 color: inherit
이 맞다고 하여서 여쭤보니
'모든 태그를 white로 쓴다면 문제가 되지 않겠지만, 기획이나 디자인은 언제든 변경이 가능하기 때문에 그렇게 명시하는 건 좋은 습관은 아니다'라는 답변을 받았고 지금은 간단한 클론 코딩이라 문제가 되지 않지만 규모가 큰 프로젝트나 실제 프로젝트에서는 문제가 생길 수 있다는 것을 알게되었다.
전
a {
color: white;
}
후
a {
color: inherit;
}
input[id="select_box1"]:checked ~ div #imgbox .select1 {
opacity: 1;
}
버튼 클릭을 이용한 이미지 전환 기능을 구현하는 과정에서 ~
(형제 선택자)를 사용하려는 태그끼리 한 태그 안에 있는 형제 태그여야(같은 부모에 속해있어야) 작동하는데 사용법을 정확히 공부하지 않아 다른 태크에 속해있어 원하는대로 동작하지 않는 것을 모르고 많은 시간이 걸렸다.
<div class="slide">
<input type="radio" name="img_select" id="select_box1" checked />
~생략~
<div class="slide-nav">
<label class="label1" for="select_box1"></label>
~생략~
</div>
<button class="slide-btn"></button>
<div>
<ul id="imgbox">
<li class="select_img select1">
<div
class="background mobile"
style="
background-image: url(/assets/images/main/revenant_m.jpeg);
"
></div>
~생략~
</li>
~생략~
</ul>
</div>
</div>
위 코드와 같이 input
태그와 div
태그 안에 있는 select1
클래스를 slide
클래스를 가진 div
태그안에 넣어주어 문제를 해결했다.
클론 코딩을 시작하기 전에는 쉽게 완료할 수 있을 줄 알았는데 생각보다 오랜 시간이 걸렸다.
JS 없이 CSS만으로 구현할 수 있는 기능이 생각보다 많다는 것을 알게되었다.
그와 동시에 CSS는 그때그때 필요한 것을 검색해서 사용하면 된다고 생각했으나 CSS 내장 기능의 존재 자체를 알고있는 것과 모르는 것의 차이는 크다는 것을 알게되었다.
형제 선택자, 인접 선택자 등의 존재와 사용법을 알게되어서 좋았고 다음 프로젝트에서도 유용하게 사용할 수 있을 것 같다.
처음으로 현업 개발자에게 코드 리뷰를 받았는데 멘토님께서 코드 리뷰를 꼼꼼히 해주셔서 많은 도움이 되었다.
생각도 못한 부분에서 놓치고 있던 부분도 있었고 간단하게 클린코드로 리펙토링 할 수 있는 부분도 있었다.