정적 사이트 생성을 위해 next를 사용하기로 했고,
CNA로 next 프로젝트 생성했다.
npx create-next-app --example with-typescript with-typescript-app
명령을 통해 타입스트립트가 적용된 next를 받는다.
기존 next 모습과 다른것은
tsconfig.jon
파일 추가next-env.d.ts
파일 추가devDependencies
: @types/node
@types/react
@types/react-dom
typescript
추가node_modules/@types/
에 위치하게 된다. 각 패키지의 타입정의 파일이 있다!script :{ "type-check" : tsc }
타입스크립트 실행명령 추가js
, jsx
파일 ts
, tsx
로 변경npm run dev
: 개발 서버 실행해보자, 아래 사진과 같은 못난이 페이지가 뜨면.. 본격 시작 ㅋ
npm i styled-components @types/styled-components
전체페이지 레이아웃과 스타일 적용을 위해 _app.tsx
를 커스텀하자! nextjs 공식문서 타입스크립트 커스텀 app 참고
// import App from 'next/app';
import { AppProps /*, AppContext */ } from 'next/app'; // 타입 임포트
import Layout from '../components/Layout'; // 레이아웃 컴포넌트 가저오기
function MyApp({ Component, pageProps }: AppProps) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// MyApp.getInitialProps = async (appContext: AppContext) => {
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext);
// return { ...appProps };
// };
export default MyApp;
AppContext
도 import
하여 사용post/[id]
폴더에 /[id].tsx
를 위치하는 식으로 동적라우팅 구현예
- 파일경로 :
/pages/post/create.tsx
, URL :도메인/post/create
- 파일경로 :
/pages/post/[pid].tsx
, URL :도메인/post/1
,도메인/post/abc
- 파일경로 :
/pages/post/[categori]/[id].tsx
, URL :도메인/post/맛집/1
- 파일경로 :
/pages/post/[...slug].tsx
, URL :도메인/post/1/2
,도메인/post/a/b/c
이전에는 getInitialProps()만으로 데이터를 가져왔는데, 몇 가지가 더 늘었다.
상황에 따라 맞는 api를 사용하자. nextjs 공식문서 : 데이터 가저오기 API설명에서 더 정확하게 알수 있다.
- 정적생성 : 사전 렌더링/정적페이지에 사용
getStaticProps()
: 빌드할 때 데이터를 가져옴getStaticPaths()
: 데이터를 기반으로 사전 렌더링(pre-render) 할 동적 경로를 지정getStaticProps()
와getStaticPaths()
는 항상 짝으로 사용하자- SSR
getServerSideProps()
:
getStatcProps()
// 예제코드
import { GetStaticProps } from 'next'; // 타입스트립트 사용시 getStatcProps의 타입임포트
// 데이터 받을 컴포넌트 작성
const ContentList = ({ contentList }) => {
// getStatcProps에서 패치받은 데이터 contentList가 props로 전달됨
return (
<ul>
{contentList.map((content) => (
<li>{content.title}</li>
))}
</ul>
);
};
export const getStaticProps: GetStaticProps = async (context) => {
// context의 구성내용
const { params, preview, previewData } = context;
// data fetch
const res = await fetch('url');
const contentLists = await res.json();
return {
// 리턴은 객체 props키에 담아 보낸다!
props: {
contentList,
}, // will be passed to the page component as props
// revalidate 옵션 사용시 1초에 한번 데이터 패치 발생한다! 계속 업뎃~
revalidate:1, // In seconds
};
};
export default ContentList;
pages
폴더에서만 사용GetStaticProps
을 사용하며만약, 패치데이터의 타입을 유추하길 원한다면
InferGetStaticPropsType
로 컴포넌트의 해당 props의 타입을 지정import { InferGetStaticPropsType } from 'next'; const ContentList = ({ contentList }: InferGetStaticPropsType<typeof getStaticProps>)=> { // will resolve posts to type Post[] } type Content = { author: string; content: string; }; export const getStaticProps = async () => { const res = await fetch('https://.../posts'); // contentList는 Content 타입으로 구성된 배열 타입 이라고 지정 const contentList: Content[] = await res.json(); return { props: { contentList, }, }; export default ContentList;
getStatcPaths()
getStatcProps
를 쓴다면 렌더링할 경로목록을 정의getStatcPaths
에 지정된 경로를 정적으로 사전 렌더링 함import { GetStaticPaths } from 'next'
export async function getStaticPaths() {
return {
paths: [
// { params: { ... } }, // See the "paths" section below
{ params: { id: '1' } }, // pages/post/[id].tsx
{ params: { categori: '맛집',id :'1' } }, // pages/post/[categori]/[id].tsx
{ params: { slug: ['a','b','c'] } }, // pages/post/[...slug].tsx => /post/a/b/c
{ params: { slug: false }} // pages/[[...slug]] 폴더일때, 사이트 루트 페이지로 설정,
],
fallback: true or false // See the "fallback" section below
};
}
pages
폴더에서만 사용getStaticProps
와 함께사용하며, getServerSisdeProps
와 함께 사용할 수 없음!paths
키paths
: 필수 키, 미리 렌더링 되는 키를 결정catch-all-routes
검색fallback
키false
false
설정 시, 경로 지정 페이지만 사전-렌더링, 없는 경로 접근시 404페이지 렌더링true
설정 시,getStaticProps
를 실행하여 필요데이터를 가저옴// pages/posts/[id].js
import { useRouter } from 'next/router'
const Compo ({ data }) {
const router = useRouter()
// 만약 정적생성되지 않은 페이지가 있다면, getStaticProps()가 실행이 끝날때 까지 'Loading' 요소를 반환한다.
if (router.isFallback) {
return <div>Loading...</div>
}
return (
// 여기에, 사전-렌더링-페이지
)
}
// 빌드시 동작
export async function getStaticPaths() {
// 데이터 목록을 받아와 아래와 같이 경로를 추가 할수 있돰
const response = await fetch(".../posts")
const data = await response.json()
const paths = data.map(({ id }) => ({ // [...slug] 형태는 reduce()를 활용하자
params: { id: String(id) }, // 값은 모두 string 타입으로 넣어야 함
}))
// paths = [
// {params : {id:'1'} },
// {params : {id:'2'} },
// {params : {id:'3'} },
// {params : {id:'4'} },
// ]
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`...url/${params.id}`)
const data = await res.json()
// 데이터를 props로 전달 하며, 요청 시 1초에 한번 다시 패칭
return {
props: { data },
revalidate: 1,
}
}
export default Post
getServerSideProps()
getInitialProps()
는 양쪽 다 실행되지만, getServerSideProps()
는 무조건 서버에서만import { GetServerSideProps } from 'next' // 타입스 임포트
const Page= ({ data })=> {
// Render data...
}
// This gets called on every request
export const getServerSideProps:GetServerSideProps = async()=> {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// 객체형태로 전달
return { props: { data } }
}
export default Page
InferGetServerSidePropsType<typeof getServerSideProps>
를 컴포넌트 Props의 타입으로 지정useSWR
hooksnext팀에서 제공하는 hooks로 클라이언트 측에서 데이터를 가져오는 경우 사용하기 권장
swr 공식문서
swr - data-fetching : axios, fetch, graphQl 방식설명
swr - nextjs : client-side-Data-fetching
const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
import useSWR from 'swr'
const Profile =()=>{
const {data, error} = useSWR('api-url',fetch)
if (error) return <div>에러다~</div>
if (!data) return <div>loading</div>
return <div>{data}</div>
}
next의 정적 파일 폴더는 public
으로 설정되어 있다.
public/image.png
/image.png
만 입력, public
을 생략하고 작성하면 원하는 이미지가 엑세스 됨import { GetStaticProps, GetStaticPaths, GetServerSideProps} from 'next'
getStaticProps()
: types name =GetStaticProps
getStaticPaths()
:types name = GetStaticPaths
getServerSideProps()
: types name = GetServerSideProps
import { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
res.status(200).json({ name: 'John Doe' })
}