getServerSideProps with TypeScript | Next.js

Bori·2024년 2월 4일
1

Next.js

목록 보기
12/12
post-thumbnail

getServerSideProps

getServerSideProps를 사용하여 외부 API의 데이터를 가져와 페이지 컴포넌트의 props로 전달할 수 있습니다.

getServerSideProps를 TypeScript와 사용할 때 기본적으로 다음과 같은 형태로 사용할 수 있습니다.

import { GetServerSideProps } from 'next'
 
export const getServerSideProps = (async (context) => {
  // ...
}) satisfies GetServerSideProps

다음은 data fetching 예제입니다.

import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
 
type Repo = {
  name: string
  stargazers_count: number
}
 
export const getServerSideProps = (async () => {
  // 외부 API로부터 데이터 가져오기
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const repo: Repo = await res.json()
  // props로 페이지에 데이터 전달
  return { props: { repo } }
}) satisfies GetServerSideProps<{ repo: Repo }>
 
export default function Page({
  repo,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  return (
    <main>
      <p>{repo.stargazers_count}</p>
    </main>
  )
}

satisfies는 TypeScript 4.9에 추가되었습니다.

적용하기

// [username].tsx
import type {
  GetServerSideProps,
  GetServerSidePropsContext,
  InferGetServerSidePropsType,
  NextPage,
} from 'next';
 
export const getServerSideProps = (async (
  context: GetServerSidePropsContext, // ②
) => {
  const { req, res, params } = context;
  const username = params?.username as string;
  // ...
  return { props: username } };
}) satisfies GetServerSideProps;

const Page: NextPage<  // ①
  InferGetServerSidePropsType<typeof getServerSideProps>
> = ({ username }) => {
  return (
    <main>
      <p>{username}</p>
    </main>
  )
}

export default Page;

공식문서 예제와 차이점

① 함수 표현식을 이용하여 페이지 컴포넌트 선언

예제의 경우 함수 선언식을 이용했습니다.
따라서, 타입을 작성하는 위치가 달라지는데 저는 이걸 제대로 확인 못하고 공식 문서 예제와 동일한 위치에 타입을 작성해서 에러가 발생했습니다.
(그냥 바보였습니다..)

GetServerSidePropsContext vs GetServerSideProps

다음 예시에는 문제가 있습니다. 설명은 하단에 이어집니다.

GetServerSidePropsContext 대신 GetServerSideProps을 사용하여 코드를 작성하면 다음과 같은 타입 에러가 발생합니다.

// GetServerSideProps 사용
export const getServerSideProps: GetServerSideProps = (async (context) => {
  const { req, res, params } = context;
  const username = params?.username as string;
  // ...
  return { props: username } };
}) satisfies GetServerSideProps;

const Page: NextPage<
  InferGetServerSidePropsType<typeof getServerSideProps>
> = ({ username }) => {
  return (
    <main>
      {/* username이 any로 추론됨 */}
      <p>{username}</p> 
    </main>
  )
}

추론된 타입을 확인해보면 GetServerSideProps을 사용한 경우 GetServerSideProps라고 추론되어 세세한 타입에 대한 정보가 없습니다.

GetServerSidePropsContext를 사용한 경우 추론된 타입이 더 명시적입니다.

다음은 추론된 타입의 전문으로 props로 전달한 username이 string으로 추론되어 타입 에러가 발생하지 않습니다.

// 추론된 타입 전문
const getServerSideProps: (context: GetServerSidePropsContext) => Promise<{
    redirect: {
        destination: string;
        permanent: false;
    };
    notFound?: undefined;
    props?: undefined;
} | {
    notFound: true;
    redirect?: undefined;
    props?: undefined;
} | {
    props: {
        dehydratedState: DehydratedState;
        username: string; // 추론된 username 타입
    };
    redirect?: undefined;
    notFound?: undefined;
}>

또는 다음과 같이 타입을 작성할 수 있습니다.

// GetServerSideProps 사용
export const getServerSideProps: GetServerSideProps<{ username: string }> = (async (context) => {
  const { req, res, params } = context;
  const username = params?.username as string;
  // ...
  return { props: username } };
}) satisfies GetServerSideProps<{ username: string }>;

const Page: NextPage<
  InferGetServerSidePropsType<typeof getServerSideProps>
> = ({ username }) => {
  return (
    <main>
      <p>{username}</p> 
    </main>
  )
}

문제점 발견

TypeScript 4.9 버전에 추가된 satisfies가 없는 이 전 버전에서 getServerSideProps를 사용할 때 항상 다음과 같이 타입을 작성했습니다.

import { GetServerSideProps } from 'next'
 
export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
}

이게 버릇이 되어서 위의 예제에서도 계속 위와 유사한 방식으로 타입을 작성했는데, 다시 공식문서를 확인해보면 위와 같이 타입을 작성하지 않습니다.

// 다시 가져온 공식문서 코드
import { GetServerSideProps } from 'next'
 
export const getServerSideProps = (async (context) => {
  // ...
}) satisfies GetServerSideProps

다시 실제 코드에 적용해보면

export const getServerSideProps = (async (context) => {
  const { req, res, params } = context;
  const username = params?.username as string;
  // ...
  return { props: username } };
}) satisfies GetServerSideProps<{ username: string }>;

const Page: NextPage<
  InferGetServerSidePropsType<typeof getServerSideProps>
> = ({ username }) => {
  return (
    <main>
      <p>{username}</p> 
    </main>
  )
}

타입은 다음과 같이 GetServerSidePropsContext를 유사하게 타입이 추론됩니다.

공식문서대로 타입을 작성하면 불필요한 타입 선언없이 잘 추론되는 것을 확인할 수 있습니다.

참고

0개의 댓글