SSR의 앱을 만들어보고 싶다는 생각이 들었고,
가장 많이 쓰이는 프레임워크인 Next.js 에 입문해보았다.
next.js는 프레임워크이기 때문에 정해진 규칙에 따라 코드를 작성해야 한다.
라이브러리인 react.js와 같이 자유롭게 원하는 방식으로 코드를 작성하는것과는 상반된다.
next.js는 정적으로 생성되는 pre-render이다.
- CSR 브라우저에 div태그 하나의 빈 html을 보냄 -> 브라우저에서 js를 다운로드하고 react.js를 실행함 -> 모두 fetch 된 후에 렌더링함
- SSR 브라우저에 pre-rendering된 실제 html을 보냄 -> 브라우저에서 js를 다운로드하고 react.js를 실행함 -> 모두 fetch 된 작동 가능한 앱 렌더링함
pre-render로 인해 첫 페이지가 작동을 하진 않지만 렌더링이 완성된 html을 유저가 빠르게 받아 볼 수 있으며 SEO에서 좋은 성능을 갖는다.
npx create-next-app
router를 사용할 필요없이 pages폴더 안에 파일을 생성하면 파일명으로 된URL 페이지가 생성된다.
이 때, 컴포넌트를 꼭 export default해야 생성된다.
a 태그를 사용하면 전체 페이지가 새로고침 되기 때문에,
클라이언트 사이드 네비게이션인 Link컴포넌트를 사용한다.
a 태그를 감싸는 이유는 class와 style을 지정할 수 있기 때문이다.
useRouter훅을 사용해 router를 연결해줄 수 있다.
import Link from "next/link";
import { useRouter } from "next/link";
export default const NavBar = () => {
const router = useRouter();
const { pathname } = router;
return (
<Link href="/">
<a
style={{ color: pathname === '/navbar' ? 'red': 'blue'}}
className={{ pathname === '/navbar' ? 'navbar' : '' }}
>
Home
</a>
</Link>
)
}
next.js는 다른 파일을 렌더링하기 전에 _app.js 파일을 먼저 렌더링한다.
만약 About컴포넌트를 렌더링 한다면 Component가 About으로 바뀌는 것이며, 이 방법으로 About컴포넌트가 렌더링 된다.
_app 파일에 다른 컴포넌트를 넣으면 모든 페이지에서 렌더링 된다.
import '../styles/globals.css'
import '../components/NavBar'
function MyApp({ Component, pageProps }) {
return (
<>
<NavBar />
<Component {...pageProps} />
<>
)
}
export default MyApp
components 폴더에 Layout 파일 생성!
Layout은 _app파일의 코드를 간소화 하기 위해 작성하는 파일이다.
모든 페이지에서 렌더링할 부분을 Layout에서 구성하고 Children을 사용해 Layout으로 _app을 감싸준다.
Layout.js
import React, { Children } from 'react'
function Layout({ Children }) {
return (
<div>{Children}</div>
)
}
export default Layout
_app.js
import Layout from '../components/Layout'
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default MyApp
components 폴더에 Seo 파일 생성!
Seo 파일을 만들어서 Head를 구성한다. Head는 인터넷 파일창의 탭 부분이다.
Head를 사용할 파일에서 Seo컴포넌트를 불러와 props로 title을 넘겨준 후 Head에서 받아준다.
import React from 'react'
import Head from 'next/head'
function Seo({ title }) {
return (
<Head>
{title}
</Head>
)
}
export default Seo
import Seo from '../components/Seo'
import styles from '../styles/Home.module.css'
export default function Home() {
return (
<div>
<Seo title="home"/>
this is Home!
</div>
)
}
redirect는 URL을 바꿔주며 유저가 이를 인식할 수 있다.
redurect는 서버가 mask하여 URL을 바꿔 주므로 유저가 이를 인식할 수 없다.
이 점을 이용하여 API KEY 등을 숨겨줄 수 있다.
const nextConfig = {
reactStrictMode: true,
async redirects() {
return [
{
source: "/old-form",
destination: "/new-form",
permanent: false,
}
]
},
async rewrite() {
return [
{
source: "/api/movie",
destination: "/basedURL/APIKEY",
//fetch를 할 때, URL을 위의 /api/movie를 사용하면 된다.
}
]
}
}
module.exports = nextConfig
서버에서 데이터를 pre-rendering해서 props를 리턴해 준다.
그리고 컴포넌트에서는 prop로 그 데이터를 받아와서 초기 HTML을 구성한다.
이를 통해 HTML의 뼈대만 갖춰주는 CSR과는 달리 SSR에서는 전체적으로 초기 렌더링을 완성한 HTML을 갖추는 것이다.
HTML의 소스코드를 살펴보면 props로 모든 데이터가 넘겨진 후 코드를 구성하고 있음을 볼 수 있다.
이 때, api키 등을 가려줄 필요는 없다. 서버에서 처리가 되기 때문에 유저는 볼 수 없기 때문이다.
import Seo from '../components/Seo'
import styles from '../styles/Home.module.css'
export default function Home({ result }) {
return (
<div>
<Seo title="home"/>
{console.log(result)}
</div>
)
}
export async function getServerSideProps() {
const res = await fetch("/basedURL/APIKEY")
const result = res.json()
return {
props: {
result,
},
};
}
/about/me,/about/user과 같이 중첩되는 URL이 필요할 경우
about 폴더 생성 -> me.js, user.js 파일을 생성한다.
/about/124512323과 같이 변수id가 URL에 들어가야 할 경우
[id].js 파일을 생성한다.