_app.tsx 파일이 지나치게 커지는 것을 막기 위함.
Layout 컴포넌트를 만들어주고, App 컴포넌트에서 이를 사용한다.
import NavBar from "./NavBar";
interface LayoutProps {
children: React.ReactNode;
}
export default function Layout({ children }: LayoutProps) {
return (
<>
<NavBar />
<div>{children}</div>
</>
);
}
...
export default function App({ Component, pageProps }: AppProps) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
import Head from "next/head";
interface SeoProps {
title: string;
}
export default function Seo({ title }: SeoProps) {
return (
<Head>
<title>{title} | Next Movies</title>
</Head>
);
}
import Seo from "../components/Seo";
export default function Home() {
return (
<div>
<Seo title="Home" />
<h1 className="active">Hello</h1>
</div>
);
}
Head 컴포넌트 사용하는 Seo 컴포넌트를 만들어주고, 각 컴포넌트에서 Seo 컴포넌트를 추가해준다.
public 폴더에 있는 파일들은 / 경로로 쉽게 import 가능하다.
next.js에서는 이미지 최적화를 위해 img 태그 대신 Image 태그를 사용한다.
import Image from "next/image";
...
<Image src="/vercel.svg" alt="" width={100} height={30} />
...
src, width, height는 필수 속성이다.
The Movie Database(TMDB
API 키 발급
interface MovieProps {
id: number;
original_title: string;
}
export default function Home() {
const [movies, setMovies] = useState([]);
useEffect(() => {
(async () => {
const { results } = await (
await fetch(
`https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`
)
).json();
console.log(results);
setMovies(results);
})();
}, []);
return (
<div>
<Seo title="Home" />
{!movies && <h4>Loading...</h4>}
{movies.map((movie: MovieProps) => (
<div key={movie.id}>
<h4>{movie.original_title}</h4>
</div>
))}
</div>
);
}
발급받은 API 키와 API DOCS를 참고하여 데이터를 fetch하는 코드를 작성해줬다.
const nextConfig = {
reactStrictMode: true,
async redirects() {
return [
{
source: "/contact",
destination: "/form",
permanent: false,
},
];
},
};
module.exports = nextConfig;
~~~url/contact
에 접속하면, ~~~url/form
으로 리다이렉트 됨....
async rewrites() {
return [
{
source: "/api/movies",
destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`,
},
];
},
};
...
...
const { results } = await (await fetch(`/api/movies`)).json();
index.tsx에서 API 요청하는 코드 주소도 변경해준다.
만약 내 API_KEY를 config 파일에 작성하고 싶지 않다면, .env
파일에 API_KEY를 저장해두고 process.env.API_KEY
로 접근해 사용해도 된다.
(.gitignore에 .env 추가)
이제까지 만든 페이지는 Loading이라는 화면이 실제로 보이지는 않지만, 페이지 소스를 확인해보면 현재 영화데이터가 들어와있음에도 불구하고 영화데이터는 존재하지 않고, 데이터가 들어와있지 않은 초기 상태인 Loading 코드가 들어가있다.
하지만 어떤 경우에는 이런 Loading을 보여주고 싶지 않을 수 있다.
next.js에서는 위와같은 작업을 getServerSideProps 함수를 통해 구현할 수 있다.
export default function Home({ results }) {
return (
<div className="container">
<Seo title="Home" />
{results?.map((movie: MovieProps) => (
<div className="movie" key={movie.id}>
<img src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} />
<h4>{movie.original_title}</h4>
</div>
))}
...
</div>
);
}
export async function getServerSideProps() {
const { results } = await await (
await fetch(`http://localhost:3000/api/movies`)
).json();
return {
props: {
results,
},
};
}
웹 페이지에서 페이지소스를 보면 이제 영화데이터의 정보도 함께 볼 수 있다.(데이터 페칭된 이후이기 때문에)
하지만 위 함수를 사용해주면 데이터가 페치되기 전까지는 아무것도 보이지 않을것이다.
개발자는 로딩화면이 보이고 데이터를 나중에 띄울 것인지,
SSR을 사용해 데이터를 모두 받은 후 띄울 것인지 선택해서 사용하면 된다!