NextJs 를 사용하는 가장 큰 이유는, 결국 seo 친화적인
싸이트를 만들기 위해서다.
SEO 는 쉽게 말해서, 구글과 네이버에서 검색엔진이
잘 찾아주는 싸이트냐인데,
개발자에게도 중요하지만, 좀 더 확장적으로 마케터나
사업자로써 본다면, SEO 는 상당히 중요한 개념으로써
우리가 "컨텐츠 마케팅이나, 퍼포먼스 마케팅"도 해야하지만
SEO 가 더 해져야 여러모로 우리하다고 할 수 있는데,
REACT 의 단점으로는 이런 SEO 적용이 상당히
불편하게 되어있고 별다른 설정없이는
SEO 에 매우 불리하다는 데 있다.
SEO 전문가를 따로 고용하는 회사들도 상당수 있기때문에
SEO 의 중요성은 두말하면 잔소리고, 쉽게 말해,
네이버 블로그에서 저품질 블로그마냥 SEO 를 제대로
안하면 이런 싸이트가 된다는 것이다.
NEXTJS 의 다른 장점들도 많지만, 무엇보다 이 SEO 를
매우 편리하게 해주는 SSG, SSR 기능, 그리고 PAGE 단위의 라우팅이 가능한 시스템이 NEXTJS 의 핵심 기능 중 핵심기능이라 할 수 있다.
SSG 는 HTML 을 미리 빌드해서 STATIC 하게 보여주는 것이고, (바뀌지 않을 내용들)
SSR 은 상세페이지, 그래프 차트등 새롭게 데이터를
받아와야하는 곳에 쓰인다.
크롬 자바스크립트 기능을 disable 하고 다음을 테스트 해보자.
import Link from "next/link";
export default function Index() {
return (
<div className="App">
<Link href="/Test">
<a>Test 페이지로 이동</a>
</Link>
</div>
);
}
import React, { useEffect, useState } from "react";
import axios from "axios";
const Test = () => {
const [list, setList] = useState([]);
useEffect(() => {
const getList = async () => {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/posts`
);
console.log(data);
setList(data);
};
getList();
}, []);
return (
<div>
<h1>Test</h1>
{list.length &&
list
.slice(0, 10)
.map((item: { id: number; title: string }) => (
<li key={item.id}>{item.title}</li>
))}
</div>
);
};
export default Test;
결과는 "자바스크립트" 를 disabled 했기때문에,
0 이 된다.
indext.tsx 는 그대로 두고,
test.tstx 만 바꿔보자.
import React, { useEffect, useState, FC } from "react";
import { GetStaticProps } from "next";
import axios from "axios";
interface Props {
list: { id: number; title: string }[];
}
const SsgTemplate = ({ list }: Props) => {
return (
<div className="About">
<h1>Test</h1>
{list.length &&
list.slice(0, 10).map((item) => <li key={item.id}>{item.title}</li>)}
</div>
);
};
export default SsgTemplate;
export const getStaticProps: GetStaticProps = async () => {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/posts`
);
console.log(data[1]);
return {
props: {
list: data,
},
};
};
그리고 크롬의 자바스크립트는 disabled 로 두자.
리스트가 제대로 나타나는 것을 확인했다.
단, constole.log 는 찍히지 않았다.
즉, 일반 api 불러오듯 하면,
SEO 기능이 최적화가 되지 않고, SSG 시에는
SEO 기능이 최적화 될 수 있음을 우리는 예측할 수 있다.
다만, 자바스크립트 기능이 비활성화 됨을 알 수 있다.
루트 폴더에 ssr 폴더 하나를 생성하고,
[id].tsx 파일 하나를 생성한다. (다이나믹 라우터 기능을 위해)
ssg.tsx 파일을 다음과 같이 변경한다.
import React, { useEffect, useState } from "react";
import { GetStaticProps } from "next";
import Link from "next/link";
import axios from "axios";
interface Props {
list: { id: number; title: string }[];
}
const SsgTemplate = ({ list }: Props) => {
return (
<div className="About">
<h1>Test!</h1>
{list.length &&
list.slice(0, 10).map((item) => (
<li key={item.id}>
<Link href={`ssr/${item.id}`}>{item.title}</Link>
</li>
))}
</div>
);
};
export default SsgTemplate;
export const getStaticProps: GetStaticProps = async () => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/posts`);
const data = res.data;
console.log(data[1]);
return {
props: {
list: data,
},
};
};
클릭하면, ssr의 해당 id 로 이동하게 만들었다.
ssr/[id].tsx
import React, { useEffect, useState, FC } from "react";
import { GetServerSideProps } from "next";
import axios from "axios";
interface Props {
item: { title: string; body: string; id: number };
}
const SsrTemplate = ({ item }: Props) => {
return (
<div className="Detail">
<h1>{item.title}</h1>
<p>{item.body}</p>
<p>{item.id}번째 게시글</p>
</div>
);
};
export default SsrTemplate;
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const id = ctx.params?.id;
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts/${id}`
);
const data = res.data;
console.log(data);
return {
props: {
item: data,
},
};
};
자바스크립트 disable 을 하더라도,
결과 : 화면에 타이틀과 body, id 에 해당 text 들이
잘 렌더링 될뿐 아니라,
console.log(data); 도 잘 동작하는 것을 확인할 수 있다.
다이나믹 라우터를 이용하더라도, ssg 를 활용하고
싶을 수 있다. 이때 getStaticPaths 를 활용하면 된다.
pathTemplate 폴더를 하나 생성하고,
[id].tsx 파일을 하나 생성해서 다음을 copy+paste 해보자.
import React from "react";
import { GetStaticProps, GetStaticPaths } from "next";
import axios from "axios";
interface Props {
item: { title: string; body: string; id: number };
}
const pathTemplate = ({ item }: Props) => {
return (
<div>
{item && (
<div className="Detail">
<h1>{item.title}</h1>
<p>{item.body}</p>
<p>{item.id}번째 게시글</p>
</div>
)}
</div>
);
};
export default pathTemplate;
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [
{ params: { id: "1" } },
{ params: { id: "2" } },
{ params: { id: "3" } },
{ params: { id: "4" } },
{ params: { id: "5" } },
],
fallback: true, // --> false 시 1,2,3외에는 404
};
};
export const getStaticProps: GetStaticProps = async (ctx) => {
const id = ctx.params?.id;
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts/${id}`
);
const data = res.data;
return {
props: {
item: data,
},
};
};
getStaticPaths 에서 paths 를 통해 미리 ssg 페이지를
만들어낼 수 있고,
fallback 옵션에 따라, 그 외 다이나믹 라우트로
들어온 경우 false 는 404 페이지를 보여주고,
true 만 ssg 페이지를 하나 더 만들어주는 것을 확인할
수 있다.
(확인을 위해선 생성된 .next 폴더의 server/pages 폴더를 확인해보자.)
fallback 옵션을 주지 않으면 기본 true 상태로
prefetch 기능이 활성화 된다.
첫 렌더링시 뷰포트에 보이는 목록 또는 스크롤 하여
랜더링 되는 목록들은 next/link 가 prefetch 하여
ssg 페이지들이 생성된다.
다이나믹 라우터가 아닌 경우, SEO => SSG
다이나믹 라우터를 사용하는데, 어느정도 정해진 상품군 => getStaticPaths
차트나, 변화가 심한 경우 => SSR 을 사용하면 될듯 하다.
SEO 기능은
META 설정등이 중요하기때문에,
추후 NEXT-SEO 라이브러리를 소개해 추가하도록 하겠다.
(SPA 용 sitempa 제네레이션과 네이버, 구글에 등록하는 것도
차차 블로그에 올리겠다)
( SEO 는 백링크나 싸이트 유입량, 체류랑에도 영향을 받기때문에 방금 소개한, SSG, SSR 만이 전부는 아니다.
SSG 나 SSR 은 의미에 맞는 마크업을 하는 것이
SEO 에 영향을 미치는 것과 비슷한 영향을 미친다 할
수 있을 것이다.)
구글등의 검색엔진은 SPA 도 잘 해석하고 있고,
마인드케어센터도 잘 노출되고 있다.
네이버 검색엔진도 SPA 가 잘된다고 하고,
마인드케어센터도 잘 나오기는 하지만
아직 많은 사람들이 네이버 검색엔진의 성능을
제대로 믿지 못하고 있으므로, SSG 나 SSR 로
로봇이 잘 읽을 수 있는 환경등을 만들자.