Next.js
자바스크립트 웹 프레임워크
개요
프로젝트 시작
npx create-next-app@latest
npm run dev
폴더 방식 라우팅
// /app/layout.jsexport default function RootLayout({ children }) {return (<html lang='en'><body><div className='navbar'><Link href='/'>home</Link><Link href='/list'>list페이지</Link></div>{children}</body></html>);}
이미지 최적화
import Image from 'next/image'import 이미지 from './food0.png'export default function Home() {return(<div><Image src={이미지} alt="옥수수"/><div/>)}
Server/Client Component
'use client';import { useState } from 'react';export default function ListItem({ item, idx }) {const [count, setCount] = useState(0);const onPlusClick = () => {setCount(count + 1);};return (<div><img src={`/images/food${idx}.png`} alt='음식' /><h3>{item}</h3><span>{count}</span><button onClick={onPlusClick}>+</button></div>);}
서버 컴포넌트와 클라이언트 컴포넌트의 사용 사례
패턴
MongoDB 연결하기
// /util/database.jsimport { MongoClient } from 'mongodb';const url = process.env.NEXT_PUBLIC_MONGO_CODE;const options = { useNewUrlParser: true };let connectDB;if (process.env.NODE_ENV === 'development') {if (!global._mongo) {global._mongo = new MongoClient(url, options).connect();}connectDB = global._mongo;} else {connectDB = new MongoClient(url, options).connect();}export { connectDB };
// /app/list/page.jsimport { connectDB } from '@/util/database';import Link from 'next/link';export default async function List() {const client = await connectDB;const db = client.db('forum');const posts = await db.collection('post').find().toArray();return (<div className='list-bg'>{posts.map(el => (<div key={el._id} className='list-item'><h4>{el.title}</h4><p>{el.content}</p></div>))}</div>);}
Dynamic Route
// /app/detail/[id]/page.jsexport default async function Detail(props) {console.log(props);return <div></div>;}
// http://localhost:3000/detail/647ef278724cb7262751b0ed 로 접속했을 때의 콘솔{ params: { id: '647ef278724cb7262751b0ed' }, searchParams: {} }
// /app/detail/[id]/page.jsimport { ObjectId } from 'mongodb';import { connectDB } from '@/util/database.js';export default async function Detail({ params }) {const db = (await connectDB).db('forum');const result = await db.collection('post').findOne({ _id: new ObjectId(params.id) });return (<div><h1>상세페이지</h1><h4>{result.title}</h4><p>{result.content}</p></div>);}
// /app/list/page.jsimport { connectDB } from '@/util/database';import Link from 'next/link';export default async function List() {const client = await connectDB;const db = client.db('forum');const posts = await db.collection('post').find().toArray();return (<div className='list-bg'>{posts.map(el => (<Link key={el._id} href={`/detail/${el._id}`}><div className='list-item'><h4>{el.title}</h4><p>{el.content}</p></div></Link>))}</div>);}
useRouter
'use client';import { useRouter } from 'next/navigation';export default function DetailLink({ id }) {const router = useRouter();return (<buttononClick={() => {router.push(`/detail/${id}`);}}>상세보기</button>);}
클라이언트 컴포넌트에서 DB데이터 꺼내기
'use client';import Link from 'next/link';import DetailLink from './DetailLink';import axios from 'axios';export default function Listitem({ posts }) {const removePost = async (id) => {const res = await axios.delete(`http://localhost:3000/api/remove/${id}`);console.log(res.data);};return (<>{posts.map((el) => (<div key={el._id} className='list-item'><Link href={`/detail/${el._id}`}><h4>{el.title}</h4></Link><p>{el.content}</p><DetailLink id={el._id} /><button onClick={() => removePost(el._id)}>삭제</button></div>))}</>);}
서버 컴포넌트에서 req.query 사용하기
import { connectDB } from '@/util/database';import { ObjectId } from 'mongodb';export default async function write(req, res) {if (req.method === 'DELETE') {try {const client = await connectDB;const db = client.db('forum').collection('post');const result = await db.deleteOne({ _id: new ObjectId(req.query.id) });return res.status(200).send('게시글 삭제 성공');} catch (err) {console.error(err);}}}
Pre-rendering (Next 13 이전)
Static Site Generation(SSG)
Server Side Rendering(SSR)
Incremental Static Regeneration(ISR)
App Router에서의 렌더링 (13버전)
App Router vs Pages Router
Pages Router
클라이언트 중심 라우팅
src/pages├─_app.js // root layout├─index.js // root page├─a-page.js└─b-page└─index.js└─component.js // 라우팅과 관련없는 코드지만 b-page/component라는 경로가 생김└─b-subpage.js