🌱 NextJS의 기초를 다지고자 노마드코더의 NextJS입문 강의를 완강했다. 강의를 따라가며 공부한 내용을 기록해두고자 한다!
TypeScript로 프로젝트를 시작하고자 한다면, --typescript
를 추가해주면 된다.
npx create-next-app@latest
npx create-next-app@latest --typescript
👩🏻💻 Library는 개발자로서 내가 사용하는 것
👩🏻💻 Framework는 나의 코드를 불러오는 것
💁🏻♀️ NextJS에서 페이지를 생성하려면? pages 폴더만 보면 된다!
export default 컴포넌트이름
과 같이 default로 export를 꼭 해주어야한다는 것만 잘 지키자!index.js
가 나오게 되어 있다.✨ NextJS의 가장 좋은 기능 중 하나는, 앱에 있는 페이지들이 미리 렌더링된다는 것이다.
CSR은 나의 브라우저가 UI를 만드는 모든 것을 한다는 것을 의미한다!
create-react-app
으로 React 프로젝트를 시작할 때를 생각해보자. index.html
파일의 body태그에는 <div id="root"></div>
밖에 없는 것을 볼 수 있다. 사실상 비어있는 div태그이며, 브라우저가 프론트 서버로부터 프론트 관련 소스(JavaScript파일 등)를 전달받아 UI가 그려진다.NextJS는 React.js를 백엔드에서 동작시켜서 페이지를 미리 만든다!
✨ NextJS에 앱 내에서 페이지를 네비게이트할 때 사용해야만 하는
Link 컴포넌트가 존재한다. (따라서 a태그를 사용하면 경고표시가 뜬다!)
❓ Link태그 사용하지 않으면?
브라우저가 다른 페이지로 보내기 위해 전체 페이지를 새로고침하며,
이는 속도 저하를 야기한다.
import Link from "next/link";
export default function NavBar() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
</nav>
);
}
import styles from "./NavBar.module.css";
export default function NavBar() {
return (
<nav className={styles.nav}>
// 생략
</nav>
);
}
<style>{`
nav {
background-color: tomato;
}
a {
text-decoration: none;
}
.active {
color: ${props.color};
}
`}</style>
NextJS는 App 컴포넌트를 사용하여 page를 초기화한다. 이는 다음과 같은 효과가 있다.
- 페이지 변경 간에 레이아웃 유지
- 페이지 탐색 시 state 유지
- componentDidCatch를 사용한 Custom 에러 처리
- 페이지에 추가 데이터 삽입
- Global CSS 추가
./pages/_app.js
파일을 만든다.index.js
를 렌더링하기 전에 _app.js
을 보고 내용물을 렌더링한다. 그 뒤, index.js
의 내용물을 렌더링한다.export default function MyApp({ Component, pageProps }) {
return (
<>
<NavBar />
<Component {...pageProps} />
<style jsx global>{`
a {
color: white;
}
`}</style>
</>
);
}
Layout 컴포넌트를 만들어 App 컴포넌트에 적용시켜준다.
// Layout 컴포넌트
import NavBar from "./NavBar";
export default function Layout({ children }) {
return (
<>
<NavBar />
<div>{children}</div>
</>
);
}
// _app 컴포넌트
import Layout from "components/Layout";
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
NextJS에서 제공하는
Head
컴포넌트를 사용해
페이지를 이동할 때마다 해당 title이 나타나게 만들어준다.
다음 이미지는 About 페이지로 이동했을 때의 모습이다.
// Seo.js
import Head from "next/head";
export default function Seo({ title }) {
return (
<Head>
<title>{title} | Next Movies</title>
</Head>
);
}
// About.js
import Seo from "components/Seo";
export default function about() {
return (
<div>
<Seo title="Home" />
<h1>About</h1>
</div>
);
}
- redirect를 이용해서 한 페이지에서 다른 페이지로 이동하게 할 수도 있고, 아예 다른 URL의 웹사이트로 이동하게 할 수도 있다.
/old-blog/포스트숫자
를 url에 입력하면,/new-sexy-blog/포스트숫자
로 이동하는 것을 확인할 수 있다.:path*
와 같이 뒤에 별표를 붙여주면/old-blog/1212/comments/122
와 같이 뒤에 오는 모든 경로를 다 잡아낸다.
module.exports = {
reactStrictMode: true,
async redirects() {
return [
{
source: "/old-blog/:path*",
destination: "/new-sexy-blog/:path*",
permanent: false,
},
];
},
};
- redirects는 old-blog로 접속할 때, 유저가 url이 바뀌는 것을 볼 수 있다. (new-sexy-blog로!)
- rewrites는 유저를 redirect시키기는 하지만, url은 변하지 않는다!
- 설정한 source를 통해 destination에 접근할 수 있다! (ex)
fetch("/api/movies")
const API_KEY = "0123456789";
module.exports = {
reactStrictMode: true,
async redirects() {}, // 생략
async rewrites() {
return [
{
source: "/api/movies",
destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`,
},
];
},
};
- .env 파일 생성하고 가져다가 쓰면 된다.
- gitignore 됐는지 확인할 것!
// .env 파일
API_KEY=0123456789
// next.config.js
const API_KEY = process.env.API_KEY;
💥 서버에서만 실행되는 함수임
- getServerSideProps 함수는 오직 백엔드(서버)에서만 실행된다.
- 따라서, getServerSideProps 함수에 API key를 써주면 client한테 보여지지 않는다.
🎁 props란 key를 갖는 객체를 return함
- getServerSideProps는 props라는 key를 갖는 object를 return한다.
- props라는 key에는 원하는 데이터를 넣을 수 있다.
- 이로써 원하는 데이터를 props로 page에게 전달 할 수 있다.
// index.js
export default function Home({ results }) {
// 생략
}
export async function getServerSideProps() {
const { results } = awiat(await fetch("/api/movies")).json();
return {
props: {
results,
},
};
}
- Server Side Rendering으로 렌더링되는 상태에선 API가 호출되기 전까지
아무것도 화면에 보이지 않을 것이다.- 데이터가 유효할 때 화면을 보여주는 것(SSR)이 좋은 지,
loading 화면을 보여준 뒤 데이터를 받는 것(CSR)이 좋은지는 나의 선택이다.
- SSR 방식은 해당 페이지의 데이터가 들어오기 전까진 아무것도 볼 수 없다가 해당 페이지의 데이터만 들어오면 전체를 다 볼 수 있다. (다른 페이지를 갈 때도 이 과정이 필요하다.)
- CSR 방식은 모든 JS파일들이 들어와야 (’Loading…’) 보여지는데, 그 대신 다른 페이지를 갈 때는 이미 JS 파일들을 받았으니 SSR 방식보다는 빠르게 화면을 볼 수 있다.
❓ URL에 변수를 넣는 방법
- pages폴더에 movies 폴더 생성해준 뒤
[id].js
파일 생성- 이름 짓는 것은 자유다. 대괄호만 잘 작성해주기!
<Link href={`/movies/${movie.id}`}>{movie.original_title}</Link>
// index.js
export default function Home({ results }) {
const router = useRouter();
const onClick = (id) => {
router.push(`/movies/${id}`);
};
return (
{results?.map((movie) => (
<div onClick={() => onClick(movie.id)} className="movie" key={movie.id}>
{/* 생략 */}
</div>
))
)
}
- 아래와 같이 router를 설정하면, 영화 상세보기 시에 url이 다음과 같이 나오는 것을 볼 수 있다.
http://localhost:3000/movies/315162?id=315162&title=potatos
- router의 2번째 속성인
as
는 브라우저의 URL을 마스킹한다.,
뒤에 브라우저에 보일 URL을 작성하면 된다.router.query.title
은 유저가 홈→상세로 넘어올 때만 존재한다.
// index.js Home컴포넌트
const onClick = (id, title) => {
router.push(
{
pathname: `/movies/${id}`,
query: {
title: title,
},
},
`/movies/${id}`
);
};
// [id].js
export default function Detail() {
const router = useRouter();
console.log(router);
return (
<div>
<h4>{router.query.title || "Loading..."}</h4>
</div>
);
}
catch-all URL
은 뭐든 캐치해내는 URL이다.router.query.params || []
와 같은 처리를 해주는 이유
- 홈페이지를 거쳐서 가는것이 아니라 바로 상세페이지로 가면,
서버는 영화에 대한 정보를 모르기 때문에 error 발생
// index.js
export default function Home({ results }) {
const router = useRouter();
const onClick = (id, title) => {
router.push(`/movies/${title}/${id}`);
};
return (
<Link href={`/moives/${movie.original_title}/${movie.id}`}>
{movie.original_title}
</Link>
)
}
// [...params].js
export default function Detail() {
const router = useRouter();
const [title, id] = router.query.params || [];
return (
<div>
<h4>{title}</h4>
</div>
);
}
많은 도움 되었습니다.