요즘 프론트엔드를 채용할 경우 Next.js 활용할줄 아는 개발자를 우대 사항에 기입하고 있다.
또한, 최근 지원했던 회사에 사전 과제로 Next.js를 활용하여 요구 사항을 구현하라는 과제를 따로 받기도 하였다.
이번 기회 제대로 Next.Js에 대해 분석하여 기존 React
와 어떤 점이 차별화 되어 있는지 알아보자.
Next 방식으로 설치하면 pages라는 폴더가 기본으로 셋팅 되어있다.
위 사진의 pages 폴더는 URL 경로를 의미한다.
만일 기본 url 경로가 http://localhost:3000
라고 과정하고 store.js
파일은 http://localhost:3000/store
의 url 등록되어 있다.
index.js
는 원본경로 http://localhost:3000
를 의미하고,
404.js
는 pages에 등록되지 않은 경로를 접속할 경우 웹브라우저에 나타나는 파일이다.
예를 들자면, http://localhost:3000/california
접속 시 404.js
를 통해 404 페이지 인식된다.
만일 pure React에서는 위와 같이 URL 주소를 표시하기 위해선 React-router-dom
라이브러리를 별도로 설치하여야 한다.
function Header() {
return (
<>
<header>
<nav>
<h2>
<Link href="/">
<a>About</a>
</Link>
</h2>
<h2>
<Link href="/store">
<a>Store</a>
</Link>
</h2>
</nav>
</header>
</>
)
}
next.js에 제공하는 link
는 무조건 a
태그를 감싸야 된다.
해당 태그를 클릭할 경우 속성 href
에 알맞은 pages 파일을 접근하여 URL이 변경된다.
위 사진은 웹 브라우저에 렌더링된 Text들이다.
Next.js
, React
각각 어떻게 크롬 개발도구의 네트워크 응답에 결과가 나타나는지 비교해 보겠다.
네트워크 응답 결과를 확인해보면 해당 Text들이 비어있다.
id의 root
안에서 부터 가상돔(Virtual Dom) 이 형성되어 각 컴포넌트 모든 HTML 태그 요소들이 나열되어야 하지만 CSR 방법론에 의해 빈 HTML 문서를(public/index.html) 먼저 렌더링 후 브라우저에 전송한다.
그 다음 bundle.js
파일을 통해 React의 각 컴포넌트가 가상돔 과정을 걷고 난 후 최종적으로 브라우저의 화면 상에 HTML 태그 요소들이 나타나게 된다.
결국 CSR 방식은 빈 HTML 문서로 인해 SEO 과정에 취약한 단점을 갖게 되므로, 검색 엔진(Google, Naver)이 찾지 못하게 된다. 이커머스 플랫폼은 본인들의 상품을 홍보 마케팅에 큰 효과를 얻지 못하기 때문에 CSR 방식을 기피한다.
네트워크 응답 결과를 확인하면 작성한 Text들이 제대로 나온다.
SEO의 장점을 살리고 싶다면 첫 페이지 로딩에는 SSR 방식을 사용 후 나머지는 사용자 편리성을 위한 CSR 방식으로 다룬다면 두 마리 토끼를 잡게 되는 셈이다.
Next.js 공식 문서를 확인해보면 SSR
과 SSG
를 지원한다고 소개되어 있다.
SSR
은 워낙 CSR
과 비교를 자주한 관계로 어떤 특성을 갖고 있는지 잘 알고 있지만, SSG
는 도대체 무엇일까?
SSG는 Static-Side-Generation의 축약어로 HTML은 build-time에 생성되며 모든 요청에서 미리 생성된 HTML을 재사용한다.
Static Generation과 Server-side Rendering이라는 두가지 방식의 pre-rendering 방식을 가지고 있으며 각각 아래의 사진과 같은 렌더링 방식을 갖고 있다.
Static-Side-Generation: HTML은 build-time
에 생성되며 모든 요청에서 미리 생성된 HTML을 재사용하여 캐싱 관리에 효율적이다.
Server-Side-Rendering: 각 페이지에서의 요청마다 HTML을 Server에서 생성하여 사용한다.
각 pre-rendering은 Server에서 요청해서 HTML 파일을 반환하는 점은 동일하나 작동 원리는 엄연히 다르다.
그럼 이제 Next.js에서 SSR과 SSG의 적용 방법에 대해 알아보자.
브라우저 화면 상 위 명단을 렌더링하도록 코드를 작성해보았다.
import axios from 'axios'
function Home({ name }) {
return (
<div>
<h1>가게 이름</h1>
<ul className="">
{name.map(el => {
return <li key={el.id}>{el.name}</li>
})}
</ul>
</div>
)
}
export default Home;
export const getStaticProps = async () => {
const res = await axios.get(`http://localhost:9000/stores`);
const data = res.data;
return {
props: {
name: data,
},
};
};
async 비동기 함수로 선언된 getStaticProps
를 통해 JSON Data를 name이라는 props에 설정하여 보냈다.
위 코드 const data = res.data
에서 res.data
인 이유는
서버에서는 자동적으로 data라는 프로퍼티에 배열로 감싸줘 있기 때문에 바인딩하였다.
개발 도구창의 네트워크 탭 응답 결과를 확인 해보면 아래와 같이 name
요소들이 잘나타났다.
위 결과를 통해 SEO 최적화에 효율적인 방법인것은 알았으나, SSG가 SSR과 달리 어떨 경우 사용해야할지 궁금할 것이다.
SSG는 데이터가 상황에 따라 변하지 않고, 정적으로 고정된 데이터를 웹페이지에 사용할 경우 쓰면 된다.
SSR은 페이지를 이동할 떄마다 서버에서 파일들을 생성하여 브라우저에 보내준다.
즉, SSR은 SSG와 달리 웹 페이지에서 업데이트가 주기적으로 일어나야할 경우 사용된다.
페이지가 이동될 때마다 새로고침 효과가 나타나면서 해당 페이지의 변화가 필요하다.
기존 생성하던 코드의 li태그들은 Link
를 지정하여 클릭할 때마다 각 id의 params에 지정하여 나오도록 코드를 구성하였다.
import axios from 'axios'
import Link from 'next/link'
function Home({ name }) {
return (
<div>
<h1>가게 이름</h1>
<ul>
{name.map(el => {
return (
<li key={el.id}>
<Link href="/params/[id]" as={`/params/${el.id}`}>
<a>
{el.name}
</a>
</Link>
</li>)
})}
</ul>
</div>
)
}
export default Home;
export const getStaticProps = async () => {
const res = await axios.get(`http://localhost:9000/stores`);
const data = res.data;
return {
props: {
name: data,
},
};
};
import axios from 'axios'
function Detail({ item }) {
return (
<div>
<h1>{item.name}</h1>
<h3>{item.id}번째 가게</h3>
</div>
);
};
export default Detail
export const getServerSideProps = async (target) => {
const id = target.params.id;
const res = await axios.get(
`http://localhost:9000/stores/${id}`
);
const data = res.data;
return {
props: {
item: data,
},
};
};
기존 리스트의 link
들은 href
, as(브라우저 URL 창에 표시될 URL)
를 묶었다.
또한, pages 폴더에는 하위 폴더로 params를 생성하여 해당 폴더안에 [id].js를 만들었으며, 이 방식은 dynamic routing 이라고 부른다.
비동기 함수로 구축된 getServerSideProps
의 매개변수에는 target 이라는 요소를 추가하였는데, 이는 params에 접근하여 매개변수에 담긴 데이터들을 활용할 수 있다.
즉, link
에서 클릭 후 각 id의 값에 따른 데이터를 전달 받은 것이다.
getServerSideProps
방식을 통해 각 Params에 따른 상세 페이지를 SSG에 따른 정적으로 관리하고 싶다면 getStaticPaths
함수를 사용하면 된다.
import axios from 'axios'
function Params_static({ item }) {
return (
<div>
// 비동기 처리를 통해 item인자를 받는 관계로
// data라는 state에 아직 값이 없는 경우 && 문구를 통해 false로 처리
// 해당 방법 안쓰고 넘어갈 경우
// Uncaught TypeError: Cannot read properties of undefined (reading 'name') 발생
{ item
&&
<>
<h1>{item.name}</h1>
<h3>{item.id}번째 가게</h3>
</>
}
</div>
);
};
export default Params_static
export const getStaticPaths = async () => {
const res = await axios.get(
`http://localhost:9000/stores`
);
const data = res.data;
return {
paths: data.map(el => {
return { params: { id: String(el.id) } }
}),
fallback: false,
};
};
export const getStaticProps = async (target) => {
const id = target.params.id;
const res = await axios.get(
`http://localhost:9000/stores/${id}`
);
const data = res.data;
return {
props: {
item: data,
},
};
};
getStaticProps
와 동일해보이지만, 브라우저 상에서 요청이 매번 일어날 때마다 서버에서 가져오는 것이 아닌 서버에서 모든 요청마다 미리 생성된 정적인 HTML 파일들을 생성하여 관리하는 것이다.
getStaticPaths
에 반환문을 확인해보면 fallback
이라는 속성을 볼 수가 있는데, 이 값을 true로 설정하면 지정되지 않은 params 경로에서도 대응하여 정적 생성을 하고,
false로 지정할 경우 404 에러 결과를 브라우저 상에 출력한다.
실제 웹 브라우저에서는 개발모드(npm dev, yarn dev
)에서는 static 생성을 사전에 생성하여 보여주는 것이 아닌 요청에 따라 서버에서 계속 생성하여 브라우저에 전달한다.
그렇기 때문에 프로덕션 모드로 빌드하여 확인해보자.
npm run build
프로덕션 모드 생성npm run start
프로덕션 모드 실행빌드 과정에 server/pages/params-static 경로에 8개의 html과 json 파일들이 생성된 것을 볼 수가 있다.
이로인해 SSG
의 방식의 개념인 build-time에 생성되며 모든 요청에서 미리 생성된 HTML을 재사용된다는 이론을 눈으로 직접 확인할 수가 있다.
static URL 경로로 접속할 때마다 이미 생성된 각 8가지에 따라 html 파일들이 나타나게 된다.
Next.js는 Route를 별도의 설치 필요없이 직접 관리할 수 있고, CSR
, SSR
, SSG
를 골고루 사용할 수 있는 유용한 프레임워크이다.
그렇다면 활용할 수 있는 세가지 방식을 어떻게 사용해야될까?
세가지 방안은 결국 만들고자하는 서비스 특성에 따라 알맞게 사용해야 한다.
CSR의 바른 사용 예시
1. 사용자에게 UX적으로 제공해야할 요소가 많은 경우
2. 중요 정보를 관리하는 플랫폼이라 검색 엔진에 노출될 필요가 없는 경우
SSR의 바른 사용 예시
1. 검색 엔진에 자주 노출되어 마케팅 효과가 필요한 이커머스 기반 경우
2. 주기적으로 웹페이지 업데이트가 이뤄지는 경우
SSG의 바른 사용예시
1. 검색 엔진에 자주 노출되어 마케팅 효과가 필요한 이커머스 기반 경우
2. 업데이트가 필요없고 캐싱 관리로 효율적 사용이 필요한 경우