렌더링: 리액트에서 작성한 코드를 HTML 로 변환하는 것
SSR 과 정적 사이트는 Pre-Rendering 이라고도 부른다.
외부 데이터를 fetch해서 리액트의 컴포넌트를 HTML로 변환하기 때문이다.
초기 렌더링 작업이 사용자 장치에서 발생한다.
브라우저는 UI 를 구성하기 위한 JS 와 비어있는 HTML 을 받는다.
참고: 리액트의
useEffect
를 사용해서 NextJS에서도 CSR를 선택적으로 수행할 수 있다.
NextJS는 기본적으로 모든 페이지를 미리 렌더링한다.
즉 초기 렌더링이 서버에서 JS를 이용해 이루어진다.
SSR, SSG가 이에 해당한다.
HTML 이 각 페이지 요청에 대해서 서버에서 생성된다.
페이지를 인터랙티브(대화형) 으로 만들기 위해 HTML, JSON, JS 가 클라이언트로 전송된다.
클라이언트에서 React를 사용해서 JSON 데이터와 JS 명령을 사용해 컴포넌트를 인터랙티브하게 만든다. (이벤트 핸들러 연결) 이 과정을 Hydration 이라고 부른다.
getServerSideProps
를 사용해 SSR 를 선택적으로 적용할 수 있다.
애플리케이션이 배포될 때 빌드 시 콘텐츠가 한 번 생성되고 HTML이 CDN에 저장되고 각 요청에 재사용된다.
getStaticProps
를 사용해 페이지를 정적으로 생성할 수 있다.
서버는 애플리케이션 코드가 저장되고 실행되는 메인 컴퓨터이다.
NextJS는 Origin Server, CDN, Edge에 배포할 수 있다.
CDN 서버와 Edge 서버와 같이 애플리케이션 코드가 배포될 수 있는 다른 장소와 구별하기 위해서 Origin(출처) 라는 용어를 사용한다.
Origin 서버는 요청을 받으면 응답을 보내기 전 계산을 수행하고, 그 계산 결과가 CDN으로 이동할 수 있다.
CDN은 전 세계 여러 위치에 정적 콘텐츠(HTML, img) 를 저장하고 클라이언트와 Origin 서버 사이에 배치된다.
새 요청이 들어오면 사용자와 가장 가까운 CDN에서 캐시된 결과로 응답한다.
Edge는 사용자에게 가장 가까운 네트워크의 일반화된 개념이다.
CDN은 네트워크의 가장자리(Edge)에 정적 콘텐츠를 저장하기 때문에, Edge의 일부로 간주된다.
CDN과 유사하게 Edge 서버는 전 세계 여러 위치에 배포된다.
하지만 정적 콘텐츠를 저장하는 CDN과 달리 Edge서버는 코드를 실행할 수 있다.
즉, 사용자에게 더 가까운 Edge에서 캐싱과 코드 실행을 모두 수행할 수 있다.
Edge에서 코드를 실행하면 전통적으로 클라이언트나 서버에서 수행하던 작업을 Edge에서 수행할 수 있다.
→ 클라이언트로 전송되는 코드양은 감소하고, 사용자 요청이 Origin 서버에서 계산될 필요가 없으므로 애플리케이션 성능이 향상되어 대기 시간이 줄어든다.
https://nextjs.org/learn/basics/create-nextjs-app
리액트는 애플리케이션을 처음부터 빌드하려면 신경써야하는 세부사항이 많다.
NextJS는 React 의 번거로운 점을 간편하게 해결하였다.
Link
public
디렉토리public
폴터가 /
로 매칭됨.<img src="/images/profile.jpg" alt="Your Name" />
일반 HTML 의 img
를 사용하면 다음을 수동으로 처리해야한다.
Image
Component - 이미지 자동 최적화next/image 는 HTML의 img
의 확장
이미지 최적화가 자동으로 이뤄지며, CMS 같이 외부 데이터인 경우에도 최적화가 가능하다.
최적화는 사용자가 요청할 때 on-demand 로 적용된다. 빌드 시 적용되지 않기 때문에 몇 개의 이미지를 제공하던 빌드 시간이 증가하지 않는다.
이미지는 기본적으로 Lazy Loading된다.
즉, 뷰포트 밖의 이미지는 페이지 속도에 영향을 주지 않는다. 이미지는 뷰포트로 스크롤 될 때 로드된다.
import Image from 'next/image'
const YourComponent = () => (
<Image
src="/images/profile.jpg" // Route of the image file
height={144} // Desired size with correct aspect ratio
width={144} // Desired size with correct aspect ratio
alt="Your Name"
/>
)
Head
Component - Meta dataHTML의 title
태그와 같은 메타 데이터를 수정하기 위해서 Head
컴포넌트를 제공한다.
og:image
: 공유 했을 때 썸네일import Head from 'next/head'
export default function FirstPost() {
return (
<>
<Head>
<title>First Post</title>
</Head>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
)
}
NextJS 에서 타사 스크립트를 추가하기
script
를 사용한 경우<Head>
<title>First Post</title>
<script src="https://connect.facebook.net/en_US/sdk.js" />
</Head>
Script
Component - 최적화된 가져오기next/script 는 HTML 의 script
요소의 확장이다.
추가 스크립트를 가져와 실행할 때 최적화가 적용된다.
Script
를 적용한 예제import Script from 'next/script'
export default function FirstPost() {
return (
<>
<Head>
<title>First Post</title>
</Head>
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
onLoad={() =>
console.log(`script loaded correctly, window.FB has been populated`)
}
/>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
)
}
Script
의 proppertiesstrategy
onLoad
NextJS 에 내장되어 있는 CSS-in-JS 라이브러리
<style jsx>{`
…
`}</style>
.css
나 .scss
를 import 할 수 있다..css
파일을 만든다.components/layout.module.css
.container {
max-width: 36rem;
padding: 0 1rem;
margin: 3rem auto 6rem;
}
components/layoust.js
import styles from './layout.module.css'
export default function Layout({ children }) {
return <div className={styles.container}>{children}</div>
}
_app.js
- 전역 스타일_app.js
_app.js
에서만 import 할 수 있다.// pages/_app.js
import '../styles/global.css'
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
NextJS는 각 페이지에 대해서 미리 HTML 을 생성한다.
이는 더 나은 성능과 SEO를 제공할 수 있다.
생성된 각 HTML은 해당 페이지에 필요한 최소한의 JS 와 연결된다.
브라우저에서 페이지를 로드하면 해당 JS 코드가 실행되고 페이지가 대화식으로 만들어진다. (hydration)
Pre-rendering 안에 SSR, SSG 가 포함된다.
getStaticProps
getServerSideProps
SSG가 SSR 보다 빠르기 때문에 가능하다면 SSG를 사용하는 것이 좋다.
하지만 SSG는 요청에 따른 최신화된 데이터를 사용하지 못하기 때문에 그런 경우에는 SSR를 사용해야한다.
NextJS는 페이지 단위로 SSG와 SSR을 적용할 수 있다.
필요에 따라서 선택해서 사용하자.
getStaticProps
export default function Home(props) { ... }
export async function getStaticProps() {
// Get external data from the file system, API, DB, etc.
const data = ...
// The value of the `props` key will be
// passed to the `Home` component
return {
props: ...
}
}
getStaticProps
에서 외부 API 나 Query database를 Fetch할 수 있다.getStaticProps
는 모든 요청에 대해서 실행됨getStaticProps
는 빌드타임에만 실행됨. 하지만 getStaticPaths
가 반환한fallback
key를 사용해서 향상될 수 있음.매 요청마다 HTML을 만들어서 제공하고 싶다면 SSR를 사용해야한다.
getServerSideProps
context
에는 요청별 매개변수가 포함된다.getStaticProps
보다는 TTFB(Time to first byte) 가 느리다 매 요청마다 서버는 계산하고, 그 결과는 추가 구성 없이는 CDN에 캐싱될 수 없기 때문.export async function getServerSideProps(context) {
return {
props: {
// props for your component
}
}
}
pre-rendering 할 필요가 없는 경우에, CSR 방법을 사용할 수 있다.
SSG + CSR 의 조합
import useSWR from 'swr'
function Profile() {
const { data, error } = useSWR('/api/user', fetch)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
https://nextjs.org/learn/basics/dynamic-routes
URL 이 페이지의 데이터에 따라 달라지는 것을 원하면, 동적 경로(Dynamic Routes) 를 사용해야한다.
페이지 경로가 외부 데이터에 따라 달라지는 경우
즉, 외부 데이터에 의존적인 경로를 가진 페이지를 pre-render 할 수 있다.
pages/posts/[id].js
파일을 생성한다.
다른 페이지와 마찬가지로 페이지를 렌더링하는 코드를 작성한다.
getStaticPaths
함수를 내보낸다. 이 함수는 id
로 가능한 값을 반환한다.
비동기 함수라서 async
를 꼭 작성해준다.
https://nextjs.org/docs/basic-features/data-fetching/get-static-paths
import Layout from '../../components/layout'
export default function Post() {
return <Layout>...</Layout>
}
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } }
],
fallback: true // false or 'blocking'
};
}
getStaticProps
함수를 내보내며 외부 데이터를 빌드시 fetch한다.
getStaticPaths
구현paths
와 fallback
이 담긴 객체를 반환한다.paths
params
key를 가져야 하며, id
(파일이름) 키를 가진 객체를 포함해야 한다.id
의 값이 배열이면, 포괄 경로로 생성된다. /posts/a/b/c
fallback
false
: getStaticPaths 로 반환되는 path가 아닌 경우엔 404 페이지를 보여준다true
: 빌드타임에 만들어진 path가 아니더라도 404 페이지를 보여주지 않는다. 대신 fallback
버전 페이지를 처음 요청때 제공한다. getStaticProps가 background 에서 실행된다. 즉 요청된 path에 대해서 정적으로 페이지를 생성한다. 이후에 동일한 경로로 요청이 들어온 경우에 페이지가 빌드타임때 pre-rendering 된 페이지와 마찬가지로 생성된 페이지를 제공한다.blocking
: getStaticProps가 초기 렌더하기 전에 호출된다. 새로운 path가 getStaticProps 와 함께 SSR 으로 생성되고, 향후 요청을 위해서 캐싱한다.export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory)
// Returns an array that looks like this:
// [
// {
// params: {
// id: 'ssg-ssr'
// }
// },
// {
// params: {
// id: 'pre-rendering'
// }
// }
// ]
return fileNames.map(fileName => {
return {
params: {
id: fileName.replace(/\.md$/, '')
}
}
})
}
export async function getStaticPaths() {
const paths = getAllPostIds()
return {
paths,
fallback: false
}
}
getStaticPaths
주의점getStaticProps
와 함께 사용해야한다. 즉, SSG 인 경우에 사용한다. getServerSideProps
와 사용하면 안된다.useRouter
hook 으로 라우터 정보를 가져올 수 있다.
404 이름의 페이지를 만들면 된다. 이 파일은 반드시 정적으로 생성된다.
// pages/404.js
export default function Custom404() {
return <h1>404 - Page Not Found</h1>
}
https://nextjs.org/learn/basics/api-routes/creating-api-routes
https://nextjs.org/docs/api-routes/introduction
API Routes를 사용해 NextJS 앱 내에서 API 엔드포인트를 생성할 수 있다.
이는 서버리스 함수(람다)로 배포할 수 있다.
pages/api
디렉토리에 함수를 생성하여 이를 수행할 수 있다.
API Routes 코드는 클라이언트 번들에 포함되지 않아서 서버 측 코드를 안전하게 작성할 수 있다.
// req = HTTP incoming message, res = HTTP server response
export default function handler(req, res) {
// ...
}
req
: http.IncommingMessage 의 인스턴스
res
: http.ServerResponse 의 인스턴스
예시 pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ text: 'Hello' })
}
http://locallhost:3000/api/hello 로 접근하면 아래의 json을 응답한다.
{"text":"Hello"}
getStaticProps
, getStaticPaths
에서 API 라우트를 fetch하지 마시오getStaticProps
, getStaticPaths
에서 fetch하면 안된다.getStaticProps
, getStaticPaths
안에 직접 Server-side 코드 또는 헬퍼함수 호출을 작성해야한다.getStaticProps
, getStaticPaths
는 빌드타임 때만 실행되고 클라이언트 측에서 실행되지 않는다. 또한 이 함수들은 브라우저를 위한 JS 번들에 포함되지도 않는다. 즉, 직접 DB 쿼리와 같은 코드를 브라우저에 보내지 않고 작성할 수 있다는 뜻이다.