React, Next and Typescript #1 설치

eunji hwang·2020년 7월 31일
10

NEXT.JS

목록 보기
3/3

CNA(create-next-app)

정적 사이트 생성을 위해 next를 사용하기로 했고,
CNA로 next 프로젝트 생성했다.

nextjs 공식문서 typescript 사용하기
nextjs+typescript 예제 깃헙

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 : 개발 서버 실행해보자, 아래 사진과 같은 못난이 페이지가 뜨면.. 본격 시작 ㅋ

styled-components

npm i styled-components @types/styled-components

  • 스타일드 컴포넌트, @타입스 설치

커스텀 _app.tsx

전체페이지 레이아웃과 스타일 적용을 위해 _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;
  • SSR을 한다면 주석 처리 된 타입 AppContextimport하여 사용

동적라우팅

nextjs 동적라우팅

  • pages 폴더에 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

Data fetching

이전에는 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;
  • 서버-사이드-렌더링(SSR: server-side-render)에서만 사용되는 api, 즉, js번들에 포함하지 않음
  • pages 폴더에서만 사용
  • 페이지를 사전 렌더링(SEO 적용) & 캐시 될 수 있음
  • 페이지의 데이터가 빌드시 구성됨, (사용자의 요청X)
  • 예: 블로그 페이지가 컨텐츠 관리 시스템(CMS)에서 블로그 게시물 목록을 가져와야 할 때
  • 타입스크립트 타입 : 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()

nextjs 공식문서 : 정적 페이지의 데이터쓰기

  • pages폴더에 동적라우팅이 구현되 있을때, 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 설정 시,
    • fallback 페이지 렌더링하여 로딩페이지 렌더링
    • 다음, 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()

  • pages폴더에만 사용가능
  • 서버사이드에서만 실행, getInitialProps()는 양쪽 다 실행되지만, getServerSideProps()는 무조건 서버에서만
  • 페이지 렌더링 전 꼭 필요한 data-fetch!, 매 페이지 요청시마다 호출
  • context객체는 params, req, res, query, preview, previewData 키를 포함. nextjs 공식문서 확인
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
  • getServerSideProps로 부터 전달받은 props를 통해 데이터타입을 유추하려면 InferGetServerSidePropsType<typeof getServerSideProps>를 컴포넌트 Props의 타입으로 지정

useSWR hooks

next팀에서 제공하는 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을 생략하고 작성하면 원하는 이미지가 엑세스 됨

타입스크립트 types

정적 생성 및 서버측렌더링 api

import { GetStaticProps, GetStaticPaths, GetServerSideProps} from 'next'

  • getStaticProps() : types name =GetStaticProps
  • getStaticPaths() :types name = GetStaticPaths
  • getServerSideProps() : types name = GetServerSideProps

라우팅 api

import { NextApiRequest, NextApiResponse } from 'next'
type Data = {
  name: string
}

export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
  res.status(200).json({ name: 'John Doe' })
}

커스텀 _App

상단 설명참고

profile
TIL 기록 블로그 :: 문제가 있는 글엔 댓글 부탁드려요!

0개의 댓글