오늘을 Next.js의 핵심 내용과 사용 의의에 대해서 공부했는데, 드디어 SSR과 SG가 와닿기 시작한 것 같다. 처음에는 각 방식이 어떻게 동작하는지 보고 읽고 들어도, 이해는 가는데 왠지 피부로 와닿는 느낌은 없었는데, 강의를 들으면서 실제 페이지를 로드할 때 뭐가 어떻게 달라지는지 보니 이제는 이해가 완벽히 된 것 같다.
Head 컴포넌트
html에서 head 태그에 해당하는 Next.js의 빌트인 컴포넌트이다.
pages 폴더에 있는 페이지 컴포넌트에 Head 컴포넌트를 쓰고 안에 title과 같은 태그들을 작성하면 된다.
const HomePage = ()=>{
...
return (
<>
<Head>
<title>페이지 제목</title>
</Head>
...
</>
);
}
Head 태그 컴포넌트 안에 favicon또한 다음과 같이 넣을 수 있다. 보통 _app.js에 넣음으로써 모든 페이지에 공통으로 들어가게 해둔다.
const App = ({Components, pageProps})=>{
...
return (
<>
<Head>
<title>페이지 제목</title>
<link rel="icon" href="/favicon.ico" />
</Head>
...
</>
);
}
next에서 구글폰트 적용하기
@next/font/google
이라는 패키지에서 원하는 폰트를 임포트하고 객체를 만든다.import { Noto_Sans_KR } from '@next/font/google';
const notoSansKR = Noto_Sans_KR({
weight: ['400', '700'], // 문자열 주의
subsets: [], // 영문(latin), 한글등 서브셋 지정, 빈 배열이면 전부 사용
});
<main className={notoSansKR.className}>
...
</main>
<Head>
<style>{`
html {
font-family: ${notoSansKR.style.fontFamily}, sans-serif;
}
`}</style>
</Head>
빌드와 실행하기
리액트와 마찬가지로 next.js에서도 jsx문법을 자바스크립트로 변환하는 트랜스파일링 과정이 필요하다. (빌드)
그 밖에도 서버 실행에 필요한 코드들을 생성해야 한다.
npm run build
.next
라는 폴더가 생성되고, 그 안에 실행에 필요한 파일들이 들어가게 된다.npm run start
.next
폴더가 있어야 명령이 실행된다.npm run dev
는 개발모드, npm run start
는 프로덕션 모드next.js 서버의 역할
vercel로 배포하기
프리렌더링: 미리 렌더링 하는 것
아래와 같이 html 문서를 서버에서 클라이언트로 보내기 이전에 렌더링이 일어나는 것을 프리렌더링이라고 한다.
프리렌더링은 Static Generation과 서버사이드 렌더링 방식으로 나뉜다.
Static Generation: 빌드 타임에 html문서를 렌더링하는 것
서버사이드 렌더링
Static Generation 실습
export async getStaticProps (){
const res = await axios.get('/products');
const products = res.data.results;
return {
props: {
products,
}
}
}
export default function Home({products}){
// ...prop으로 정적생성한 products를 받아 사용 (프리렌더링 된 데이터 사용)
}
export async getStaticPaths (){ // 경로를 정적 생성
return {
paths: [
{ params: { id: '1' } }, // 사이트 주소에서 가져오는 값(상품의 id), 문자열 주의
{ params: { id: '2' } },
],
falllback: false, // 없는 경로에 대한 처리 지정 프로퍼티
}
}
export async getStaticProps(){ // 페이지를 정적 생성
const productId = context.params['id'];
const res = await axios.get(`/products/${productId}`);
const product = res.data;
return {
props: {
product,
}
}
}
주의점: 만약 파일명이
pages/users/[userId].js
였다면,params
객체도userId
키값을 가지고 있어야한다.
fallback
은 static paths에 없는 경로에 대한 처리를 지정한다. 위의 코드에서는 1번, 2번 상품에 대해서만 정적 생성을 하고 있다. fallback이 false인 상태로 3번 경로로 가면 404페이지가 보인다.getStaticProps
의 동작이 바뀌게 된다. 다음은 fallback이 true일 경우 getStaticProps
가 동작하는 과정이다.getStaticPaths
가 반환한 path들은 빌드 타임에 HTML로 렌더링된다getStaticProps
함수를 이용하여 HTML 파일과 JSON 파일을 만들어낸다next/router
의 router.isFallback
값 체크를 통해서 조건 분기하면 된다. 이때 페이지 컴포넌트는 props
로 빈값을 받게된다. 백그라운드에서 정적 생성이 끝나면 진짜 props
값을 받게 되고, 이후로 사용자는 풀 페이지를 보게 된다. function Post({ post }) {
const router = useRouter();
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>;
}
// Render post...
}
export async function getStaticProps(context){
const productId = context.param['id'];
let product;
try{
const res = axios.get(`/products/${productId}`);
product = res.data;
}catch{
return { notFound: true, };
}
return {
props: {
product,
}
}
}
export async function getStaticPaths(){
const res = await axios.get('/products');
const products = res.data.results;
const paths = products.map((product)=>(
{params: {id: String(product.id)} },
));
return {
paths,
fallback: true,
}
}
SSR
export async function getServerSideProps(context){
const q = context.query['q'];
const res = await axios.get(`/products/?q=${q}`);
const products = res.data.results ?? [];
return {
props: {
products,
q,
}
}
}
클라이언트에서 데이터 주고받는 패턴
Next.js에서 정적 생성 페이지, SSR로 페이지를 만들었다 해서 CSR을 전혀 하지 않는 것은 아니다. Next.js는 SG, SSR, CSR을 적재 적소에 실행하는 최적화가 내부 구현되어 있다.
예를 들어, SSR로 상품 상세 페이지를 만들었다고 할 때, 홈페이지의 특정 링크를 통해 해당 상세 페이지로 이동하는 링크(버튼)이 있다면, 그 페이지의 getServerSideProps함수를 통해 새로운 html이 아닌 필요한 데이터를 담은 json만 새로 생성한다. 또한 해당 페이지 컴포넌트를 js조각으로 미리 로드해 놓는다. 사실은 그 페이지로 이동할때 SSR을 했다 해서 html을 새로 받는 것이 아니었던 것이다. 로드한 js파일을 살펴보면 우리가 컴포넌트로 만든 page를 transpile한 js코드가 들어있음을 알 수 있다.
이렇게 필요한 js파일을 그때 그때 불러오는 기법을 code splitting이라고 한다. Next.js의 페이지들은 모두 자동적으로 code splitting 되어있다.
예외는 url을 통해 어떤 페이지를 get요청했을 경우이다. 이런 경우에는 json만으로 페이지를 가져올 수 없고, SSR을 통해 html을 새로 생성해야 한다.
SSR로 만들었다 해서 해당 페이지로 이동하면 항상 MPA처럼 화면이 깜빡이고 새로 전체를 리로드하는 것이 아니라, next.js에서 제공하는 링크 컴포넌트를 통해 페이지를 이동하면 기존의 SPA처럼 부드러운 화면전환이 일어나도록 잘 설계되어 있는 것이다.
그리고 이런 최적화가 Next.js를 사용하는 가장 큰 의의이자 핵심적인 아이디어 이다.
1. 항상 최신 데이터를 보여줘야 하는 페이지
2. 데이터가 자주 바뀌는 페이지
3. 리퀘스트의 데이터를 사용해야 하는 페이지(예: 헤더, 쿼리스트링, 쿠키 등)
의외에 특별한 이유가 없다면 Next.js팀은 무조건 정적 생성을 우선적으로 하는 것을 권장한다고 한다.
면접 스터디를 하면서 이전에 학습했던 개념들이 머릿속에서 휘발되고 있다는 것을 알았다. 매일 부담없이 진행하고 있는 스터디인데, 오늘은 꽤 중요하다고 널리 알려진 개념인 쿠키와 웹 스토리지에 대한 질문을 받았는데 대답을 명확하게 못했다. 내가 그 내용을 학습할 때 온전히 내 것으로 만들지 못하고 넘어갔다는 것을 알게 되었다. 어떻게 보면 면접 스터디를 통해 약점을 알게 되고 보완할 수 있게 되어서 좋은 일이라고도 할 수 있다.