[์ฅ์ ]
[๋จ์ ]
[์ฅ์ ]
[๋จ์ ]
[์ฅ์ ]
[๋จ์ ]
๐กnext.js์ฑ์
yarn dev (npm run start)
๋ก ์คํํ๋ ๊ฒฝ์ฐ ์์ง build ๋์ง ์์ ์ํ์ ์ฑ์ด๊ธฐ๋๋ฌธ์ SSG๋ก ๊ตฌํ ๋ ์ฝ๋๋ผ๋ SSR๋ฐฉ์์ผ๋ก ์๋
$ yarn create next-app (์ฑ์ด๋ฆ) --js
ํ๋ก์ ํธ ์์ฑ ์ import alias๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค์ ๋์ด, import ๊ฒฝ๋ก๋ก @
(๊ธฐ๋ณธ๊ฐ)๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ( @
๋ src/
ํด๋๋ฅผ ๋ปํจ )
yarn dev
๋ก ํ๋ก์ ํธ๋ฅผ ์คํ์ํค๋ฉด src/pages
ํด๋ ๋ด๋ถ์ index.js๊ฐ ์คํ ๋จ
index.js
๋ ์คํ ์ _app.js
์ _document.js
๋ฅผ ์ฐจ๋ก๋ก ๊ฑฐ์น ๋ค ์คํ
_app.js
_document.js
ํด๋๊ฒฝ๋ก Route ๋น๊ณ pages/post/index.jsx /post indexํ์ผ์ ํด๋นํ์ผ์ ํด๋๋ช ์ผ๋ก ๊ฒฝ๋ก๊ฐ ์ฐ๊ฒฐ๋จ pages/post/detail.jsx /post/detail index.jsx๋ฅผ ์ ์ธํ ํ์ผ๋ช ์ ํด๋๋ช ์ ๋ฃจํธ ๋ค์ ์ฐ๊ฒฐ ๋จ pages/post/[id].jsx /post/${id} - ๋์ ์ผ๋ก ๊ฒฝ๋ก ์์ฑ
- useRouter() ๋ฅผ ์ฌ์ฉํด์ ๊ฒฝ๋ก์ id๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์์pages/post/[โฆparams].jsx /post/*/*/* - ๋์ ์ผ๋ก ๊ฒฝ๋ก ์์ฑ
- ๊ฒฝ๋ก์ ๊น์ด์ ์๊ด์์ด ๋ชจ๋ ๊ฒฝ๋ก๋ก ์ ๊ทผ ๊ฐ๋ฅ
- useRouter()๋ฅผ ํตํด ๊ฐ ๊ฒฝ๋ก๋ฅผ ๋ฐฐ์ดํํ๋ก ๋ฐ์์ฌ ์ ์์๊ฒฝ๋ก ์ฐ์ ์์
detail(๊ฒฝ๋ก๋ช ์ง์ ๋ช ์) > [id](ํ๋์ ์์ฑ ๋ช ์) > [...params](๋๋จธ์ง ๊ฒฝ๋ก)
import { useRouter } from 'next/router'
const router = useRouter()
// src/pages/post/[id].jsx
// ์ ๊ทผ๊ฒฝ๋ก '/post/1?name=hello'
import { useRouter } from 'next/router'
const router = useRouter()
const {id, name} = router.query
console.log(id) //'1'
console.log(name) //'hello'
// src/pages/user/[...params].jsx
// ์ ๊ทผ๊ฒฝ๋ก '/user/123/abc'
import { useRouter } from 'next/router'
const router = useRouter()
const { params } = router.query
console.log(params) //['123', 'abc']
<Link>
ํ๊ทธ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ํจ์๋ด์์ ํ์ด์ง ์ ํ์ ํ๊ณ ์ถ๋ค๋ฉด router.push()
์ฌ์ฉrouter.push(url)
<button onClick={() => router.push('/post/1')}>1๋ฒ ๊ธ๋ก๊ฐ๊ธฐ</button>
<button onClick={() => router.push({ pathname: '/post/[id]', query: { id: 1 } })}>1๋ฒ ๊ธ๋ก๊ฐ๊ธฐ</button>
getStaticProps()
๋ฅผ ์ฌ์ฉํ ํ์ด์ง)์ ๊ฒฝ์ฐ query๊ฐ ๋น ๊ฐ์ฒด๋ก ์ ๋ฌ ๋ ๋ค์ ์ฑ์์งrouter.isReady
๋ฅผ ์ฌ์ฉํ์ฌ, query๊ฐ ์ค๋น ๋๋ฉด ๊ทธ๋ query ๋ฐ์ดํฐ์ ์ ๊ทผ ํ๋๋ก ํจuseEffect(() => {
if (router.isReady) console.log(router.query)
}, [router.isReady])
<a>
ํ๊ทธ ๋์ <Link>
ํ๊ทธ๋ฅผ ์ฌ์ฉํด์ผ ํจimport Link from "next/link";
// ๊ธฐ๋ณธ๋ฐฉ์
<Link href='/post'>post๋ก ์ด๋</Link>
// ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ
<Link href='/post?name=hello'>post๋ก ์ด๋</Link>
// url๊ฐ์ฒด
<Link href={{ pathname: '/post', query: { name: 'hello' } }}>post๋ก ์ด๋</Link>
์ฑ๋ฅ์ต์ ํ๋ฅผ ์ํด ๊ธฐ์กด์ <img />
ํ๊ทธ๊ฐ ์๋ <Image />
ํ๊ทธ๋ฅผ ์ฌ์ฉ
Imageํ๊ทธ ํน์ง
import Image from 'next/image';
<Image
src="/images/profile.jpg" // ์ด๋ฏธ์ง ์ฃผ์, ๊ฒฝ๋ก
height={200} // CLS ๋ฐฉ์ง๋ฅผ ์ํด ์ฌ์ฉ๋จ
width={200} // CLS ๋ฐฉ์ง๋ฅผ ์ํด ์ฌ์ฉ๋จ
alt="profile"
/>
position: relative
๋ฐ display: block
์์ฑ์ ๊ฐ์ง๊ณ ์์ด์ผ ํจ<Image src="/images/profile.jpg" alt="profile" fill />
<Image src={dogImage} alt="dog" fill sizes="(max-width: 768px) 33vw, (max-width: 1200px) 50vw, 100vw"/>7
/* ํ๋ฉด ํฌ๊ธฐ๊ฐ 768px ๋ณด๋ค ์๋ค๋ฉด ๋ทฐํฌํธ ๊ฐ๋ก๊ธธ์ด์ 33% ์ ๋์ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง ์ด๋ฏธ์ง๋ฅผ ๊ณต๊ธํด์ฃผ๊ณ ,
1200px ๋ณด๋ค ์๋ค๋ฉด ๋ทฐํฌํธ ๊ฐ๋ก๊ธธ์ด์ 50% ์ ๋์ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง ์ด๋ฏธ์ง๋ฅผ ๊ณต๊ธํด์ฃผ๊ณ ,
๊ทธ๊ฒ๋ณด๋ค ํฌ๋ค๋ฉด 100% ํฌ๊ธฐ๋ฅผ ๊ฐ์ง ์ด๋ฏธ์ง๋ฅผ ๊ณต๊ธํ๋๋ก ์ค์ */
//next.config.js
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'via.placeholder.com', // ์ ๋ถ ํ์ฉํ๊ณ ์ ํ ๊ฒฝ์ฐ์๋ **
port: '',
pathname: '**', // ์ ๋ถ ํ์ฉํ๊ณ ์ ํ ๊ฒฝ์ฐ์๋ **
},
],
},
Prop โclassNameโ did not match
๋ผ๋ ๊ฒฝ๊ณ ๊ฐ ๋ฐ์ํ๋ฉฐ ์คํ์ผ์ด ์ ์ฉ๋์ง ์์.import styled from 'styled-components'
export const Container = styled.div`
display: flex;
flex-direction: column;
`
function Post() {
return (
<Container>
<h1>Post</h1>
</Container>
)
}
export default Post
// ERROR! Prop โclassNameโ did not match
styled-components์ ๊ฒฝ์ฐ ์ฌ์ฉ์์๊ฒ ํ์ด์ง๋ฅผ ๊ณต๊ธ ํ ๋ ๋ง๋ค ์๋ก ๊ณ ์ ํ classname์ ์ค์ ํ๋๋ฐ, next.js์์ build์์ ๋ฏธ๋ฆฌ ๋ ๋๋ง ํ ์ ์ ํ์ด์ง์ classname๊ณผ ์๋ฒ์์ ์ฌ์ฉ์์๊ฒ ๊ณต๊ธํ๋ฉฐ ๋ ๋๋ง ๋ ํ์ด์ง๊ฐ์ classname์ด ์ผ์นํ์ง ์๊ธฐ๋๋ฌธ
next.config.js
์ compiler์ถ๊ฐ
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
compiler: {
styledComponents: true
}
}
module.exports = nextConfig
styledComponents: true
์ค์ ์ ์ต์ด ๋น๋ํ ๋์ ์๋ฒ/ํด๋ผ์ด์ธํธ์์ ๋ ๋๋งํ ๋์ className ์ ์ผ์น์ํด getStaticProps()
ํจ์๋ฅผ ๋ง๋ค์ด ํ์ํ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ ๋ค, props
์ ๊ฐ์ฒดํํ๋ก ๋ด์ returngetStaticProps()
์์ returnํ ๋ฐ์ดํฐ๋ ์ปดํฌ๋ํธ์์ props๋ก ๋ฐ์ ์ ์๋ค//๊ธฐ๋ณธํํ
export async function getStaticProps() {
// ๋ฐ์ดํฐ ํธ์ถ
return { props: { ๋ฐ์ดํฐ } }
}
function ํ์ด์ง์ปดํฌ๋ํธ({ ๋ฐ์ดํฐ }) {
return <div></div>
}
import axios from 'axios'
export async function getStaticProps() {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts')
const posts = response.data
return { props: { posts } }
}
function Post({ posts }) {
return (
<div>
{posts.map((post) => (
<div key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
))}
</div>
)
}
export default Post
revalidate: 10 // ๋จ์: ์ด(s)
revalidate
์ต์
์ ์ฌ์ฉํ๋ฉด ์ค์ ํ ์๊ฐ์ด ์ง๋ ๋ ๋ง๋ค ์ฃผ๊ธฐ์ ์ผ๋ก ๋ค์ buildํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์๋ก ๋ถ๋ฌ์ดISR(Incremental Static Regeneration)
๋ฐฉ์์ด๋ผ ํจ// ์์
export async function getStaticProps() {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
const posts = response.data;
return { props: { posts }, revalidate: 10 }; //10์ด์ ํ๋ฒ์ฉ ๋ค์ build
}
๋์ ์ธ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ ํ์ด์ง์์, next.js๋ ์ฌ์ ์ ์ด๋ค ํ์ด์ง ๊ฒฝ๋ก๋ฅผ ๋ฏธ๋ฆฌ ๋ ๋๋ง ํด๋์ด์ผ ํ๋์ง ์ ์ ์์
getStaticPaths()
๋ ๋์ ์ธ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ ํ์ด์ง์์ ํน์ ํ ๊ฒฝ๋ก๋ฅผ ๋ฏธ๋ฆฌ ๋ ๋๋ง ํด๋ ์ ์๋๋ก ์ง์
getStaticPaths()
์ return ๊ฐ์ ํตํด ์ง์ ์ด ๊ฐ๋ฅํ๋ฉฐ, ๋ฐฐ์ดํ์์ผ๋ก ๊ฒฝ๋ก๋ฅผ ๋ด์ ์ค์
์๋ฅผ๋ค์ด /posts/[id]
์ ๋์ ๊ฒฝ๋ก๋ก ์ ๊ทผํ๋ ํ์ด์ง๊ฐ ์์ ๋, ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ฉด, posts/1
, posts/2
, posts/3
ํ์ด์ง๋ฅผ build์์ ์ ์ ์ผ๋ก ์์ฑ
export async function getStaticPaths() {
return {
paths: [
{params: {id: '1'}}, {params: {id: '2'}}, {params: {id: '3'}}...
]
}
}
export async function getStaticPaths() {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
const posts = response.data;
const paths = posts.map((post) => ({ params: { id: `${post.id}` } }));
return { paths, fallback: false };
}
1~100๋ฒ๊ธ๋ง ์๋ ์ํฉ์ ์ฌ์ฉ์๊ฐ 101๋ฒ ๊ฒ์๊ธ์ ์ ๊ทผํ๋ ๊ฒฝ์ฐ
fallback: false
=> 404ํ์ด์ง๋ก ์ด๋fallback: true
=> ๊ทธ์ ์์ผ ํด๋น id๋ก ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๊ฐ์ ธ์ด (๋ก๋ฉํ๋ฉด ์ค์ ๊ฐ๋ฅ)fallback: blocking
=> ๊ทธ์ ์์ผ ํด๋น id๋ก ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๊ฐ์ ธ์ด (๋ก๋ฉํ๋ฉด ์์ด, ๋ก๋ฉ์ด ์๋ฃ๋์ด์ผ ํ์ด์ง๋ฅผ ๋ณด์ฌ์ค)true
๋ blocking
๋ก ์ต์
๊ฐ์ ์ค์ ํ๋ ๊ฒฝ์ฐ ์ฌ์ฉ์๊ฐ ํ๋ฒ ์ ๊ทผํ ํ์ด์ง๋ ์บ์ฑ๋์ด ์ฌ์ฌ์ฉ
true
์ค์ ์ ๋ณด์ฌ ์ค ๋ก๋ฉํ์ด์ง๋ router.isFallback
์ผ๋ก boolean๊ฐ์ ๋ฐ์์ ์๋์ฒ๋ผ ์์ฑ
const router = useRouter()
if (router.isFallback) {
return <div>๋ก๋ฉ ์ค..</div>
}
getStaticPaths()
๋ก ์์ฑํ๊ฒ ๋๋ ํ์ด์ง์์ ์ธ๋ถ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ ๋ ๋ฉ์๋๋ฅผ ํจ๊ป ์ฌ์ฉํ๋คgetStaticPaths()
์์ returnํ ๊ฒฝ๋ก๋ฐ์ดํฐ๋ฅผ getStaticProps()
๋ก ๋ฐ์์ ์ฌ์ฉ ํ ์ ์๋คgetStaticPaths()
์์ returnํ paths๋ฐฐ์ด์ ๊ธธ์ด๋งํผ getStaticProps()
๊ฐ ์คํ์ฝ๋ ํ๋ฆ:
getStaticPaths()
> paths์ ๋ฌ >getStaticProps()
> props์ ๋ฌ =>ํ์ด์ง์ปดํฌ๋ํธ()
import axios from "axios";
// getStaticPaths => ์ง๊ธ ์ ๊ทผ ํ ์์ด๋ ๋ฒํธ ๋ช
์
export async function getStaticPaths() {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/posts"
);
const posts = response.data;
const paths = posts.map((post) => ({ params: { id: `${post.id}` } }));
// paths = [{params: {id: 1}}, {params: {id: 2}}, {params: {id: 3}} ...]
return { paths };
}
// getStaticProps => getStaticPaths๊ฐ ๋ฆฌํดํ ๋ฒํธ๋ฅผ ํ ๋๋ก ์์ฒญ์ ๋ณด๋ด๊ณ , ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ
export async function getStaticProps({ params }) {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/posts/${params.id}`
);
const post = response.data;
return { props: { post } };
}
// ํ์ด์ง์ปดํฌ๋ํธ => getStaticProps๊ฐ ๋ฆฌํดํ ๋ฐ์ดํฐ๋ฅผ props๋ก ๋ฐ์์ ์ฌ์ฉ
function PostDetail({ post }) {
return (
<div>
<span>{post.id}</span>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
);
}
export default PostDetail;
getServerSideProps()
ํจ์๋ฅผ ์ฌ์ฉํ๋ฉฐ ์ฌ์ฉ ๋ฐฉ์์ getStaticProps()
์ ๊ฐ์import axios from 'axios'
export async function getServerSideProps() {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts')
const posts = response.data
return { props: { posts } }
}
function Post({ posts }) {
return (
<div>
{posts.map((post) => (
<div key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
))}
</div>
)
}
export default Post
๐ก์ ๋ง ์ ๋ฐ์ดํธ๊ฐ ์ฆ์ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด, SSR ๋ฐฉ์ ๋ณด๋ค๋ SSG ๋ฐฉ์์ revalidate ์ต์ ์ ๋ช ์ํ์ฌ ์ฌ์ฉํ๋๊ฒ์ ์ถ์ฒ