어떤 주소에 어떤 페이지를 보여줄지 정하는 것
파일의 경로가 주소에 매칭되는 라우팅 방식, pages라는 이름의 폴더 안에 페이지 파일을 만들면 알아서 라우팅한다.
path 생성 구조
/pages
└ index.js -> my-domain.com/
└ about.js -> my-domain.com/about
└ /products
└ index.js -> my-domain.com/products
└ [id].js -> my-domain.com/products/1
next.js에서는 파일시스템 기반 라우팅 방식이 지원된다.
pages라는 폴더 이름은 next.js에서 정해주는 것이기 때문에 바꾸면 안 된다.
index.js : 홈페이지에 해당
페이지를 나누려면 페이지 파일에서 컴포넌트를 default로 export하면 된다.
export default function Search() {
return <>Search Page</>;
}
여러 주소를 하나의 페이지 컴포넌트에서 처리하는 것
파일 이름을 대괄호로 쓰면 안에 이름을 변수처럼 사용할 수 있다.
ex) [id].js
대괄호 표기법은 파일뿐만 아니라 폴더에서도 사용할 수 있다.
a 태그로 페이지를 이동하면 html 전체를 렌더링하지만
link 컴포넌트를 쓰면 페이지 전체를 불러오는 것이 아니라 필요한 데이터만 불러오기 때문에 속도도 빠르고 부드럽다.
외부로 가는 Link 컴포넌트는 a 태그와 똑같이 작동하므로 그냥 무조건 Link 컴포넌트를 사용하면 된다.
import styles from '@/styles/Home.module.css';
export default function Home() {
return (
<div>
<h1>Codeitmall</h1>
<ul>
<li>
<Link href="/products/1">첫 번째 상품</Link>
</li>
<li>
<Link href="/products/2">두 번째 상품</Link>
</li>
<li>
<Link href="/products/3">세 번째 상품</Link>
</li>
<li>
<Link href="https://codeit.kr">코드잇</Link>
</li>
</ul>
</div>
);
}
사이트 주소에서 원하는 값을 가져오는 방법
ex) pages/products/[id].js 페이지에서 router.query['id'] 값으로 Params id에 해당하는 값을 가져올 수 있다.
pages/products/[id].js
import { useRouter } from 'next/router';
export default function Product() {
const router = useRouter();
const{ id } = router.query;//파일 이름에 있는 그 id임
return <div>Product {id} 페이지</div>;
}
param을 가져오는 방법이랑 같다 (router.query
)
http://localhost:3000/search?q=티셔츠
이런 식의 경우에서 q 값을 가져오는 방법은 아래와 같다.
import { useRouter } from 'next/router';
export default function Search() {
const router = useRouter(0);
const { q } = router.query;
return (
<div>
<h1>Search 페이지</h1>
<h2>{q} 검색 결과 </h2>
</div>);
}
router.push()
로 페이지 이동
searchForm.js
import { useRouter } from "next/router";
import { useState } from "react";
export default function SearchForm({ initialValue = "" }) {
const router = useRouter();
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
if (!value) {
router.push(`/`);
return;
}
router.push(`search?q=${value}`);
}
return (
<form onSubmit={handleSubmit}>
<input name="q" value={value} onChange={handleChange} />
<button>검색</button>
</form>
);
}
@/lib/axios.js
import axios from "axios";
const instance = axios.create({
baseURL: "https://learn.codeit.kr/api/codeitmall",
});
export default instance;
@/pages/[id].js
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import axios from "@/lib/axios";
import SizeReviewList from "@/components/SizeReviewList";
export default function Product() {
const [product, setProduct] = useState();
const [sizeReviews, setSizeReviews] = useState([]);
const router = useRouter();
const { id } = router.query; //파일 이름에 있는 그 id임
async function getProduct(targetId) {
const res = await axios.get(`products/${targetId}`);
const nextProduct = res.data;
setProduct(nextProduct);
}
async function getSizeReviews(targetId) {
const res = await axios.get(`/size_reviews/?product_id=${targetId}`);
const nextSizeReviews = res.data.results ?? [];
setSizeReviews(nextSizeReviews);
}
useEffect(() => {
if (!id) return;
getProduct(id);
getSizeReviews(id);
}, [id]);
if (!product) return null;
return (
<div>
<h1>{product.name}</h1>
<img src={product.imgUrl} alt={product.name} />
<SizeReviewList sizeReviews={sizeReviews} />
</div>
);
}
@/pages/index.js
import SearchForm from "@/components/SearchForm";
import ProductList from "@/components/ProductList";
import axios from "@/lib/axios";
import { useEffect } from "react";
import { useState } from "react";
export default function Home() {
const [products, setProducts] = useState();
async function getProducts() {
const res = await axios.get(`/products`);
const nextProducts = res.data.results;
setProducts(nextProducts);
}
useEffect(() => {
getProducts();
}, []);
return (
<>
<h1>Codeitmall</h1>
<SearchForm />
<ProductList products={products} />
</>
);
}
이런식으로 사용하면 된다.
사이트를 운영하다면 주소를 바꾸는 경우가 생긴다. (ex)products/1
-> items/1
)
next.js에서는 폴더 이름과 링크 주소만 바꿔주면 된다.
방문자 중에 옛날 주소를 북마크한 경우가 있다면 사이트에 접속했을 때 오류가 생길 것이다.
이런 상황에는 products로 시작하는 주소를 모두 items로 바꿔주서 이동시켜주면 된다.
이럴 때 사용하는 것이 리다이렉트이다.
async redirects() {
return [
{
source: '/about', //리다이렉트 처리를 할 주소
destination: '/', //이동시킬 주소
permanent: true, //웹 브라우저에게 주소가 바뀌었단 사실을 저장하게 하려면 true로
},
]
},
이 코드를 참고해서next.config.js
를 수정하면 된다.
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
async redirects() {
return [
{
source: '/products/:id',
destination: '/items/:id',
permanent: true,
},
]
},
};
export default nextConfig;
:id는 params
false : 307
true : 308
그냥 pages 폴더에 404.js를 만들면 된다! (매우 간단!)
app.js에 코드를 작성하면 모든 페이지에 적용시킬 수 있다.
공통된 레이아웃(헤더 같은)을 구현할 때 app 컴포넌트에 구현하면 된다.
import "../styles/global.css";
import Container from "@/components/Container";
import Header from "@/components/Header";
export default function App({ Component, pageProps }) {
return (
<>
<Header />
<Container>
<Component {...pageProps} />
</Container>
</>
);
}
_document.js는 HTML 코드의 뼈대를 만드는 역할이라고 볼 수 있다.
서버에서 렌더링 할 때만 이 컴포넌트를 실행한다.
클라이언트에서는 실행하지 않음
인터렉티브한 자바스크립트 사용 불가 (useState, useEffect)
App 컴포넌트에서 Context를 사용하면 사이트 전체에서 활용할 수 있다.
module.css에서 light라는 클래스를 다른 모듈 css에서 사용하려면 :global이라고 사용해야 한다.
:global(.light) .button{
color:#f9f9f9;
background-color:#505050;
}
Next.js에서는 간단한 백엔드 API를 만들 수 있다.
/pages
폴더 아래에 /api
라는 폴더를 만들고 여기에 다음과 같은 자바스크립트 파일을 추가하면 된다.
let cart = [];
export default function handler(req, res) {
if (req.method === 'GET') {
return res.status(200).json(cart);
} else if (req.method === 'PUT') {
cart = req.body;
return res.status(200).json(cart);
} else {
return res.sendStatus(404);
}
}
이렇게 default export로 리퀘스트 객체(req)와 리스폰스 객체(res)를 파라미터로 받는 함수를 만들면 된다.
위 코드는 GET 리퀘스트를 보냈을 때 cart 배열을 리스폰스로 보내 주고, PUT 리퀘스트를 보냈을 때 cart 배열을 수정하는 간단한 코드이다.
이 API의 주소는 Next.js에서 페이지를 만들었을 때의 주소와 마찬가지이다.
/api/cart.js라는 경로이니까 /api/cart라는 주소로 리퀘스트를 보내면 파일에 있는 핸들러 함수를 실행해서 리스폰스를 보내 주는 형태이다.
웹 브라우저에서 http://localhost:3000/api/cart라는 주소로 접속하거나, API 테스트를 해 보면 아래와 같은 JSON 데이터가 리스폰스로 전달될 것이다.
GET 리퀘스트를 보낼 때
GET http://localhost:3000/api/cart
Content-Type: application/json
GET 리퀘스트를 보내고 받은 리스폰스 예시
[]
PUT 리퀘스트를 보낼 때
PUT http://localhost:3000/api/cart
Content-Type: application/json
[1, 2, 3]
PUT 리퀘스트를 보내고 받은 리스폰스 예시
[1, 2, 3]
리퀘스트 객체를 활용하면 리퀘스트의 헤더나 쿠키 같은 값을 사용해서 다양한 동작을 하도록 만들 수 있다.
Next.js-API Routes