본문 바로가기
Tutorial/youtube2023

05. 나만의 유튜브 사이트 만들기 : 페이지 SEO 작업

by @webstoryboy 2023. 8. 30.
Tutorial/Portfolio

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

by @webs 2023. 09. 01.
05
나만의 유튜브 사이트 만들기 : 페이지 SEO 작업
난이도 중간

소개

안녕하세요! 웹스토리보이입니다. 이번에는 페이지 별로 SEO 기능을 추가해보겠습니다. 리액트를 싱글 페이지를 기본으로 하기 때문에 페이지가 없습니다. 페이지가 있는 것첨럼 타이틀과 설명을 추가할 것입니다. 그럼 시작해볼까요? 🫡

인덱스

  • 1. 셋팅하기
    • 1_1. Node.js 설치
    • 1_2. Vscode 설치
    • 1_3. React.js 설치
    • 1_4. 폴더 정리하기
    • 1_5. 필요한 파일 설치하기
  • 2. CSS 셋팅하기
    • 2_1. SCSS 설정하기
    • 2_2. style.scss 설정하기
    • 2_3. fonts.scss 설정하기
    • 2_4. vars.scss 설정하기
    • 2_5. reset.scss 설정하기
    • 2_6. mixin.scss 설정하기
    • 2_7. common.scss 설정하기
  • 3. 페이지 및 컴퍼넌트 만들기
    • 3_1. 페이지 만들기
    • 3_2. 컴퍼넌트 만들기
  • 4. 섹션 컴퍼넌트 만들기
    • 4_1. Main 컨퍼넌트 만들기
    • 4_2. Header 컨퍼넌트 만들기
    • 4_3. Header 컨퍼넌트 데이터화 하기
    • 4_4. Footer 컨퍼넌트 만들기
  • 5. 페이지 SEO 작업
    • 5_1. App.js 재구성
    • 5_2. main 컴퍼넌트 재구성
    • 5_3. 모든 페이지 재구성

5. 페이지 SEO 작업

리액트의 단점을 극복하기 위한 방법으로 페이지 별로 페이지에 대한 설명이나 타이틀을 다시 작업해주겠습니다. 모든 페이지가 왼쪽 메뉴를 기준으로 변하기 때문에 main 영역만 컴퍼넌트를 통해 테이터를 불러올 것입니다.

또한 React Suspense 기능을 사용하여 페이지 이동시에도 끊기지 않고, 비동기 데이터를 좀 더 효율적으로 관리해보겠습니다.

React.Suspense는 React에서 비동기적으로 데이터나 컴포넌트를 불러오는 과정에서 대기 상태를 처리하기 위한 컴포넌트입니다. 이 컴포넌트를 사용하면 데이터나 컴포넌트가 로딩되기를 기다릴 수 있으며, 로딩 중에는 대체 컨텐츠(로딩 스피너 또는 로딩 메시지)를 표시할 수 있습니다. React.Suspense를 사용하면 코드 스플리팅, 데이터 로딩, 서버 사이드 렌더링과 같은 시나리오에서 사용자 경험을 향상시키는 데 도움이 됩니다.

5_1. App.js 재구성

기존에 파일을 다음과 같이 재구성합니다. 컴포넌트를 비동기적으로 로드하고 Suspense 컴포넌트를 사용하여 로딩 중에 대기 상태를 처리하고 있습니다.

import React, { Suspense, lazy } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Main from './components/section/Main';

const Home = lazy(() => import('./pages/Home'));
const Today = lazy(() => import('./pages/Today'));
const Developer = lazy(() => import('./pages/Developer'));
const Webd = lazy(() => import('./pages/Webd'));
const Website = lazy(() => import('./pages/Website'));
const Gsap = lazy(() => import('./pages/Gsap'));
const Port = lazy(() => import('./pages/Port'));
const Youtube = lazy(() => import('./pages/Youtube'));
const Channel = lazy(() => import('./pages/Channel'));
const Video = lazy(() => import('./pages/Video'));
const Search = lazy(() => import('./pages/Search'));
const Not = lazy(() => import('./pages/Not'));

const App = () => {
    return (
        <BrowserRouter>
            <Suspense fallback={<Main />}>
                <Routes>
                    <Route path='/' element={<Home />} />
                    <Route path="/today" element={<Today />} />
                    <Route path="/developer" element={<Developer />} />
                    <Route path="/webd" element={<Webd />} />
                    <Route path="/website" element={<Website />} />
                    <Route path="/gsap" element={<Gsap />} />
                    <Route path="/port" element={<Port />} />
                    <Route path="/youtube" element={<Youtube />} />
                    <Route path='/channel/:channelId' element={<Channel />} />
                    <Route path='/video/:videoId' element={<Video />} />
                    <Route path='/search/:searchId' element={<Search />} />
                    <Route path="*" element={<Not />} />
                </Routes>
            </Suspense>
        </BrowserRouter>
    );
}

export default App;
  • BrowserRouter: React Router에서 사용되는 브라우저 라우터 컴포넌트입니다. 브라우저 히스토리 API를 사용하여 라우팅을 처리합니다.
  • Suspense: Suspense 컴포넌트는 비동기적으로 로드되는 컴포넌트의 로딩 상태를 관리하는 데 사용됩니다. 여기서는 fallback 속성을 사용하여 로딩 중에 <Main /> 컴포넌트를 표시합니다.
  • Routes: Routes 컴포넌트는 각 경로에 대한 라우트를 정의하고, 해당 경로에 접근할 때 렌더링될 컴포넌트를 결정합니다.
  • Route: Route 컴포넌트는 특정 경로에 대한 라우팅 규칙을 설정하며, element 속성을 통해 해당 경로에 접근할 때 렌더링될 컴포넌트를 지정합니다.
  • lazy(): lazy() 함수는 코드 스플리팅을 위해 사용되며, 비동기적으로 컴포넌트를 로드합니다. 컴포넌트가 필요할 때 비동기적으로 불러와서 번들 크기를 줄일 수 있습니다.
  • fallback: fallback 속성은 Suspense 컴포넌트에서 로딩 중에 표시할 컴포넌트를 지정합니다. 여기서는 <Main /> 컴포넌트가 로딩 중에 보여집니다.

5_2. main 컴퍼넌트 재구성

main이라는 컴퍼넌트를 모든 페이지에서 사용하기 때문에 여기에 타이틀과 설명을 추가하는 SEO 잡업을 하였습니다.

import React from 'react'
import { Helmet, HelmetProvider } from 'react-helmet-async'

import Header from './Header'
import Footer from './Footer'

const Main = ( props ) => {
    return (
        <HelmetProvider>
            <Helmet 
                titleTemplate="%s | Webs Youtube" 
                defaultTitle="Webs Youtube" 
                defer={false}
            >
                {props.title && <title>{props.title}</title>}
                <meta name="description" content={props.description} />
            </Helmet>

            <Header />
            <main id="main" role="main">
                {props.children}
            </main>
            <Footer />
        </HelmetProvider>
    )
}

export default Main
  • HelmetProvider: react-helmet-async의 HelmetProvider 컴포넌트를 사용하여 Helmet 컴포넌트를 초기화합니다. Helmet은 페이지의 헤드 태그에 메타 데이터와 타이틀을 동적으로 추가하거나 변경하는 데 사용됩니다.
  • Helmet: Helmet 컴포넌트를 사용하여 페이지의 메타 데이터와 타이틀을 설정합니다. 이 컴포넌트를 사용하면 서버 사이드 렌더링(SSR) 및 동적 페이지 제목 설정과 같은 작업을 쉽게 수행할 수 있습니다. 주로 title, meta, link, script 등의 태그를 동적으로 조작하는 데 사용됩니다.
  • titleTemplate: 페이지 타이틀의 템플릿을 설정합니다. %s는 나중에 실제 타이틀로 대체됩니다.
  • defaultTitle: 기본 타이틀을 설정합니다. 페이지 타이틀이 없는 경우 사용됩니다.
  • defer: true로 설정하면 렌더링 전까지 <Helmet> 컴포넌트가 기다립니다.
  • <title>과 <meta> 태그: props로 전달된 title과 description 값을 사용하여 페이지의 타이틀과 메타 설명을 설정합니다. props.title과 props.description이 없는 경우, 기본값으로 "Webs Youtube"가 타이틀로 설정됩니다.

5_3. 모든 페이지 재구성

이제 나머지 페이지도 main 컴퍼넌트를 불러오고 페이지 별로 타이틀과 설명을 추가하면 됩니다. 그럼 페이지가 변경되면 타이틀이 변경되는 것을 확인할 수 있습니다.

import React from 'react'
import Main from '../components/section/Main'

const Channel = () => {
    return (
        <Main 
            title = "유튜브 채널"
            description="유튜브 채널페이지입니다.">
            Channel
        </Main>
    )
}

export default Channel
import React from 'react'
import Main from '../components/section/Main'

const Developer = () => {
    return (
        <Main 
            title = "추천 개발자"
            description="오늘의 추천 개발자 유튜버입니다.">
            Developer
        </Main>
    )
}

export default Developer
import React from 'react'
import Main from '../components/section/Main'

const Gsap = () => {
    return (
        <Main 
            title = "GSAP 사이트"
            description="GSAP 사이트 튜토리얼 강의입니다.">
            Gsap
        </Main>
    )
}

export default Gsap
import React from 'react'
import Main from '../components/section/Main'

const Home = () => {
    return (
        <Main 
            title = "웹스토리보이 유튜브"
            description="웹스토리보이 유튜버에 오신 것을 환영합니다.">
            Home
        </Main>
    )
}

export default Home
import React from 'react'
import Main from '../components/section/Main'

const Not = () => {
    return (
        <Main 
            title = "잘못된 페이지"
            description="접근이 잘못된 페이지입니다.">
            Not
        </Main>
    )
}

export default Not
import React from 'react'
import Main from '../components/section/Main'

const Port = () => {
    return (
        <Main 
            title = "포트폴리오 사이트"
            description="포트폴리오 사이트 튜토리얼 강의입니다.">
            Port
        </Main>
    )
}

export default Port
import React from 'react'
import Main from '../components/section/Main'

const Search = () => {
    return (
        <Main 
            title = "유투브 검색"
            description="유튜브 검색 결과 페이지입니다.">
            Search
        </Main>
    )
}

export default Search
import React from 'react'
import Main from '../components/section/Main'

const Today = () => {
    return (
        <Main 
            title = "추천 영상"
            description="오늘의 추천 유튜브 영상입니다.">
            Today
        </Main>
    )
}

export default Today
import React from 'react'
import Main from '../components/section/Main'

const Video = () => {
    return (
        <Main 
            title = "유튜브 비디오 영상"
            description="유튜브 비디오 영상을 볼 수 있습니다.">
            Video
        </Main>
    )
}

export default Video
import React from 'react'
import Main from '../components/section/Main'

const Webd = () => {
    return (
        <Main 
            title = "웹디자인 기능사"
            description="웹디자인 기능사 튜토리얼 강의입니다.">
            Webd
        </Main>
    )
}

export default Webd
import React from 'react'
import Main from '../components/section/Main'

const Website = () => {
    return (
        <Main 
            title = "웹표준 사이트"
            description="웹표준 사이트 튜토리얼 강의입니다.">
            Website
        </Main>
    )
}

export default Website
import React from 'react'
import Main from '../components/section/Main'

const Youtube = () => {
    return (
        <Main 
            title = "유튜브 사이트"
            description="유튜브 사이트 튜토리얼 강의입니다.">
            Youtube
        </Main>
    )
}

export default Youtube

마무리

리액트의 단점이라고 꼽히는 SEO에 대해 약간의 대처를 하였습니다. 모든 페이지에 대해 메타 태그를 동적으로 설정하였고, main 컴퍼넌트와 비동기 방식으로 불러오기 위해 리액트 Suspense 기능을 사용하였습니다. 이 부분을 이용하여 화면 전환간 끊김 현상을 줄일 수 있고 ux를 조금 더 개선할 수 있습니다. 그럼 오늘도 수고하셨습니다. 🤭


예제 목록

댓글