Next.js에서 특별하게 사용하는 타입은 많지 않기 때문에 공식문서만 읽어보아도 충분합니다. 이번 레슨에서는 공식 문서의 내용을 기반으로 타입을 활용하는 방법에 대해서 알아보도록 하겠습니다.
_app.tsx 파일에선 웹사이트 전체에 공통적으로 렌더링 되는 App이라는 컴포넌트를 만들죠? 이 컴포넌트의 Props 형태는 정해져 있는데요. AppProps라는 타입을 next/app 패키지에서 불러와서 아래처럼 사용하면 됩니다.
_app.tsx
import Head from 'next/head';
import { AppProps } from 'next/app';
import Header from '@/components/Header';
import '@/styles/global.css';
export default function App ({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<title>Codeitmall</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
<Component {...pageProps} />
</>
);
}
정적 생성
Next.js에선 빌드 시점에 리액트 코드를 미리 렌더링해 둘 수 있는데요. 이런 걸 정적 생성(Static Generation)이라고 합니다.
import Image from 'next/image';
export async function getStaticPaths () {
const res = await fetch('https://learn.codeit.kr/api/codeitmall/products/');
const data = await res.json();
const products = data.results;
const paths = products.map((product) => ({
params: { id: String(product.id) },
}));
return {
paths,
fallback: true,
};
};
export async function getStaticProps(context) {
const productId = context.params ['id'];
let product;
try {
const res = await fetch(`https://learn.codeit.kr/api/codeitmall/products/${productId}`);
const data = await res.json();
product = data;
} catch {
return {
notFound: true,
};
}
return {
props: {
product,
},
};
}
export default function ProductPage({ product }) {
return (
<div>
<h1>{product.name}</h1>
<Image
src={product.imgUrl}
width="480"
height="480"
alt={product.name}
/>
</div>
);
}
위 예시 코드를 살펴보면, getStaticPaths()라는 함수에선 https://learn.codeit.kr/api/codeitmall/products/라는 API 주소에 리퀘스트를 보내서 상품 목록 데이터를 받았습니다. 이 데이터로 아이디 값들을 모아서 params 값들을 만들고요. 이 값을 바탕으로 getStaticProps() 함수에서는 context 값을 활용해 리퀘스트를 보내서 상품 데이터를 받아옵니다. 이걸 product라는 이름의 Prop으로 내려주고 있죠.
Next.js에선 기본적으로 화살표 함수로 만든 다음 아래와 같이 GetStaticPaths, GetStaticProps 타입을 지정하는 걸 권장합니다.

context 값에 마우스를 호버해 보면 타입이 잘 추론되죠?
import { GetStaticPaths, GetStaticProps } from 'next';
// ...
export const getStaticPaths: GetStaticPaths = async () => {
// ...
return {
paths,
fallback: true,
};
};
export const getStaticProps: GetStaticProps = async (context) => {
// ...
return {
props: {
product,
},
};
}
이번엔 페이지의 타입을 정의해 보겠습니다. 우선 일반적인 리액트 컴포넌트의 Props 타입을 정의하듯이 Props를 정의하고요. 이걸 getStaticProps() 함수의 제네릭으로 지정해 줍니다.
interface Props {
product: Product;
}
export const getStaticProps: GetStaticProps<Props> = async (context) => {
// ...
return {
props: {
product,
},
};
};
export default function ProductPage({ product }: Props) {
return (
<div>
<h1>{product.name}</h1>
<Image
src={product.imgUrl}
width="480"
height="480"
alt={product.name}
/>
</div>
);
}
Next.js 서버에 리퀘스트가 들어올 때마다 렌더링을 해서 보내주는 서버사이드 렌더링의 경우에도 비슷한 방식으로 해주면 됩니다. 앞에서 보았던 예시에서 서버사이드 렌더링으로 바꾼 코드를 한번 보겠습니다.
import Image from 'next/image';
export async function getServerSideProps(context) {
const productId = context.params['id'];
let product;
try {
const res = await fetch(`https://learn.codeit.kr/api/codeitmall/products/${productId}`);
const data = await res.json();
product = data;
} catch {
return {
notFound: true,
};
}
return {
props: {
product,
},
};
}
export default function ProductPage({ product }) {
return (
<div>
<h1>{product.name}</h1>
<Image
src={product.imgUrl}
width="480"
height="480"
alt={product.name}
/>
</div>
);
}
getServerSideProps() 함수의 타입은 화살표 함수로 만든 다음에 GetServerSideProps로 지정하면 되고요. 마찬가지로 Props 타입도 정의한 다음, 아래처럼 제네릭으로 지정하고, 페이지 컴포넌트에도 정의하면 됩니다. 이 부분은 자주 쓰일 테니까 기억해 두시면 좋습니다.
interface Props {
product: Product;
}
export const getServerSideProps: GetServerSideProps<Props> = async (context) => {
// ...
return {
props: {
product,
},
};
};
export default function ProductPage({ product }: Props) {
return (
<div>
<h1>{product.name}</h1>
<Image
src={product.imgUrl}
width="480"
height="480"
alt={product.name}
/>
</div>
);
}
마지막으로 API 라우트의 타입을 살펴보면 아래와 같이 리퀘스트와, 리스폰스에 타입을 지정하면 됩니다. 어렵지 않죠?
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// ...
}