특정 Component에서 쓰이는 css는 컴포넌트와 이름이 같은 css 파일을 만들어서 import 할 수 있다.
예를들어 Components/Button 폴더 안에 index.js, Button.module.css 파일이 있다고 가정할 때 아래와 같이 사용할 수 있다.
Components/Button/Button.module.css
/*
You do not need to worry about .error {} colliding with any other `.css` or
`.module.css` files!
*/
.error {
color: white;
background-color: red;
}
Components/Button/index.js
import styles from './Button.module.css'
export function Button() {
return (
<button
type="button"
// Note how the "error" class is accessed as a property on the imported
// `styles` object.
className={styles.error}
>
Destroy
</button>
)
}
// pages/index.js
import setupAnalyticsService from '../lib/my-analytics-service'
// NEXT_PUBLIC_ANALYTICS_ID can be used here as it's prefixed by NEXT_PUBLIC_
setupAnalyticsService(process.env.NEXT_PUBLIC_ANALYTICS_ID)
function HomePage() {
return <h1>Hello World</h1>
}
export default HomePage
만약 application에 stylesheet을 추가하려면 pages/_app.js에 CSS 파일을 import해라.
예를들어
/styles.css
body {
font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica',
'Arial', sans-serif;
padding: 20px 20px 60px;
max-width: 680px;
margin: 0 auto;
}
그다음 pages/_app.js에서 import한다.
pages/_app.js
import '../styles.css'
// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
Adding Component-Level CSS
Next.js는 [name].module.css 파일명 형식의 CSS Modules도 지원한다.
예를 들어 Button 컴포넌트를 생각해보자.
먼저 components/Button.module.css 를 생성한다.
/*
You do not need to worry about .error {} colliding with any other `.css` or
`.module.css` files!
*/
.error {
color: white;
background-color: red;
}
그다음 components/Button.js를 생성하고 CSS 파일을 import한다.
import styles from './Button.module.css'
export function Button() {
return (
<button
type="button"
// Note how the "error" class is accessed as a property on the imported
// `styles` object.
className={styles.error}
>
Destroy
</button>
)
}
오직 .module.css라는 확장명을 사용한 파일에만 적용된다. 이런 css 파일들은 application이 빨리 실행되도록 해주는데 app이 paint되기에 최소한 양의 CSS만 로드되도록 해주기 때문이다.
Sass Support
Next.js는 .scss나 .sass 모두 import할 수 있게 해준다. 그러기 위해선 먼저 설치해야한다.
$ npm install sass
Note: Sass는 2개의 syntax를 지원한다. .scss는 SCSS 문법을 사용하길 요구한다. 반면에 .sass는 들여쓰는 SASS문법을 사용하길 요구한다.만약 특별히 사용에 대한 기준이 없다면, .scss를 사용해라. 이건 CSS의 상위집합개념이고 SASS에서 사용하는 어떤 들여쓰기 문법도 요구하지 않는다.
redirect: 옵셔널한 값으로 내외부 자원으로 redirecting 해주는 값이다. 반드시 {destination: string, permanent: boolean} 형태여야 한다.
export async function getServerSideProps(context) {
const res = await fetch(`https://.../data`)
const data = await res.json()
if (!data) {
return {
redirect: {
destination: '/',
permanent: false,
},
}
}
return {
props: {}, // will be passed to the page component as props
}
}
언제 getServerSideProps를 써야할까?
getServerSideProps는 pre-render되는 페이지의 필요한 데이터가 요청시에 가져와야하는 경우에만 사용해라. 서버가 반드시 모든 요청에 대한 결과를 계산해야만 하기 때문에 getStaticProps보단 느리다.
만약 데이터가 pre-render 될 필요가 없다면, client-side에서 데이터를 가져오는 것을 고려해봐라.
동적 라우트에서 정적인 데이터를 필요로 할 때 : getStaticProps
동적 라우트에서 정적인 데이터를 필요로 하며, 정적인 페이지를 생성할 때 : getStaticPaths with getStaticProps
pre-render되는 페이지에 필요한 데이터를 요청시에 가져와야 하는경우 : getServerSideProps
pre-render가 필요없고 데이터를 가져와야하는 경우 : Axios
TypeScript : Use GerServerSideProps
타입스크립트를 사용할 때에는 next에서 GetServerSideProps를 사용할 수 있다.
만약 dynamic routes를 가지고 있는 페이지가 있고, 그 페이지가 getStaticProps를 사용한다면 그 페이지는 빌드시에 HTML pre-render될때 어떤 경로의 리스트의 페이지들인지 정의할 필요가 있다.
dynamic routes를 사용하는 어떤 페이지에서 getStaticProps라 불리는 async 함수를 export한다면, Next.js는
getStaticProps에 의해 정의된 모든 paths들을 정적으로 pre-render 할 것이다.
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } } // See the "paths" section below
],
fallback: true or false // See the "fallback" section below
};
}
The paths key(required)
path키는 pre-render되야하는 경로들을 정의한 것이다. pages/posts/[id].js 라는 이름의 dynamic routes를 사용하는 페이지가 있다고 가정해보자. getStaticPaths를 해당 페이지에서 export하고 paths를 다음과 같이 리턴한다.
그럼 Next.js는 posts/1,posts/2 를 빌드시에 정적으로 생성하고, pages/posts/[id].js의 페이지 컴포넌트로서 사용된다.
만약 페이지 명이 pages/posts/[postId]/[commentId] 라면 params는 반드시 postId와 commentId를 포함해야한다.
만약 pages/[...slug] 라고 한다면 params는 반드시 slug를 배열로 포함해야한다.
The fallbackkey (required)
getStaticPaths에 의해 리턴되는 객체는 반드시 boolean속성의 fallback 키를 포함해야한다.
fallback: false
만약 fallback이 false이면 getStaticPaths에 의해 전달받지 못한 path들은 모두 404 페이지로 호출된다.
새 페이지가 자주 추가되지 않는다면 이방식은 유용하다. 만약 데이터 소스에 항목을 더 추가하고 새 페이지를 렌더링해야하는 경우엔 빌드를 다시 실행해야 한다.
아래 페이지를 예로 들어보자. getStaticPath에 의해 CMS로 부터 블로그 포스트리스트는 반환된다. 그리고 각 페이지들은 getStaticProps 에 의해 CMS로부터 각 포스트 데이터가 반환된다.
// pages/posts/[id].js
function Post({ post }) {
// Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
export default Post
만약 fallback이 true이면 getStaticProps의 행동이 변한다.
getStaticPaths로 부터 리턴된 경로들은 getStaticProps에 의해 빌드시에 HTML로 render된다.
빌드시에 생성되지 않은 경로들은 404페이지로 로드되지 않는다. 대신 첫번째요청시에 "fallback"버전을 제공한다.
fallback: true는 next next 사용시 지원되지 않는다.
Fallback Pages: 로딩페이지 같은 역할
"fallback" 버전의 페이지는
페이지의 props는 비워진다.
라우터사용시 fallback이 렌더되면 router.isFallback이 true값이 되는데 이로인해 fallback이 렌더링 되었다는 것을 확인할 수 있다.
// pages/posts/[id].js
import { useRouter } from 'next/router'
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...
}
// This function gets called at build time
export async function getStaticPaths() {
return {
// Only `/posts/1` and `/posts/2` are generated at build time
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
// Enable statically generating additional pages
// For example: `/posts/3`
fallback: true,
}
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return {
props: { post },
// Re-generate the post at most once per second
// if a request comes in
revalidate: 1,
}
}
export default Post
When is fallback : true Useful?
fallback: true 는 app이 매우 많은 양의 정적페이지를 가지고 있을때 유용하다. 어떤 사람들이 아직 생성되지 않은 페이지들을 요청할 수 있다. 그때 그 유저는 로딩창을 볼 수 있을것이다. 다만 생성된 페이지들을 업데이트 하지 않는다.
업데이트하려면 ISR을 fallback true와 같이 쓰도록 하자.
fallback : 'blocking'
만약 fallback이 blocking이라면 getStaticPath에 의해 리턴되지 않은 새로운 페이지들은 HTML이 생성될 때 까지
기다릴 것이다. 원래는 데이터 받고 HTML이 생성되지만 그 반대로 작동한다.
getStaticPaths로 부터 반환된 경로들은 getStaticProps에 의해 빌드시 HTML로 render된다.
빌드시에 생성되지 않은 경로들이 404페이지로 가지 않는다.
loading/fallback 상태가 없고 그냥 요청된 페이지가 바로 로드된다.
fallback : 'blocking'은 next export 사용시 지원되지 않는다.
When should I use getStaticPaths?
동적 라우트를 사용해서 페이지를 정적으로 pre-rendering한다면 ( pages/[id].js ) 반드시 getStaticPaths를 써야한다.