1. CRA와 CNA 비교하기
react와 Next.js가 생성한 프로젝트를 보면 다름이 보인다. react에서는 pages폴더가 없는데, next에는 존재한다. 여기에 파일을 만들면 자동으로 라우팅되어 url에 해당 파일의 이름을 치면 해당 사이트로 이동된다.
1) 페이지 비교하기(CRA)
개발자 도구를 열어 Network 탭을 눌러, 첫 페이지의 response탭을 보면 첫 페이지 요청으로 받아온 HTML문서의 내용을 확인해볼 수 있다. 보면 우리가 App.js 파일에 적어준 내용이 하나도 들어있지 않다. id가 root인 텅 비어있는 DIV 태그만 반환하고 있음과 밑에 번들링된 static/js 경로의 'bundle.js' 파일을 전달하고 있다.
CSR에서 빈 HTML 문서를 먼저 반환하고, 그 후 React로 코딩한 여러 컴포넌트들을 번들링하여 전달한다. 그리고 브라우저는 해당 js 파일을 전달받은 이후에 유저에게 보여줄 화면을 렌더링 한다. 때문에 우리가 아무리 복잡한 파일을 코딩하여 구성하더라도 CSR에서 처음 로딩되는 HTML 문서는 빈 페이지일 수 밖에 없다. 그래서 CSR 방식의 SPA는 SEO에서 약하다.
CSR 방식에서 번들링이 된 JS 파일을 전달 받아야 정상적인 렌더링이 가능하다. JS 파일을 받아야 렌더링 되는지 보기 위해 source 탭에서 js 파일 로드를 제외하고 새로고침을 하면 아래와 같이 noscript 태그의 내용이 뜨는 것을 확인할 수 있다.
추가) 자바 스크립트 비활성화
여기서 noscript가 뜨는 것을 보고 싶다면 개발자 도구를 활용해야 한다. 해당 지문이 이해가 되지 않는다면 참고의 사이트를 붙여놓겠다. 그것을 참고하는 것을 추천한다.
- 개발자 도구를 연다.
- 윈도우인 경우 Control+Shift+P, 맥은 Command+Shift+P 버튼을 누른다.
- 명령 메뉴가 나오게 되는데, javascript를 쳐서 debugger로 되어 있는 메뉴를 Enter버튼을 눌러 비활성화한다.
4. 그러고 나면 Sources탭에 비활성화된 것을 알 수 있다. 또한 주소 표시줄에도 비활성화가 되었다는 아이콘이 뜨게 된다.
5. 만약, 비활성화가 된지 모르겠다면 새로고침을 해보면 된다. 그러면 아래와 같이 사이트에 뜨게 된다.
2) CNA 페이지
위와 같이 똑같이 개발자 도구에서 network탭을 확인해보고, 서버에서 HTML 문서가 렌더링이 된 후 브라우저에게 전달된 것을 볼 수 있다. js 파일 로드 기능을 꺼도 html구조가 정상적으로 사용되는 것을 확인할 수 있다.
개발자가 빌드 시 pre-render page를 만들어 정적인 페이지를 가지고 있다. 클라이언트에서 페이지 요청 시 이 페이지를 로드하여 보여준다.
페이지를 미리 생성하여 가지고 있기 때문에 클라이언트 요청에 대한 응답이 빠르다. 빌드 할 때 페이지가 생성되므로 변경사항이 생기게 되었을 때는 next.js의 특정 함수를 활용하여 변경사항을 읽은 다음 페이지를 생성한다.
사진으로 요약하기
CRA와 CNA를 사진 한장으로 이해하자면 이 사진이 적절할 것 같다. react app의 겨우 pre-rendering을 하지 않기에 javascript를 비활성화하면 화면에 뜨는 것이 없다. 그렇지만 Next.js의 경우 사전에 로딩된 것이 있어, HTML을 보여주게 된다. 그래서 자바스크립트를 비활성화해도 화면을 구성할 수 있다.
위 사진처럼 HTML을 미리 렌더링하고, 그 뒤에 요청이 오면 Chunk 단위로 javascript를 보내주어 이벤트가 작동하게 되는 것이 Hydration이며, Next.js에서 사용되는 방법입니다.
2. SSG적용하기
1) getStaticProps
첫 요청에 하나의 정적 HTML 문서를 생성 후 그 이후의 요청엔 계속 동일한 문서를 반환하면 된다. 화면에 뿌려줄 데이터 역시 미리 서버에서 처리한다. 그리고 완성된 정적 HTML 문서를 반환하면 SEO 적용이 용이하게 된다.
SSG를 사용할 때 getStaticProps를 사용한다.
- promise를 반환한다.
- 클라이언트 측에 실행되지 않고 실행된다.
- 컴포넌트 함수에서 사용할 props를 반환한다.
const Component = (props) => {
return <List listData={props.listData} />;
};
export async function getStaticProps(){
// fetch 통신처리 처리후 DUMMY_LIST을 가져왔다고 가정
const DUMMY_LIST = fetch(...)
return {
props:{
listData : DUMMY_LIST,
}
};
}
💡 개발모드에서는 SSG로 작성하더라도 매 요청마다 페이지를 재생성한다. npm run build 후에 npm start 로 실행하게 되면 첫 요청때 해당 페이지를 pre-rendering 하여 정적 문서로 생성해두고 그 다음 요청때는 생성된 문서를 반환한다.
2) getStaticPaths
💡 개발모드에서는 SSG로 작성하더라도 매 요청마다 페이지를 재생성한다. npm run build 후에 npm start 로 실행하게 되면 첫 요청때 해당 페이지를 pre-rendering 하여 정적 문서로 생성해두고 그 다음 요청때는 생성된 문서를 반환한다.
- fallback : NextJS에게 paths배열이 모든 지원되는 매개변수를 저장 할 지 아니면 일부만 저장할 지 알려주는 속성이다.
- false : getStaticPaths로 리턴되지 않는 것은 모두 404 페이지가 뜬다.
- true : getStaticPaths로 리턴되지 않는 것은 404로 뜨지 않고 , fallback 페이지가 뜬다.
if(router.isFallback){ return <div>Loading...</div> }
- path : pre-render를 할 것을 결정한다.
import React from "react";
import axios from "axios";
const DetailStatic = ({ item }) => {
return (
<div>
{item && (
<div className="Detail">
<h1 style={{ color: "#fff" }}>with Static Generation</h1>
<h1>{item.title}</h1>
<p>{item.body}</p>
<p>{item.id}번째 게시글</p>
</div>
)}
</div>
);
};
export default DetailStatic;
export const getStaticPaths = async () => {
return {
paths: [
{ params: { id: "1" } },
{ params: { id: "2" } },
{ params: { id: "3" } },
],
fallback: true,
};
};
export const 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,
},
};
};
getStaticProps로 데이터를 가져온다. static이기 때문에 매 요청마다 렌더링되지 않고 가져온 데이터로 서버에 정적 HTML문서를 생성한다.
getStaticPaths은 3개의 id의 페이지가 미리 생성된다. fallback 옵션은 이 값이 false인 경우엔 지정되지 않은 경로(4 -10번까지의 포스트)에 대한 요청엔 404에러를 출력한다. 이 값을 true로 지정하여 지정되지 않는 경로에 대한 요청에도 대응하여 정적생성을 하도록 설정한다.
정리
- SEO 적용 또는 데이터 pre-rendering이 필요 없다면 CSR 방식(React)
- 정적 문서로 충분한 화면이면서 빠른 HTML 문서 반환이 필요하다면 SSG 방식
- 매 요청마다 달라지는 화면이면서 서버 사이드로 이를 렌더링 하고자 한다면 SSR 방식