pages > book 폴더 하위를 기준으로 한다.
[id].tsx를 할 경우 book/id 가 나올 경우 그에 맞는 id에 하나에 대응 하는 페이지를 보여준다.
[...id].tsx를 할 경우 book/id/name/day 등 모든 데이터를 /를 기준으로 잘라 배열로 가져올 수 있다. -> catch all segment
하지만 두 경우 모두 /book 뒤에 /가 아무것도 없다면 대응 할 수 없다.
[[...id]].tsx를 할 경우 대응 할 수 있다. -> optional catch all segment
import { useRouter } from "next/router";
const Page = () => {
const router = useRouter();
console.log(router); // query.id 에 url에 입력한 쿼리스트링이 있다.
const { id } = router.query;
return <h1>Book {id}</h1>;
};
export default Page;
pages > api 폴더 내부에는 next.js가 서버와 통신할 파일을 자체적으로 만들 수 있다.
/**
* localhost:3000/api/hello 로 요청을 보낼 시 이곳에서 응답한다
* 특별한 상황이 아니면 자주 사용하지 않는다
*/
import type { NextApiRequest, NextApiResponse } from "next";
type Data = {
name: string;
};
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: "John Doe" });
}
Link / router.push - Client Side Rendering 으로 페이지를 이동시킨다
src > app.tsx 는 root 컴포넌트이다(모든 페이지의 부모 페이지)
Component : 현재 페이지 역할을 할 컴포넌트를 받는다
pageProps : Component에 전달할 Props를 저장한 객체
// _app.tsx
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
<GlobalLayout>
<Component {...pageProps} />
</GlobalLayout>
);
}
// GlobaLayout.tsx
import Link from "next/link";
import { ReactNode } from "react";
import style from "./global-layout.module.css";
export default function GlobalLayout({ children }: { children: ReactNode }) {
return (
<div className={style.container}>
<header/>
<main>{children}</main>
<footer/>
</div>
);
}
프리페칭 - 사전에 미리 불러온다.
현재 보고있는 페이지에서 링크들로 연결되어있는 페이지를 미리 불러와 사용자들이 페이지 이동을 빠르게 할 수 있도록 해준다 그렇기 때문에 Client Side Rendering처럼 빠르게 페이지 이동이 가능하다
개발자도구 -> 네트워크탭에서 확인 가능
npm run build를 통해 페이지별로 출력된 빌드 결과로 용량을 확인할 수 있다. 빌드를 해야 프로덕션 모드에 적용이 되는듯 하다
npm run start를 통해 프로덕션 모드로 실행 가능
npm run dev 는 개발자 모드이다
// 특정 페이지를 프리페칭 하는 코드. /test 페이지를 프리페칭한다
router.prefetch("/test");
{/* Link는 자동으로 프리페칭이 진행된다. */}
{/* 하단은 Link의 기능. 해당 페이지는 프리페칭 하지 않는다 */}
<Link href={"/search"} prefetch={false}>
// home.tsx
import SearchableLayout from "@/components/SearchableLayout";
import { ReactNode } from "react";
export default function Home() {
return <div>보여줄 해당 컴포넌트</div>;
}
/**
* Home 컴포넌트도 결국 함수이고 함수는 객체이다.
* 그렇기 때문에 메서드를 사용할 수 있고 아래의 메서드는 page 리액트 컴포넌트를
* <SearchableLayout> 이라는 태그에 감싸져서 나오게 한다
* 이렇게 하면 특정 컴포넌트를 특정 layout에 감싸진 형태로 나오게 할 수 있다.
*/
Home.getLayout = (page: ReactNode) => {
return <SearchableLayout>{page}</SearchableLayout>;
};
이후
// _app.tsx
type NextPageWithLayout = NextPage & {
getLayout: (page:ReactNode) => ReactNode;
};
export dfault function App({Component,pageProps,}: AppProps & {
Component: NextPageWithLayout;
}) {
// ?? 연산자는 왼쪽이 null이나 undefined이면 오른쪽을 반환한다
const getLayout = Component.getLayout ?? ((page: ReactNode)=>page);
return (
<GlobalLayout>
{getLayout(<Component {...pageProps}/>)}
</GlobalLayout>
)
}
input 태그에 onKeyDown을 사용하면 e 객체를 받을 수 있고 이곳에서 엔터키를 분기처리 하여 엔터를 누를 시 검색이 되는 input을 만들 수 있다.
const onKeyDown = (e: React.ChangeEvent<HTMLInputElement>) => {
if(e.key === "Enter") {
onSubmit(); // 실제로 데이터를 보내는 함수
}
}
<input onKeyDown={onKeyDown}/>
평소 Overlay를 따로 구현했다면 이제 그럴 필요가 없어 보인다. ::before를 통해 Overlay를 더 쉽게 구현 할 수 있다.
.cover {
background-position : center;
background-repeat: no-repeat;
background-size: cover;
position: relative;
}
// cover의 overlay를 만들기 위해 가상요소를 사용한다
.cover::before {
position: absolute;
background-color: rgba(0,0,0,0.7);
}
// cover css 내부의 img 태그를 선택한다
.cover > img {
z-index: 1;
max-hight: 350px;
height: 100%;
}