marautube

머로튜브 (동영상 커뮤니티 사이트)

marautube

머로튜브 (동영상 커뮤니티 사이트)

유튜브 클론 코딩.
해리포터의 '머로더즈 맵'에서 모티브를 얻어 호그와트 학생들을 위한 동영상 사이트를 구축했습니다. 계정 생성, 수정, 동영상 업로드, 수정, 삭제, 로그인, 녹화, 댓글 생성, 삭제가 가능합니다.

그동안은 Javascript의 실력이 부족해서 시도해보지 못했던 것이 많았다면, 이번 작품에는 직접 만든 코드를 넣을 수 있었습니다. 가장 공들인 부분은 로딩 페이지 채널 페이지 커서 커스텀 다크 모드입니다.

백엔드까지 구축해본 것은 처음이라서 이론을 머릿속에 넣는 것도 어렵고 시행착오도 많았지만, 멋진 작품이 나와서 노마드코더 내에서 우수 졸업생 완성작에도 뽑히게 돼서 의미있는 작업이 되었습니다.

concept

해리포터의 마법학교 컨셉에 충실하게

머로더즈 맵에서 모티브를 가져온 거라서 마법학교 호그와트 세계관을 충실하게 따르고 싶었습니다.
평범하지 않은 마법학교를 다니고 있지만, 머로더즈는 장난치기 좋아하는 10대 학생들이기에 교수님들이 접속할 수 없는 마법을 건 비밀 커뮤니티 사이트를 만들었다는 컨셉입니다.

메인 인트로 페이지에 '머로더즈 맵'을 불러오는 마법 주문을 외쳤다는 가정하에 오른쪽 이미지가 시간을 두고 페이드 인 되도록 애니메이션을 주었습니다.
'머글이 뭐예요?'를 클릭하면 머글들을 추방하기 위해 google uk 페이지로 넘어가도록 했습니다.

marautube intro
  • color

    main color

    #2a0100

    color

    #820606

    color

    #d8ae84

  • font

    English: Cinzel Decorative
    Korean: Hahmlet
    Google Material Icon

  • tool

    Front-end

    SCSS, Javascript, Pug, Webpack, Babel

    Back-end

    NodeJS, Express, MongoDB, AWS

    Design

    Photoshop

  • type

    Responsive web

  • total elapsed time

    2022.6~8

  • theme

    유튜브 클론 코딩, 동영상 사이트, 해리포터, 머로더즈 맵, 반응형, 개그

  • target

    10대

  • role

    design/Development 100%

marautube intro

design

동영상 위주로 한 심플한 디자인

동영상 사이트이기 때문에 별다른 디자인 요소를 넣지 않고 동영상에 집중되도록 디자인했습니다.
제일 먼저 출력되는 상단 부분(DISCOVERY)에는 '사이트에서 추천하는 동영상', '사이트에서 추천하는 채널' 그리고 '가장 구독자가 많은 업로더'를 노출시켰습니다. (디자인만 했을 뿐, 실제로 기능이 구현되지는 않았습니다.)

그리고 그 아래(TRENDING VIDEO)는 등록한 동영상이 노출되도록 했습니다. 동영상이 메인이라서 섬네일과 제목이 가장 눈에 띄도록 했습니다.

marautube

function/design

상세 페이지 디자인과 기능

왼쪽에는 동영상, 오른쪽에는 댓글을 남길 수 있게 디자인했습니다.
동영상 컨트롤 바(재생 바)도 디자인했으며, 유튜브처럼 커서의 움직임이 없으면 재생바가 자동으로 감춰집니다. 또한 동영상을 끝까지 봤을 경우, 자동으로 재생횟수가 카운트 되며, 단축키도 동작합니다.
현재 동영상은 문제가 있는데, 모바일에서는 재생이 되지 않는다는 것입니다. (수정 예정)

오른쪽 댓글의 경우, 로그인을 하지 않으면 댓글을 남길 수 없도록 막아뒀으며(사진 참조), 최신 댓글이 가장 아래로 정렬되도록 했습니다.

marautube

로그인했을 경우, 댓글 작성이 가능해지며, 본인이 남긴 댓글을 삭제할 수도 있습니다. 또한, 본인이 올린 게시물의 경우, 영상을 수정하거나 삭제할 수도 있습니다.

marautube marautube marautube

function/design

회원가입/로그인/업로드 등의 폼 양식

폼을 작성해야 하는 회원가입, 로그인, 업로드, 회원정보 수정, 동영상 수정 등의 폼 양식은 통일감을 주기 위하여 같은 디자인을 적용했습니다.
로그인 페이지는 사이트 내에서 가입해서 로그인할 수도 있지만, 깃허브 소셜 로그인을 적용하여, 깃허브와 연동해서 로그인하는 것도 가능합니다.

회원가입에는 컨셉에 충실하기 위해 '기숙사 입력'과 자신을 뽐낼 수 있는 자기소개를 적을 수 있는 칸을 마련했으며, 회원 정보 수정에서 프로필 등록을 하는 것보다 회원가입 할 때 프로필 이미지를 등록할 수 있도록 했습니다. 만약 프로필 이미지를 등록하지 않았을 때는 상단에 no data라고 뜨도록 했습니다. 회원 정보 수정에서는 'no data'라는 이미지가 뜨며, 아이디는 수정이 불가능하도록 막았습니다.

업로드 페이지는 기본으로 등록되는 첨부파일 폼 디자인이 사이트 디자인과 맞지 않아 커스텀을 진행했으며, 썸네일 이미지를 가져오면, 현재 자신이 가져온 이미지를 프리뷰해서 보여줄 수 있도록 했습니다.

marautube

function/design

채널 상세보기

유저나 나의 채널에 들어가면 출력되는 페이지입니다. 해당 유저의 아이디와 소속 기숙사, 자기소개, 프로필 이미지가 왼쪽에 출력되며, 해당 유저가 업로드한 동영상은 오른쪽에 출력되도록 했습니다. 또한, 등록한 동영상이 3개 이상일 경우, 편의를 위하여 스크롤을 해도 왼쪽 유저 정보는 움직이지 않도록 고정했습니다.

모바일에서는 유저 정보의 고정을 풀고 상단에 위치시켰으며, 그 아래에 업로드한 동영상이 출력됩니다.

marautube

만약 프로필 이미지와 업로드한 동영상이 없는 유저의 경우에는 'no data'가 아닌, '머로더즈 맵' 이미지를 기본으로 출력되도록 했으며, 오른쪽에는 '죽음을 먹는 자'의 이미지를 넣고, Something Wrong. Someone used Deletrius.이라는 문구를 추가해 컨셉을 지키며 위트를 더했습니다. (Deletrius: 없애는 마법)

marautube

function/design

다크 모드

눈의 편안함을 위하여 다크 모드를 추가하였습니다. 한 번도 해본 적이 없었기에 나름 연구하면서 구현했습니다. 유저가 원할 때마다 버튼으로 끄고 켜기 위하여 Javascript를 채택했으며, 브라우저를 켜고 끌 때마다 유저가 지정한 모드가 저장되길 바라서 웹 스토리지 객체인localstorage를 사용해서 브라우저 내에 '키-값'을 저장할 수 있도록 했습니다.

Cookie, Session이 아닌 localstorage를 사용한 이유는 지식이 깊지 않다는 것, 저장된 데이터는 세션 간에 공유된다는 점, 서버가 아닌 로컬에 보존되는 점, 자바스크립트로 조작하며 모바일에서도 사용할 수 있다는 점 등이 있습니다.

HTML5
  
    <li class="theme-change">
      <a href="javascript:void(0)"
        ><i class="material-icons">dark_mode</i></a
      >
    </li>
  

html구조는 간단합니다. 기본은 light mode이므로 material icons아이콘을 dark_mode로 설정합니다.

dark mode가 되었을 때는 CSS파일 안에 dark-mode일 때 변하는 색상 등을 지정해줍니다. 색상은 눈을 편하게 하고자 사용한다는 것을 명심하며 강렬하지 않은 색상을 사용했습니다. #000#fff을 사용하면 오히려 가독성이 떨어지거나 밝아서 눈에 들어오지 않을 수 있어, #121212#bbb을 사용했습니다.

Javascript
  // dark mode
  const darkTheme = document.querySelector(".theme-change a");
  const darkIcon = darkTheme.querySelector("i");

  // 버튼을 클릭할 때마다 동작하는 함수
  function darkThemeClick() {
    darkTheme.addEventListener("click", function () {
      console.log(darkIcon);
      if (!body.classList.contains("dark-mode")) {
        body.classList.add("dark-mode");
        darkIcon.innerText = "light_mode";
        localStorage.setItem("modeSwitch", "light_mode");
      } else if (body.classList.contains("dark-mode")) {
        body.classList.remove("dark-mode");
        darkIcon.innerText = "dark_mode";
        localStorage.setItem("modeSwitch", "dark_mode");
      }
    });
  }

  darkThemeClick();

  const modeSwitch = localStorage.getItem("modeSwitch"); // "modeSwitch" 아이템 값 불러오기

  //만약 modeSwitch가 dark_mode(테마는 light)라면 아이콘을 dark_mode(달)로 바꿈.
  if (modeSwitch === "dark_mode") {
    body.classList.remove("dark-mode");
    darkIcon.innerText = "dark_mode";
  } else if (modeSwitch === "light_mode") {
    // modeSwitch가 light_mode(테마는 dark)라면 아이콘을 light_mode(해) 바꿈.
    body.classList.add("dark-mode");
    darkIcon.innerText = "light_mode";
  }

버튼을 클릭할 때마다 localstorage에 아이콘 이름을 저장해서 현재 아이콘이 light인지 dark인지 확인할 수 있게 해줍니다. 또한, 현재 dark mode라면, bodydark-mode클래스를 추가하고, 아이콘을 light_mode로 바꿉니다.

function

마우스 오버 효과/커서 커스텀

마법학교 컨셉을 따르기 위해 마우스를 마법 지팡이라고 설정해, 링크에 마우스를 올리면 LUMOS(루모스. 불을 밝히는 마법)로 변하는 애니메이션을 넣었습니다.

  • 마우스 오버 전

  • 마우스 오버 후

HTML5
  
    <div class="cursor">
      <div class="link-wrap">
        <div class="cursor-rotate">
          <span class="eng">l</span><span class="eng">u</span
          ><span class="eng">m</span><span class="eng">o</span
          ><span class="eng">s</span>
        </div>
        <div class="cursor-rotate">
          <span class="eng">l</span><span class="eng">u</span
          ><span class="eng">m</span><span class="eng">o</span
          ><span class="eng">s</span>
        </div>
      </div>
    </div>
  

커서를 CSS로 감추고 div로 가짜 커서를 만들었습니다. 이미지를 사용하면 유지보수가 힘들어서 일일이 글자를 분리하여 transform: rotate(~deg);position: absolute;로 위치값을 주었습니다.

Javascript
let mouseCursor = document.querySelector(".cursor");
  let link = document.querySelectorAll("a");
  let hoverLink = document.querySelectorAll(".hover-link");
  
  // 마우스가 움직일 때마다 div를 움직이게 함.
  window.addEventListener("mousemove", cursor);
  function cursor(e) {
    mouseCursor.style.left = e.pageX + "px";
    mouseCursor.style.top = e.pageY + "px";
  }
  
  // a 태그에 마우스를 가져다대면 'link-cursor'라는 클래스를 붙임
  link.forEach((link) => {
    link.addEventListener("mouseover", () => {
      mouseCursor.classList.add("link-cursor");
    });
  
    link.addEventListener("mouseleave", () => {
      mouseCursor.classList.remove("link-cursor");
    });
  });
  // '.hover-link' 라는 클래스를 가지고 있는 요소에 마우스를 가져다대면 'link-cursor'라는 클래스를 붙임
  hoverLink.forEach((hoverLink) => {
    hoverLink.addEventListener("mouseover", () => {
      mouseCursor.classList.add("link-cursor");
    });
    hoverLink.addEventListener("mouseleave", () => {
      mouseCursor.classList.remove("link-cursor");
    });
  });

가짜div를 움직일 때마다 Javascript로 x, y좌표값을 각자 left, top값에 입력되도록 했습니다.

function

로딩 페이지 애니메이션 효과

'머로더즈 맵'을 실행하면 모든 사람들의 움직임을 알 수 있도록 '발자국'이 움직이는데, 그것을 로딩 페이지에 적용시켜 유저가 지도 안에서 실제로 움직이고 있다는 것처럼 느낄 수 있도록 표현하고 싶었습니다.

marauders

marauders

HTML5/CSS
  
  <div class="loading">
    <div class="ani">
      <div class="rotate-wrap">
        <p class="rotate">
          <span>m</span><span>a</span><span>r</span><span>a</span
          ><span>u</span><span>d</span><span>e</span><span>r</span
          ><span>s</span>
        </p>
        <p class="rotate">
          <span>m</span><span>a</span><span>r</span><span>a</span
          ><span>u</span><span>d</span><span>e</span><span>r</span
          ><span>s</span>
        </p>
      </div>
    </div>
  </div>

  //css
  .ani {
    background: url("https://marautube.herokuapp.com/loading.png") no-repeat 0
      0;
    background-size: 1287px 127px;
    position: relative;
    z-index: 99999999;
    width: 127px;
    height: 127px;
    animation: loading 2000ms infinite steps(10);
  }
  @keyframes loading {
    100% {
      background-position: -1287px 0;
    }
  }
  

서클 주위를 도는 글자는 커서에서 만든 방식대로 구현했습니다.
발자국이 움직이는 애니메이션은 CSS keyframessteps를 이용해서 10프레임에 거쳐 background positionwidth 값만큼 보여지며 동작되도록 했습니다.

Javascript
window.addEventListener("load", () => {
  body.classList.remove("before-load");
  document.querySelector(".loading").addEventListener("transitionend", (e) => {
    body.removeChild(e.currentTarget);
  });
});

loading이 완료되면 body에 지정했던 before-load클래스를 제거하고 transition애니메이션이 전부 재생된다면, body안에 있는 .loading 태그를 전부 삭제해서 보이지 않도록 했습니다.

marautube

function/design

로그인 했을 때 헤더의 변화

로그인을 했을 때는 유저의 프로필 이미지와 닉네임이 기본으로 출력됩니다. 또한 drop down을 사용하여 프로필을 누르면 메뉴가 나옵니다. 이 곳에서 업로드, 비디오 녹화, 내 채널, 회원 정보 수정, 로그아웃을 할 수 있습니다.

이런 자잘한 것에도 컨셉을 유지하기 위하여 회원 정보 수정을 폴리주스 마법약으로 로그아웃을 마법의 장난 끝이라 지정했습니다.

marautube

function/design

404 에러 페이지

404 에러 페이지도 디자인했습니다. 끝까지 모티브이자 컨셉 유지를 위하여 '머로더즈 맵'에서 나온 스네이프 교수의 호기심을 영원히 피하게 하는 마법이 출력되도록 했습니다.

스네이프 교수의 이미지는 gif로 움직이며, '마법의 장난 다시 시작'을 누르면 인트로 화면으로 넘어갑니다.