Youtube 클론코딩 (개발노트)

정우시·2022년 6월 13일
1

1. 프론트엔드 입문

목록 보기
13/13
post-thumbnail

Youtube 클론코딩

유튜브 모바일, 테스크탑 클론 코딩 (HTML, CSS, JS 프로젝트2)

깃허브: https://github.com/wusi-hub/Youtube-Mobile


구현할 유튜브 모바일과 데스크탑


들어가기 앞서......

웹 사이트를 구현하기 전에 레이아웃 구조를 먼저 생각한다.

  • 전체 레이아웃: 헤더, 비디오 플레이어, 비디오 정보란, 재생될 목록란

  • 비디오 정보란: 비디오 메타 데이터, 액션 버튼, 채널 정보

  • 비디오 메타 데이터: 해시 태그, 타이틀, 토글 버튼, 조회수와 올린 날짜

  • 액션 버튼: 버튼 5개

  • 채널 정보: 채널 이미지, 채널 이름, 구독 버튼

반응형으로 웹사이트를 만들 때는 레이아웃을 최소한으로 재배치한다.

  • 미디어 쿼리별로 레이아웃을 너무 많이 작성하면 개발, 유지보수, 사용자 경험 등 모두 좋지 않다.

스케치북이나 종이가 있다면 Wireframe을 만드는 것도 좋다.

  • 간단한 스케치 후 레이블링을 한다.

모르는 것이 있다면 MDN을 참고한다.


1. HTML 구조 만들기

head

  • 링크 태그를 통해 파비콘 삽입하고 유튜브 이미지는 유튜브 개발자 도구를 통해 가져온다.
  • 타이틀을 입력한다.
  • Font Awesome Kit code를 삽입한다.
  • 구글 폰트 코드를 삽입한다.
  • styles.css와 main.js를 연결한다.
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="shortcut icon"
      href="https://www.youtube.com/s/desktop/a7767b34/img/favicon.ico"
      type="image/x-icon"
    />
    <link rel="stylesheet" href="styles.css" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap"
      rel="stylesheet"
    />
    <title>
      Mother Nature | Mother Nature (sometimes known as Mother Earth or the
      Earth Mother) is a personification of nature that focuses on the
      life-giving and nurturing aspects of nature by embodying it, in the form
      of the mother
    </title>
    <script
      src="https://kit.fontawesome.com/6478f529f2.js"
      crossorigin="anonymous">
    </script>
    <script src="main.js" defer></script>
  </head>

body

  • 커다란 박스를 만들 때는 최대한 Semantic tag를 이용하여 만든다.
<body>
	<header>
    
    </header>
    
    <section class="player">
    
    </section>
    
    <div class="infoAndUpNext">
    <section class="info"></section>
    <section class="upNect"></section>
    </div>
</body>

!-- header --

  • Font Awesome을 이용하여 아이콘 삽입하고 타이틀을 넣는다.
  • Font Awesome 삽입은 Kit code를 받은 후 안에 붙여넣기 한다.
<header>
      <div class="logo">
        <i class="fab fa-youtube"></i>
        <span class="title">Premium</span>
      </div>
      <div class="icons">
        <i class="fas fa-search"></i>
        <i class="fas fa-ellipsis-v"></i>
      </div>
</header>

!-- Video Player --

  • video 태그를 이용해서 다운로드 받거나 링크를 통해 비디오 삽입한다.
  • 속성은 controls를 이용한다.
<section class="player">
	<video controls src="video/video.mp4"></video>
</section>

!-- Video Info --

  • info안에 metadata, actions, channel을 만든다.
  • Video Info와 Up Next의 정렬을 하기 편하게 같은 박스(<div class="infoAndUpNext">) 안에 넣는다.
<div class="infoAndUpNext">
  <section class="info">
  <div class="metadata"></div>
  <ul class="actions"></ul>
  <div class="channel"></div>
</section>

!-- Metadata --

  • metadata안에 있는 해시태그는 리스트로 만들고 나머지(titleAndButton, views)는 div로 만든다.
<div class="metadata">
  <ul class="hashtags"></ul>
  <div class="titleAndButton"></div>
  <div class="views"></div>
</div>
  • 해시태그안에 3개의 태그를 만든다.
<div class="metadata">
	<ul class="hashtags">
   		<li>#World</li>
        <li>#MotherNature</li>
		<li>#Phillip</li>
	</ul>
  • 타이틀은 span안에 넣는다.
  • 타이틀 옆에 moreBtn이라는 이름으로 버튼을 만든다.
  • span에 -webkit-line-clamp를 대비해서 clamp라는 클래스를 만들어준다.
  • JS 파일을 이용해 토글 버튼을 누르면 clamp를 빼준다.
<div class="titleAndButton">
	<span class="title clamp">
       Mother Nature | Mother Nature (sometimes known as Mother Earth or
       the Earth Mother) is a personification of nature that focuses on
       the life-giving and nurturing aspects of nature by embodying it,
       in the form of the mother
	</span>
    <button class="moreBtn">
      	<i class="fas fa-caret-down"></i>
    </button>
</div>
  • 조회수와 올린 날짜(views)의 경우 텍스트이기에 span으로 만든다.
<span class="views">1M views 1 month ago</span>
  </div>

!-- Actions --

  • 액션 버튼의 경우 클릭이 가능한 버튼으로 만들고 싶기에 리스트안에 버튼을 만든다.
  • 해당 버튼안에는 Font Awesome을 이용해서 아이콘과 span을 넣는다.
  • thumbs-up의 경우 눌렀을 때 파란색으로 표시하기 위해 active라는 클래스 이름을 넣는다.
<ul class="actions">
  <li>
   <button><i class="active fas fa-thumbs-up"></i><span>1K</span></button>
  </li>
  <li>
   <button><i class="fas fa-thumbs-down"></i><span>0</span></button>
  </li>
  <li>
   <button><i class="fas fa-share"></i><span>Share</span></button>
  </li>
  <li>
   <button><i class="fas fa-plus"></i><span>Save</span></button>
  </li>
  <li>
   <button><i class="fab fa-font-awesome-flag"></i><span>Report</span></button>
  </li>
 </ul>

!-- Channel Description --

  • 채널 왼쪽에는 채널과 관련된 metadata가 있고 오른쪽에는 subscribe로 2가지의 박스로 나누어져있다.
  • metadata에는 이미지 태그를 이용해서 아바타를 삽입한다.
  • 이미지 옆에는 이름과 구독자수가 나와 있기에 새로운 박스로 만들고 span 태그를 사용한다.
  • subscribe는 누를 수 있게 버튼으로 만들고 대문자의 경우 CSS로 처리할 것이기에 놔둔다.
	<div class="channel">
          <div class="metadata">
            <img src="image/avatar.jpeg" alt="" />
            <div class="info">
              <span class="name">WUSI</span>
              <span class="subscribers">1M subscribers</span>
          	</div>
          </div>
          <button class="subscribe">subscribe</button>
	</div>
</section>

!-- Up Next --

  • 똑같은 방식으로 반복해서 리스트 아이템들이 나오는 것을 알 수 있다.
  • ul 태그와 li 태그를 이용하면 된다. 또한 li 태그의 경우 item이라는 클래스를 넣어준다.
  • 아이템의 경우 이미지 태그, 인포메이션이라는 div 그리고 moreBtn이라는 클래스로 button 태그를 만든다.
  • 인포메이션이라는 div에는 span 태그를 이용해서 타이틀, 이름 그리고 조회수를 만든다.
  • 이미지의 경우 반응형으로 만들기 위해 박스안에 넣는다.
<section class="upNext">
        <span class="title">Up next</span>
        <ul>
          <li class="item">
            <div class="img"><img src="image/1.jpg" alt="" /></div>
            <div class="info">
              <span class="title"
                >Aurora | An aurora also commonly known as the polar
                lights</span
              >
              <span class="name">WUSI</span>
              <span class="views">80K views</span>
            </div>
            <button class="moreBtn"><i class="fas fa-ellipsis-v"></i></button>
          </li>
          <li class="item">
            <div class="img"><img src="image/2.jpg" alt="" /></div>
            <div class="info">
              <span class="title"
                >Space | Space is the boundless three-dimensional extent</span
              >
              <span class="name">WUSI</span>
              <span class="views">81K views</span>
            </div>
            <button class="moreBtn"><i class="fas fa-ellipsis-v"></i></button>
          </li>
          <li class="item">
            <div class="img"><img src="image/3.jpg" alt="" /></div>
            <div class="info">
              <span class="title"
                >Forest | A forest is an area of land dominated by trees</span
              >
              <span class="name">WUSI</span>
              <span class="views">82K views</span>
            </div>
            <button class="moreBtn"><i class="fas fa-ellipsis-v"></i></button>
          </li>
        </ul>
      </section>
    </div>
  </body>

2. CSS 구조 만들기

/ 변수 만들기 /

  • 전반적으로 프로젝트에서 쓰이는 색, 사이즈 등을 변수로 정의해서 사용한다.
:root {
  /* Color */
  --white-color: #fff;
  --black-color: #140a00;
  --blue-color: #045fd4;
  --red-color: #ff0000;
  --grey-dark-color: #909090;
  --grey-light: #e0e0e0;
  /* Size */
  --padding: 12px;
  --avatar-size: 36px;

  /* Font Size */
  --font-large: 18px;
  --font-medium: 14px;
  --font-small: 12px;
  --font-micro: 10px;
}

/ 전체 선택자 /

  • 전체 선택자(*)를 이용해서 모든 태그의 padding과 margin을 없애고 box-sizing도 padding과 margin을 줬을 때 그것을 포함해서 사이즈가 결정 되도록 설정한다.
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

/ 폰트 지정 /

  • body 태그를 이용해서 폰트 지정한다.
body {
  font-family: "Roboto", sans-serif;
}

/ 리스트 스타일 바꾸기 /

  • list-style: none;을 이용해서 점모양을 없앤다.
ul {
	list-style: none;
}
  • 버튼과 버튼이 포커스 되었을 때의 기본 설정 값을 없애준다.
button,
button:focus {
  border: none;
  cursor: pointer;
  outline: none;
  background-color: transparent;
}

/ Header /

  • 헤더를 플렉스 박스로 지정한다.
  • space-between을 이용해서 간격을 나눈다.
  • 패딩은 미리 정의한 변수를 지정한다.
  • 헤더의 배경색을 미리 정의한 변수 검정색을 지정한다.
  • 폰트 색상 또한 미리 정의한 변수 흰색을 지정한다.
header {
  display: flex;
  justify-content: space-between;
  padding: var(--padding);
  background-color: var(--black-color);
  color: var(--white-color);
}
  • 헤더의 로고 사이즈를 미리 정의한 변수 large를 지정한다.
header .logo {
  font-size: var(--font-large);
}
  • 헤더 로고의 아이콘을 미리 정의한 변수 빨간색으로 지정한다.
header .logo i {
  color: var(--red-color);
}
  • 헤더 서치 아이콘의 간격을 미리 정의한 변수 padding을 지정해서 간격을 만든다.
header .icons .fa-search {
  margin-right: var(--padding);
}

/ Video Player /

  • 커지고 옆에 공간이 생겼을 때 플레이어를 컨테이너 중간으로 오게 한다.
  • 빈 부분을 검정색으로 설정한다.
  • 스크롤할 때 고정되게 하기 위해 sticky를 이용한다.
  • top을 0으로 해야 sticky가 구현된다.
.player {
  position: sticky;
  top: 0;
  text-align: center;
  background-color: var(--black-color);
}
  • 아이템(비디오 플레이어)을 반응형으로 만들기 위해 가장 좋은 방법은 퍼센티지를 이용하는 것이다. 그러나 가로 세로 그냥 100%로 하면 플레이어가 너무 커지기 때문에 제한을 두는 것이 좋다.
.player video {
  width: 100%;
  height: 100%;
  max-width: 1000px;
}

/ Video Info /

  • 바디 바로 밑에 있는 info 클래스에 패딩을 넣어준다.
body > .info {
  padding: var(--padding);
}
  • .infoAndUpNext 바로 밑에 있는 info 클래스에 패딩을 넣어준다.
.infoAndUpNext > .info {
  padding: var(--padding);
}

/ Metadata /

  • 인포 안에 있고 메타데이타 안에 있는 해시태그에만 적용한다.
  • 플렉스를 이용해서 해시태그를 한 줄로 만든다.
  • 폰트 사이즈는 이미 지정한 변수를 통해 작게 만든다.
  • 색상은 변수로 지정한 파란색으로 한다.
.info .metadata .hashtags {
	display: flex;
    font-size: var(--font-small);
    color: var(--blue-color);
}
  • 해시태그의 개별 아이템마다 패딩을 준다.
.info .metadata .hashtags li {
	margin-right: var(--padding);
}
  • .titleAndButton을 플렉스 박스로 만든다.
  • margin-right를 이용해서 타이틀과 moreBtn 사이의 공간을 만든다.
.info .metadata .titleAndButton {
	display: flex;  
}
  • 타이틀의 폰트 사이즈를 변수에 미리 지정한 medium 사이즈로 만든다.
  • margin-right를 이용해서 타이틀과 moreBtn 사이의 공간을 만든다.
.info .metadata .titleAndButton .title {
	font-size: var(--font-medium);
    margin-right: var(--padding);
}
  • 타이틀은 점점 작아졌을 때 2줄 이상 안되게 라인을 고정시킨다.
  • 라인을 고정시킬 때는 MDN 사이트의 -webkit-line-clamp를 이용한다.
  • 평소에는 clamp로 고정시킨다.
  • 나중에 JS를 이용해서 버튼을 눌렀을 때만 전체 타이틀이 나오게 한다.
.info .metadata .titleAndButton .title.clamp {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
  • moreBtn의 높이를 타이틀의 위치와 똑같이 맞춘다.
  • 토글 버튼이 바로 안바뀌고 천천히 바뀌도록 transition을 이용한다.
.info .metadata .titleAndButton .moreBtn {
	height: 100%;
}
  • 애니메이션을 이용해서 토글 버튼을 눌렀을 때 180도 돌아가게 만든다.
.info .metadata .titleAndButton .moreBtn.clicked {
  transform: rotate(180deg);
}
  • views의 폰트와 색상을 미리 지정한 변수로 바꾼다.
.info .views {
  font-size: var(--font-small);
  color: var(--grey-dark-color);
}

/ Action Buttons /

  • 안전하게 actions에 설정된 CSS를 넣어주기 위해 .actions와 더불어 .info를 함께 넣어준다.
  • .action을 플렉스 박스로 만든다.
  • 간격을 골고루 하기 위해 space-around를 이용한다.
  • .action자체가 너무 붙어있어서 마진을 통해 간격을 만들어준다.
  • 마진은 위아래에는 패딩을 넣고 양옆은 0으로 만들어준다.
.info .actions {
  display: flex;
  justify-content: space-around;
  margin: var(--padding) 0;
}
  • 각각의 버튼들도 플렉스 박스로 바꾼다.
  • 주축을 세로로 바꾼다.
  • 전반적인 폰트의 사이즈는 변수에 미리 지정한 small로 만든다.
  • 색상은 미리 지정한 다크 그레이로 바꾼다.
.info .actions button {
  display: flex;
  flex-direction: column;
  font-size: var(--font-small);
  color: var(--grey-dark-color);
}
  • 아이콘과 텍스트의 간격을 주기 위해 margin-bottom을 이용한다.
  • margin-bottom은 변수에 설정한 패딩의 절반만 넣어준다.
  • 절반만 넣어줄 때는 calc()함수를 이용한다.
  • 아이콘들의 가운데 정렬을 하기 위해 margin을 위아래는 0 양옆은 auto로 지정해준다.
  • 아이콘 사이즈를 조금 늘려 16px로 한다.
.info .actions button i {
  margin: 0 auto;
  margin-bottom: calc(var(--padding) / 2);
  font-size: 16px;
}
  • i에 active로 되어있는 거만 파란색으로 지정한다.
.info .actions button i.active {
  color: var(--blue-color);
}

/ Channel Description /

  • 제일 상위에 있는 채널 컨테이너를 플렉스 박스로 만들어준다.
  • space-between을 이용해서 채널 정보와 구독 버튼의 간격을 만든다.
  • 채널 부분의 경계선을 border를 이용해서 만든다.
.channel {
  display: flex;
  padding: 5px;
  justify-content: space-between;
  border-top: 1px solid var(--grey-light);
  border-bottom: 1px solid var(--grey-light);
}
  • 채널안에 있는 메타데이터 부분도 플렉스 박스로 만들어준다.
  • 이미지와 채널 제목을 가운데 정렬 하기 위해 align-items를 center로 만들어준다.
.channel .metadata {
  display: flex;
  align-items: center;
}
  • 메타데이터 안에 있는 이미지를 미리 지정한 아바타 사이즈로 만들어준다.
  • 너비 높이 똑같은 사이즈로 만들어준다.
  • 이미지를 동그랗게 만들기 위해 border-radius를 50%를 만든다.
.channel .metadata img {
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: 50%;
}
  • 채널 이름도 세로로 나누기 위해 플렉스 박스로 만들고 주축은 세로로 한다.
.channel .metadata .info {
  display: flex;
  flex-direction: column;
}
  • 채널 이름의 폰트 사이즈는 미리 정의한 medium 사이즈로 한다.
.channel .metadata .info .name {
  font-size: var(--font-medium);
}
  • 구독 버튼 부분은 font 사이즈를 미리 정의한 small로 바꾼다.
  • 색상은 미리 정의한 다크 그레이로 만든다.
.channel .metadata .info .subscribers {
  font-size: var(--font-small);
  color: var(--grey-dark-color);
}
  • 채널안에 있는 구독 버튼은 모두 대문자로 만든다.
  • 색상과 폰트 사이즈는 미리 정의한 red와 medium으로 지정한다.
.channel .subscribe {
  text-transform: uppercase;
  color: var(--red-color);
  font-size: var(--font-medium);
}

/ Up Next /

  • upNext 왼쪽에만 미리 지정한 패딩을 준다.
.upNext {
  padding-left: var(--padding);
}
  • upNext 박스의 바로 밑에 있는 자식 타이틀에만 폰트 사이즈, 색상 그리고 아래의 마진 값을 넣어준다.
  • 마진 값은 calc()함수를 이용한다.
.upNext > .title {
  font-size: var(--font-medium);
  color: var(--grey-dark-color);
  margin-bottom: calc(var(--padding) / 2);
}
  • upNext에 있는 아이템들을 플렉스 박스로 만들어준다.
  • 위에 간격을 주기 위해 마진을 이용하고 마진 값은 미리 지정한 padding 값을 설정한다.
.upNext .item {
  display: flex;
  margin-top: var(--padding);
}
  • 이미지의 간격은 줄어들 때나 늘어날 때나 1 1을 유지하고 basis 15%로 맞춘다.
.upNext .item .img {
  flex: 1 1 15%;
  margin-right: var(--padding);
}
  • 이미지의 사이즈는 너비는 168px로 하고 높이는 94px로 한다.
.upNext .item .img img {
  width: 168px;
  height: 94px;
}
  • 글자의 간격은 줄어들 때나 늘어날 때나 1 1을 유지하고 basis 80%로 맞춘다.
.upNext .item .info {
  flex: 1 1 80%;
}
  • 아이콘의 위치를 해당 박스의 가장 위로 올린다.
  • moreBtn의 간격은 줄어들 때나 늘어날 때나 1 1을 유지하고 basis 5%로 맞춘다.
.upNext .item .moreBtn {
  height: 100%;
  flex: 1 1 5%;
}
  • 3개의 아이템들을 각각 플렉스 박스로 만들고 나서 column 형태로 만들어준다. 그러면 자동으로 정렬이 된다.
.upNext .item .info {
  display: flex;
  flex-direction: column;
}
  • name의 폰트 사이즈와 색상을 넣어준다.
.upNext .item .info .name {
  font-size: var(--font-small);
  color: var(--grey-dark-color);
}
  • views의 폰트 사이즈와 색상을 넣어준다.
.upNext .item .info .views {
  font-size: var(--font-micro);
  color: var(--grey-dark-color);
}

/ infoAndUpNext /

  • Video Info와 Up Next의 정렬을 하기 편하게 같은 박스 안에 넣는다.
  • 해당 박스는 플렉스 박스로 만들고 주축은 세로로 한다.
.infoAndUpNext {
  display: flex;
  flex-direction: column;
}

/ Media Query /

  • 미디어 쿼리를 이용해서 .infoAndUpNext의 값을 지정한다.
  • 화면이 768px 이상일 때는 주축을 row로 한다.
  • 마진 값을 위아래는 미리 정의한 패딩 값으로 양 옆은 0으로 준다.
@media screen and (min-width: 768px) {
  .infoAndUpNext {
    flex-direction: row;
    margin: var(--padding) 0;
  }
}

3. JS를 이용해서 동적인 토글 버튼 만들기

  • 인포 메타데이타 안에 있는 moreBtn을 const moreBtn에 할당한다.
  • 인포 메타데이타 안에 있는 title을 const title에 할당한다.
  • moreBtn이 클릭이 되면 moreBtn 클래스의 클릭 여부를 토글한다.
  • 타이틀의 클래스를 clamp 여부를 토글한다.
const moreBtn = document.querySelector('.info .metadata .moreBtn');
const title = document.querySelector('.info .metadata .title');

moreBtn.addEventListener('click', () => {
    moreBtn.classList.toggle('clicked');
    title.classList.toggle('clamp');
});
profile
프론트엔드 공부하고 있는 정우시입니다.

0개의 댓글