[SEO] 실제 구현해보기

채연·2024년 6월 14일
3

목록 보기
26/26

앞선 포스팅에선 SEO 최적화하는 방법을 알아보았다.
이젠 실제로 구현해볼 것이다!
요샌 회사든 개인 플젝이든 next.js로 작업하고 있어서 next.js 14버전으로 해보장

터미널에 npx create-next-app seo를 입력하여
프로젝트 이름 seo로 next 를 설치해주었다.

1. title, description

제일 먼저 해볼 것은 metadata 설정해보기!

사실 cra 하면 layout.tsx에 metadata가 설정된 채로 나온다.
하지만 이 root layout 페이지는 제일 최상단에 위치하므로 우선순위가 제일 낮음

우리 프로젝트의 목적을 넣어준다
지금은 SEO가 목적이니 title에 SEO 적어주고 설명도 아무거나 적어주면 끝!

title과 description이 잘 적용된 모습이다!

이건 정적 상황에 적용된 title, description이고 이번엔 동적 상황에도 적용해보자.

동적의 상황의 예시로는 서버에서 값을 불러오는 방식이 있다.

[id]안에 page.tsx로 페이지 하나파주고
api 폴더 아래에 route.ts로 서버 코드도 하나 파준다.

route.ts

import { NextResponse } from 'next/server';

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const name = searchParams.get('name');

  const productName = `Product ${name}`;
  return NextResponse.json(productName);
}

쿼리스트링으로 name 값을 넣어주면, Product {name} 으로 반환하는 코드.

page.tsx

import type { Metadata, ResolvingMetadata } from 'next';

type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

export async function generateMetadata(
  { params }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  const id = params.id;
  const product = await fetch(`http://localhost:3000/api?name=${id}`).then(
    (res) => res.json()
  ); 

  return {
    title: {product}
  };
}

export default function Page() {
  return <div></div>;
}

title에 '상품정보 :' 적어주고 product를 동적으로 받아온다.

2. open graph protocol

open graph protocol을 적용하는 방법은 openGraph 객체를 넣어주면 된다!

export async function generateMetadata(
  { params }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  const id = params.id;
  const baseURL = 'https://seo-fawn.vercel.app';
  const product = await fetch(`${baseURL}/api?name=${id}`).then((res) =>
    res.json()
  );
  const previousImages = (await parent).openGraph?.images || [];

  return {
    metadataBase: new URL(baseURL),
    title: `${product}`,
    openGraph: {
      title: '저랑 공부하실래용?',
      description: '챕터 1. SEO',
      images: [
        {
          url: '/opengraph-image.png',
          alt: '이미지 설명',
        },
      ],
    },
    twitter: {
      title: '트위터 하이!',
      description: '챕터 1. SEO',
      images: [
        {
          url: '/twitter-image.png',
          alt: '이미지 설명',
        },
      ],
    },
  };
}

배포를 한 후 소스를 보면, 잘 적용된 모습을 볼 수 있다.
meetadataBase는 baseURL을 정해주는 역할이다! 본인의 url을 넣어주면 된다.

실제 트위터에 넣어봤더니 잘 적용된 모습

근데 카톡은 왜인지 적용이 안 된당...

찾아보니, 카카오톡에 og가 캐시되어 있어서 그랬던 것!

카카오 developer 링크에 들어가서 로그인 후 og 캐시 지우기를 클릭하면 위의 사진과 같이 잘 나온다.

위는 캐시 지우기 전, 아래는 캐시 지운 후.

다른 곳들도 og 확인방법

하나하나 내가 게시글 해보는 수밖에 없나 싶었는데, 역시 할 수 있는 방법이 있었다!

실험 사이트 에 들어가서 링크를 입력하면 각 페이지들에 어떻게 보이는지 확인할 수 있다.

페이스북, 링크드인, 디스코드 모두 잘 나오는 것을 확인 :D

3. 검색엔진에 사이트 등록

다 설정이 완료되었으면, 검색엔진에 사이트를 등록해줘야 검색엔진이 크롤링을 할 수 있다.

검색엔진에 사이트 등록 글을 보며 참고하여 진행하였다.

이슈사항

소유권 확인을 하는 부분에서 엄청 헤맸다.. 한 2-3시간정도 폭풍 구글링한듯...ㅠㅠ

  verification: {
    google: [google-code],
    other: {
      'naver-site-verification':[naver-code],
    },
  },

결과적으로는 metadata 넣는 곳에 이렇게 넣으니 소유권 확인을 할 수 있었다..!
next.js의 특성 때문에 조금 헤맸었던 것 같다..

아무튼 성공!
하루, 이틀정도 걸린다는데 내일 검색해봐야겠당

4. robots.ts

robots.txt 파일도 next.js에서는 설정하는 방법이 따로있다
공식문서 참고하여 만들어주었다.

import { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
    },
  };
}

잘 적용된 모습도 확인해준다.

5. metadata robots

metadata의 robots로도 index 여부, follow 여부를 설정해줄 수 있다.

  robots: {
    index: true,
    follow: true,
  },

이것또한 next.js에서는 metadata에 객체로 적어준다.

속성 정보

all : 색인 및 크롤링에 제한이 없는 기본값. index, follow를 동시에 적용한 것과 동일
index : 페이지가 색인되어 검색 결과에 표시될 수 있음
noindex : 검색 결과에 페이지를 표시하지 않음
follow : 페이지를 포함해 링크된 페이지를 검색할 수 있음
nofollow : 페이지를 포함해 링크된 페이지를 수집하지 않음
none : noindex, nofollow를 동시에 적용한것과 동일
noarchive : 비표준, 페이지를 캐시하지 않음
nosnippet : 비표준, 검색 결과에 페이지에 대한 스니핏 혹은 미리보기를 지원하지 않음

출처 : Spemer(Hyouk Seo)

적용 결과 확인 :D

6. sitemap

공식문서 보면서 설정해보자!

const Sitemap = async (): Promise<MetadataRoute.Sitemap> => {
  return [
    {
      url: BASE_URL,
      lastModified: new Date(),
      changeFrequency: 'always',
      priority: 0.5,
    },
  ];
};

export default Sitemap;

url엔 BASE_URL 상수화 해뒀던 거 적어주고,
lastModified에는 수정날짜가 들어가도록 해준다.
changeFrequency는 얼마나 이 페이지가 자주 바뀌는지 여부!
-> 로봇이 참고만 하고 그대로 적용하지는 않는다고 한다.
priority는 이 페이지의 우선순위이다.

동적 sitemap

이러면 또 해봐야 할 고민이 있다.

우리는 /water, /paper 과 같이 상품 페이지를 만들 것인데, 동적인 경우에는 어떻게 해줄 수 있을까?

import { MetadataRoute } from 'next';
import { BASE_URL } from './constant';

export const getProducts = () => {
  return fetch(`${BASE_URL}/api/all`)
    .then((res) => {
      if (!res.ok) {
        return Promise.reject();
      }
      return res.json();
    })
    .catch(() => {
      return [];
    });
};

const Sitemap = async (): Promise<MetadataRoute.Sitemap> => {
  const products: string[] = await getProducts();

  const blogPosts = products.map((post) => ({
    url: `${BASE_URL}/${post}`,
    lastModified: new Date(),
    changeFrequency: 'always' as 'always',
    priority: 1,
  }));

  return [
    {
      url: BASE_URL,
      lastModified: new Date(),
      changeFrequency: 'always',
      priority: 0.5,
    },
    ...blogPosts,
  ];
};

export default Sitemap;

API를 불러와서 동적으로 이와 같이 처리가 가능하다!
상품 페이지가 중요도가 더 높으므로 priority를 1로 설정해준다.

결과!

--
+) 추가
이동할 때는 Link 태그 사용하기

+) 추가

성공해따!!!

profile
Hello Velog

0개의 댓글