본문 바로가기
Tutorial/youtube

08. 나만의 유튜브 사이트 만들기 : 헤더 영역 데이터 작업

by @webstoryboy 2023. 9. 4.
Tutorial/Portfolio

나만의 유튜브 사이트 만들기

by @webs 2023. 09. 01.
08
나만의 유튜브 사이트 만들기 : 헤더 영역 데이터 작업
난이도 중간

소개

안녕하세요! 웹스토리보이입니다. 이 강의는 React 프레임워크와 YouTube API를 이용하여 자신만의 간단한 영상 사이트를 만들어보겠습니다. React의 기본 개념을 이해하고, 컴포넌트를 구조화하고 상태를 관리하는 방법을 학습하게 될 것입니다. 또한 YouTube Data API를 활용하여 외부 데이터를 가져오는 방법을 익히고, API 응답을 처리하여 사용자에게 의미 있는 정보를 제공하는 방법을 이해하게 됩니다. 이로써 자신만의 유튜브 사이트를 만들고, 활용해보는 것을 목표로 합니다. 그럼 한번 시작해볼까요? 🥳

인덱스

  • 1. 셋팅하기
    • 1_1. Node.js 설치
    • 1_2. Vscode 설치
    • 1_3. React.js 설치
  • 2. 라이브러리 설치하기
    • 2_1. 폴더 정리하기
    • 2_2. 라이브러리 설치하기
  • 3. Git 연동하기
    • 3_1. 저장소 만들기
    • 3_2. 모든 파일 올리기
    • 3_3. 깃 상태 확인하기
  • 4. SCSS 셋팅하기
    • 4_1. SCSS 설정하기
    • 4_2. style.scss 설정하기
    • 4_3. fonts.scss 설정하기
    • 4_4. vars.scss 설정하기
    • 4_5. reset.scss 설정하기
    • 4_6. mixin.scss 설정하기
    • 4_7. common.scss 설정하기
  • 5. 페이지 만들기
    • 5_1. 페이지 만들기
    • 5_2. 페이지 컴퍼넌트 만들기
  • 6. 섹션 컴퍼넌트 구조화하기
    • 6_1. 전체 레이아웃 만들기
    • 6_2. 섹션 컴퍼넌트 만들기
  • 7. 헤더 영역 완성하기
    • 7_1. 헤더 영역 구조 잡기
    • 7_2. 헤더 영역 디자인 작업
  • 8. 헤더 영역 데이터 작업
    • 8_1. 헤더 영역 데이터 작업
    • 8_2. 반복문과 map()
    • 8_3. 메뉴 활성화하기
    • 8_4. 컴퍼넌트 세부화 시키기

8. 헤더 영역 데이터 작업

8_1. 헤더 영역 데이터 작업

이번에는 데이터를 따로 작업하겠습니다. 데이터를 따로 작업하면 수정하기 용이하고, 더 보기가 좋아집니다. 그전에 반복문과 map() 메서드에 대해서 먼저 이해를 해야 합니다. 우선 데이터화를 먼저 작업해 보겠습니다.

data폴더를 만들고 header.js 파일을 만듭니다.

  • src
    • assets
    • components
    • data
      • header.js
    • pages
    • App.js
    • index.js

header.js 파일은 다음과 같이 정리합니다.

import { CiBaseball } from "react-icons/ci";
import { CiCoins1 } from "react-icons/ci";
import { CiBoxes } from "react-icons/ci";
import { CiBullhorn } from "react-icons/ci";
import { CiCoffeeCup } from "react-icons/ci";
import { CiDumbbell } from "react-icons/ci";
import { CiFries } from "react-icons/ci";
import { CiMoneyBill } from "react-icons/ci";

import { AiFillGithub } from "react-icons/ai";
import { AiOutlineCodepen } from "react-icons/ai";
import { AiFillYoutube } from "react-icons/ai";
import { AiOutlineInstagram } from "react-icons/ai";

export const headerMenus = [
    {
        title: "웹스토리보이",
        icon: <CiBaseball />,
        src: "/"
    },
    {
        title: "추천 영상",
        icon: <CiMoneyBill />,
        src: "/today"
    },
    {
        title: "추천 개발자",
        icon: <CiCoins1 />,
        src: "/developer"
    },
    {
        title: "웹디자인기능사",
        icon: <CiBoxes />,
        src: "/webd"
    },
    {
        title: "웹표준 사이트",
        icon: <CiBullhorn />,
        src: "/website"
    },
    {
        title: "GSAP Parallax",
        icon: <CiCoffeeCup />,
        src: "/gsap"
    },
    {
        title: "포트폴리오 사이트",
        icon: <CiDumbbell />,
        src: "/port"
    },
    {
        title: "유튜브 클론 사이트",
        icon: <CiFries />,
        src: "/youtube"
    },
];

export const searchKeyword = [
    {
        title: "webstoryboy",
        src: "/search/webstoryboy"
    },
    {
        title: "HTML",
        src: "/search/html"
    },
    {
        title: "CSS",
        src: "/search/css"
    },
    {
        title: "JavaScript",
        src: "/search/javascript"
    },
    {
        title: "React.js",
        src: "/search/react.js"
    },
    {
        title: "Vue.js",
        src: "/search/vue.js"
    },
    {
        title: "Next.js",
        src: "/search/next.js"
    },
    {
        title: "Node.js",
        src: "/search/node.js"
    },
    {
        title: "SQL",
        src: "/search/sql"
    },
    {
        title: "portfolio",
        src: "/search/React Portfolio"
    },
    {
        title: "music",
        src: "/search/NewJeans"
    }
];

export const snsLink = [
    {
        title: "github",
        url: "https://github.com/webstoryboy",
        icon: <AiFillGithub />
    },
    {
        title: "youtube",
        url: "https://www.youtube.com/webstoryboy",
        icon: <AiFillYoutube />
    },
    {
        title: "codepen",
        url: "https://codepen.io/webstoryboy",
        icon: <AiOutlineCodepen />
    },
    {
        title: "instagram",
        url: "https://www.instagram.com/webstoryboy",
        icon: <AiOutlineInstagram />
    },
]

Header.jsx 파일은 다음과 같이 다시 정리하겠습니다. 리액트에서 페이지 이동 할 때에는 Link를 사용합니다. Link를 사용하기 위해서는 import를 하여야 합니다. 이렇게 작업하면 소스가 더 간결해지고 보기 편해집니다.

import React from 'react'

import { headerMenus, searchKeyword, snsLink } from "../../data/category";
import { Link } from 'react-router-dom';

const Header = () => {
    return (
        <header id='header' role='banner'>
            <h1 className='header__logo'>
                <a href="/">
                    <em></em>  
                    <span>webs<br />youtube</span>
                </a>
            </h1>
            <div className='header__menu'>
                <ul className='menu'>
                    {headerMenus.map((menu, key) => (
                        <li key={key}>
                            <Link to={menu.src}>
                                {menu.icon}{menu.title}
                            </Link>
                        </li>
                    ))}
                </ul>
                <ul className='keyword'>
                    {searchKeyword.map((keyword, key) => (
                        <li key={key}>
                            <Link to={keyword.src}>
                                {keyword.title}
                            </Link>
                        </li>
                    ))}
                </ul>
            </div>
            <div className='header__sns'>
                <ul>
                    {snsLink.map((sns, key) => (
                        <li key={key}>
                            <a href={sns.url} target="_blank" rel="noopener noreferrer" aria-label={sns.title}>
                                <span>{sns.icon}</span>
                            </a>
                        </li>
                    ))}
                </ul>
            </div>
        </header>
    )
}

export default Header

8_2. 반복문과 map()

리액트 작업시 반복문과 map() 메서드는 사용 빈도가 매우 높습니다. 잠깐 개념을 파악하고 넘어가겠습니다.

배열안에 데이터가 들어있습니다. for 문을 통해 데이터를 하나씩 호출합니다.

{
    const arr = [100, 200, 300, 400, 500, 600, 700, 800, 900];

    for(let i=0; i<arr.length; i++){
        console.log(arr[i]);
    }
    // 100 200 300 400 500 600 700 800 900
}   

이번에도 배열안에 데이터가 들어있습니다. map() 메서드를 통해 데이터를 하나씩 호출하겠습니다.

{
    const arr = [100, 200, 300, 400, 500, 600, 700, 800, 900];

    arr.map((element) => {
        console.log(element);
    });

    // [100, 200, 300, 400, 500, 600, 700, 800, 900];
}   

for문과 map() 메서드는 데이터 값을 출력하지만 map() 메서드는 출력한 값을 배열로 반환해주는 것에 차이점이 있습니다. 리액트에서 map() 메서드를 사용하는 이유는 출력한 데이터를 다시 배열 형태로 반환 받아서 화면에 편하게 출력하기 위함입니다. map() 메서는 인자 값으로 3가지 값을 출력할 수 있습니다.

{
    const arr = [100, 200, 300, 400, 500, 600, 700, 800, 900];

    arr.map((element, index) => {   
        console.log(index);
    });

    // 0 1 2 3 4 5 6 7 8

    arr.map((element, index, array) => {
        console.log(array);
    });

    // [100, 200, 300, 400, 500, 600, 700, 800, 900] * 9
}

8_3. 메뉴 활성화하기

메뉴를 클릭하면 해당 페이지로 이동으로 하고 해당 메뉴에는 클래스 active를 추가해서 현재 있는 곳을 표시해주겠습니다. 즉 메뉴 active를 활성화하겠습니다.

해당 사이트의 주속 값을 가져오기 위해서 useLocation()를 사용하겠습니다. 주소값고 메뉴의 주소값이 있으면 active를 붙여주는 삼항 연산자를 사용하였습니다. 키워드 메뉴도 동일하게 작업하였습니다.

이 부분을 추가했습니다. location.pathname === menu.src 이 부분이 일치하면 active가 추가되고, 아니면 빈문자열로 표현됩니다.

className={location.pathname === menu.src ? 'active' : ''}
import React from 'react'

import { headerMenus, searchKeword, snsLink } from "../../data/header";
import { Link, useLocation } from 'react-router-dom';

const Header = () => {
    const location = useLocation();

    return (
        <header id='header' role='banner'>
            <h1 className='header__logo'>
                <a href="/">
                    <em></em>  
                    <span>webs<br />youtube</span>
                </a>
            </h1>
            <div className='header__menu'>
                <ul className='menu'>
                    {headerMenus.map((menu, key) => (
                        <li key={key} className={location.pathname === menu.src ? 'active' : ''}>
                            <Link to={menu.src}>
                                {menu.icon}{menu.title}
                            </Link>
                        </li>
                    ))}
                </ul>
                <ul className='keyword'>
                    {searchKeyword.map((keyword, key) => (
                        <li key={key} className={location.pathname === keyword.src ? 'active' : ''}>
                            <Link to={keyword.src}>
                                {keyword.title}
                            </Link>
                        </li>
                    ))}
                </ul>
            </div>
            <div className='header__sns'>
                <ul>
                    {snsLink.map((sns, key) => (
                        <li key={key}>
                            <a href={sns.url} target="_blank" rel="noopener noreferrer" aria-label={sns.title}>
                                <span>{sns.icon}</span>
                            </a>
                        </li>
                    ))}
                </ul>
            </div>
        </header>
    )
}

export default Header

이렇게 하면 주소값과 메뉴의 활성화가 일치하게 됩니다.

youtube2023

8_4. 컴퍼넌트 세부화 시키기

헤더 컴퍼넌트를 세부화 시키겠습니다.

components > header 폴더를 만들고, Logo.jsx, Menu.jsx, Sns.js 파일을 만들겠습니다.

  • src
    • assets
    • components
      • header
        • Logo.jsx
        • Menu.jsx
        • Sns.jsx
      • section
    • data
    • pages

Logo.jsx 파일을 작성하겠습니다.

import React from 'react'

const Logo = () => {
    return (
        <h1 className='header__logo'>
            <a href='/'>
                <em aria-hidden='true'></em>
                <span>webs<br />youtube</span>
            </a>
        </h1>
    )
}

export default Logo

Menu.jsx 파일을 작성하겠습니다.

import React from 'react'

import { headerMenus, searchKeyword } from '../../data/header'
import { Link, useLocation } from 'react-router-dom'

const Menu = () => {
    const location = useLocation();

    return (
        <nav className='header__menu'>
            <ul className='menu'>
                {headerMenus.map((menu, key) => (
                    <li key={key} className={location.pathname === menu.src ? 'active' : ''}>
                        <Link to={menu.src}>
                            {menu.icon}{menu.title}
                        </Link>
                    </li>
                ))}
            </ul>
            <ul className='keyword'>
                {searchKeyword.map((keyword, key) => (
                    <li key={key} className={location.pathname === keyword.src ? 'active' : ''}>
                        <Link to={keyword.src}>
                            {keyword.title}
                        </Link>
                    </li>
                ))}
            </ul>
        </nav>
    )
}

export default Menu

Sns.js 파일을 작성하겠습니다.

import { snsLink } from '../../data/header'

const Sns = () => {
    return (
        <div className='header__sns'>
            <ul>
                {snsLink.map((sns, key) => (
                    <li key={key}>
                        <a 
                            href={sns.url} 
                            target='_black' 
                            rel='noopener noreferrer' 
                            aria-label={sns.title}
                        >
                            <span>{sns.icon}</span>
                        </a>
                    </li>
                ))}
            </ul>
        </div>
    )
}

export default Sns

components > section > Header.jsx 파일은 다음과 같이 수정합니다.

import React, { useState } from 'react'

import Logo from '../header/Logo';
import Menu from '../header/Menu';
import Sns from '../header/Sns';

const Header = () => {    
    return (
        <header id='header' role='banner' >
            <Logo />
            <Menu />
            <Sns />
        </header>
    )
}

export default Header

마무리

git 올리기

터미널에서 다음과 같이 작성하겠습니다. 새로운 페이지가 올라오는 것을 확인 할 수 있습니다.

webstoryboy@Webstoryboyui-iMac webs-youtube % git add .
webstoryboy@Webstoryboyui-iMac webs-youtube % git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
    (use "git restore --staged ..." to unstage)
        new file:   src/components/header/Logo.jsx
        new file:   src/components/header/Menu.jsx
        new file:   src/components/header/Sns.jsx
        modified:   src/components/section/Header.jsx
        new file:   src/data/header.js

webstoryboy@Webstoryboyui-iMac webs-youtube % git commit -m "헤더 영역 데이터 작업"
[main 1f73d2f] 헤더 영역 데이터 작업
    5 files changed, 205 insertions(+), 125 deletions(-)
    create mode 100644 src/components/header/Logo.jsx
    create mode 100644 src/components/header/Menu.jsx
    create mode 100644 src/components/header/Sns.jsx
    create mode 100644 src/data/header.js
webstoryboy@Webstoryboyui-iMac webs-youtube % git push -u origin main
Enumerating objects: 17, done.
Counting objects: 100% (17/17), done.
Delta compression using up to 8 threads
Compressing objects: 100% (11/11), done.
Writing objects: 100% (12/12), 2.35 KiB | 2.35 MiB/s, done.
Total 12 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To https://github.com/webstoryboy/webs-youtube.git
    cc0f081..1f73d2f  main -> main
branch 'main' set up to track 'origin/main'.

헤더 영역을 완성하였습니다. 메뉴를 데이터화여 불러오고 화면에 출력하는 부분은 앞으로도 자주 사용하게 될 것입니다. 아직은 어려울 수 있지만 익숙해지면 쉬업집니다. 익숙해지려면 반복적으로 작업하면 됩니다. 그럼 다음 시간에도 또 반복적으로 작업해볼까요? 수고하셨습니다 🤩


예제 목록

댓글