00 Welcome
01 Creating a Project
yarn create next-app
00 Library VS Framework
주요 차이점 : 통제의 역전(Inversion of Control)
라이브러리 : 메서드를 호출하면 사용자가 제어
예) 사용자가 파일 이름이나 구조 등을 결정, 모든 결정을 내림
프레임워크 : 제어가 역전되어 프레임워크가 사용자를 호출
예) 파일 이름이나 구조 등을 정해진 규칙에 따라 만들고 따름
01 Pages
예외 : index.js 의 경우 localhost:3000/index가 아닌 localhost:3000 이다.
02 Static Pre Rendering
SPA와 SSR의 단점을 해결하기 위해서
리액트에 서버사이드렌더링 기능을 더하여 SPA와 SSR의 장점을 가짐
리액트에 SSR 기능을 추가하려면
웹 서버를 만들어 주어야 하고, 웹팩 설정, 데이터 로딩, 코드 스플리팅 등 복잡한 과정을 필요
Next.js를 통해 이러한 것들을 설정하지 않고 사용
Client-Side-Rendering
SPA로 클라이언트 사이드에서 HTML을 반환한 후에
JS가 동작하면서 데이터만을 주고 받아서 클라이언트에서 렌더링을 진행하는 것
동작과정
CSR의 장점
CSR의 단점
Server-Side-Rendering
서버에서 렌더링을 작업하는 렌더링 방식
전통적인 웹 어플리케이션 렌더링 방식
사용자가 웹 페이지에 접근 서버에 각각의 페이지에 대한 요청
서버에서 html, js 파일 등을 다 다운로드해서 화면에 렌더링하는 방식
SSR의 장점
SSR의 단점
Hydration
03 Routing
import Link from "next/link";
import { useRouter } from "next/router";
export default function NavBar() {
// Nextjs의 router hook
const router = useRouter();
return (
<nav>
<Link
href="/"
style={{ color: router.pathname === "/" ? "red" : "blue" }}
>
Home
</Link>
<Link
href="/about"
style={{ color: router.pathname === "/about" ? "red" : "blue" }}
>
About
</Link>
</nav>
);
}
04 CSS Modules
name.module.css
일반적으로 적는 ccs 양식과 같이 작성
임포트시켜서 사용
다른 컴포넌트에서 동일 클래스 이름을 사용할 수 있음
Adding Component-Level CSS
Next.js는[name].module.css 파일 명명 규칙을 사용하여 CSS Module을 지원
.link {
text-decoration: none;
}
.active {
color: tomato;
}
import Link from "next/link";
import { useRouter } from "next/router";
import styles from "./NavBar.module.css";
export default function NavBar() {
const router = useRouter();
return (
<nav>
<Link
href="/"
className={`${styles.link} ${
router.pathname === "/" ? styles.active : ""
}`}
>
Home
</Link>
<Link
href="/about"
className={[
styles.link,
router.pathname === "/about" ? styles.active : "",
].join(" ")}
>
About
</Link>
</nav>
);
}
05 Styles JSX
import Link from "next/link";
import { useRouter } from "next/router";
export default function NavBar() {
const router = useRouter();
return (
<nav>
<Link href="/" legacyBehavior>
<a className={router.pathname === "/" ? "active" : ""}>Home</a>
</Link>
<Link href="/about" legacyBehavior>
<a className={router.pathname === "/about" ? "active" : ""}>About</a>
</Link>
<style jsx>{`
nav {
background-color: tomato;
}
a {
text-decoration: none;
}
.active {
color: yellow;
}
`}</style>
</nav>
);
}
Built-In CSS Support (내장 CSS 지원)
Next.js를 사용하면 JavaScript 파일에서 CSS 파일을 가져올 수 있음
Next.js가 import 개념을 JavaScript 이상으로 확장
CSS-in-JS 격리된 범위 CSS에 대한 지원을 제공하기 위해 styled-jsx를 번들로 제공
06 Custom App
Next.js는 App 컴포넌트를 사용하여 page를 초기화
재정의 후 페이지 초기화를 제어
기본 App을 재정의하려면 아래와 같이 ./pages/_app.js 파일을 만듬
export default function MyApp({ Component, pageProps }) {
return < Component {...pageProps} />
}
https://nextjs.org/docs/advanced-features/custom-app
import "@/styles/globals.css";
import NavBar from "@/components/NavBar";
export default function App({ Component, pageProps }) {
return (
<>
<NavBar />
<Component {...pageProps} />
<style gsx global>{`
a {
color: white;
}
`}</style>
</>
);
}
00 Patterns
layout Pattern
head component
01 Fetching Data
import Seo from "@/components/Seo";
import { useEffect, useState } from "react";
const API_KEY = "xxxxxxxxxxx";
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();
setMovies(results);
})();
}, []);
return (
<div>
<Seo title="Home" />
{!movies && <h4>Loading...</h4>}
{movies?.map((movie) => (
<div key={movie.id}>
<h4>{movie.original_title}</h4>
</div>
))}
</div>
);
}
IIFE 즉시 실행 함수 표현 ()()
첫 괄호 익명함수를 감싸 실행 될 함수가 전역 스코프에 불필요한 변수를 추가하거나
내부 안으로 다른 변수들 접근하는 것을 막을 수 있는 방법
두번째 괄호 즉시 실행 함수를 실행
함수를 즉시 해석 실행
02 Redirect and Rewrite
Next.js에서 커스텀 설정을 하기 위해서 프로젝트 디렉터리의 루트에
next.config.js 또는 next.config.mjs 파일을 만들 수 있음
next.config.js는 JSON 파일이 아닌 일반 Node.js 모듈
Next.js 서버 및 빌드 단계에서 사용
브라우저 빌드에는 포함되지 않음
Redirect을 사용하면 들어오는 request 경로를 다른 destination 경로로 Redirect 함
Redirect을 사용하려면 next.config.js에서 redirects 키를 사용
Redirects은 새 페이지로 reroute되고 URL 변경 사항을 표시
Redirects은 source, destination 및 permanent 속성이 있는 객체를 포함하는 배열을 반환하는 비동기 함수
source : 들어오는 request 경로 패턴 (request 경로)
destination: 라우팅하려는 경로 (redirect할 경로)
permanent
03 Server Side Rendering
export default function Home({ data }) {
// 데이터 랜더링
}
// 매 request마다 실행됩니다.
export async function getServerSideProps() {
const res = await fetch(`https://.../data`);
const data = await res.json();
// props를 통해 page에 data전달
return {
props: {
data
}
}
}
04 Dynamic Routes
page에 대괄호([param])를 추가 Dynamic Route를 생성
/movies/1, /movies/abc 등과 같은 모든 경로는 pages/movies/[id].js와 일치
const router = useRouter()
const { id } = router.query
대괄호 안에 세 개의 점(...)을 추가하여
모든 경로를 포착하도록 Dynamic Routes를 확장할 수 있음
pages/movies/[...id].js는 /movies/1와 일치하지만
/movies/1/2
/movies/1/ab/cd
등과도 일치
일치하는 매개변수는 페이지에 쿼리 매개변수로 전송
항상 배열이므로 /movies/a 경로에는 다음 쿼리 개체가 있음
ex) { "id": ["a"] }
05 Movie Detail
클라이언트 측 전환을 처리
이 방법은 next/link가 충분하지 않은 경우에 유용
url: UrlObject | String: 탐색할 URL
as: UrlObject | String: 브라우저 URL 표시줄에 표시될 경로에 대한 선택적 데코레이터
router.push({
pathname: '/post/[pid]',
query: { pid: post.id },
})
외부 URL에 대해서는 router.push()를 사용할 필요가 없음
window.location을 사용하는 것이 더 적합
next/link 도 사용방법이 동일
아래 코드 참조
import Seo from "@/components/Seo";
import Link from "next/link";
import { useRouter } from "next/router";
export default function Home({ results }) {
const router = useRouter();
// router.push
const onClick = (id, title) => {
router.push(
{
pathname: `/movies/${id}`,
query: {
title,
},
},
`/movies/${id}`
);
};
return (
<div className="container">
<Seo title="Home" />
{results?.map((movie) => (
<div className="movie" key={movie.id}>
// Link
<Link
href={{
pathname: `/movies/${movie.id}`,
query: {
title: movie.original_title,
},
}}
as={`/movies/${movie.id}`}
>
<img src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`} />
</Link>
<h4 onClick={() => onClick(movie.id, movie.original_title)}>
{movie.original_title}
</h4>
</div>
))}
<style jsx>{`
.container {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 20px;
gap: 20px;
}
.movie {
cursor: pointer;
}
.movie img {
max-width: 100%;
border-radius: 12px;
transition: transform 0.2s ease-in-out;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
}
.movie:hover img {
transform: scale(1.05) translateY(-10px);
}
.movie h4 {
font-size: 18px;
text-align: center;
}
`}</style>
</div>
);
}
export async function getServerSideProps() {
const { results } = await (
await fetch(`http://localhost:3000/api/movies`)
).json();
return {
props: {
results,
},
};
}
06 404Pages