데이터베이스 종류는 여러가지가 있는데
대표적으로 관계형, 비관계형 데이터베이스가 있습니다.
관계형 데이터베이스는 데이터를 엑셀처럼 표에 저장합니다.
데이터 입출력시 SQL이라는 언어를 사용해야하고 미리 스키마 정의(표만들기)도 해야하고 데이터 중복저장을 피하기 위해 정규화해야하고 여러가지 귀찮은 점들을 신경써야합니다.
주로 안정적인 데이터저장과 운영이 필요한 곳에서 쓰면 좋습니다.
비관계형 데이터베이스는 자료를 조금 더 자유로운 형식으로 저장할 수 있고
SQL 언어, 스키마 정의(표 만들기), 정규화 이런게 대부분 필요없습니다.
분산처리를 기본적으로 잘해서 주로 SNS 서비스처럼 많은 데이터 입출력이 필요할 때도 강점을 보입니다.
우리는 그 중에 비관계형 데이터베이스인 MongoDB를 사용합시다.
데이터를 자바스크립트 object자료형과 똑같은 모양으로 저장할 수 있어서 편리하고
mongodb.com 들어가면 500mb 정도 용량의 DB 무료 호스팅도 받을 수 있습니다.

collection을 하나 만들어서 그안에 document를 만들어서 데이터를 기록하는 식으로 데이터를 저장합니다.

식당예약서비스 운영하는데 예약내역을 저장하고 싶으면 이렇게 저장하면 됩니다.
데이터 한 덩어리는 document에 마음대로 저장해두면 됩니다.
ex. util/database.js
import { MongoClient } from 'mongodb'
const url = 'DB접속URL~~'
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 }
import { connectDB } from "/util/database.js"
export default async function Home() {
let db = (await connectDB).db('forum');
let result = await db.collection('post').find().toArray();
return (
<main>
{result[0].title}
</main>
)
}
DB입출력하는 코드는 server component 안에서만 사용합시다. client component 안에 적은 코드는 유저들도 쉽게 볼 수 있기 때문에 그렇습니다.
await db.collection('post').find().toArray()
MongoDB에서 특정 컬렉션에 있는 모든 document를 가져오고 싶으면 위 처럼 작성하면 됩니다.
db.collection(컬렉션명).findOne(찾을document내용)
게시물 하나만 MongoDB에서 찾아서 가져오고 싶으면 위 처럼 작성하면 됩니다.
근데 게시물 하나를 정확히 찾아오고 싶을 땐 .findOne({ _id: ObjectId('아이디명') }) 이렇게 _id로 가져와서 보여주는게 좋습니다.
let result = await db.collection('post').findOne({_id : new ObjectId(props.params.어쩌구)});
URL 파라미터 : [id]를 이용하여 dynamic route를 만들어뒀다면 그 dynamic route 부분에 입력한 내용을 가져오는 방법
(이전에 props로 클릭할시 맞는 id로 이동하는 링크를 만들어두어야 합니다.)
(/list/page.js)
import { connectDB } from "@/util/database.js"
import Link from "next/link";
export default async function List() {
let db = await connectDB).db('forum')
let result = await db.collection('post').find().toArray()
return (
<div className="list-bg">
{
result.map((a, i)=>
<div className="list-item">
<Link href={'/detail/' + result[i]._id}><h4>{a.title}</h4></Link>
<p>1월 1일</p>
</div>
)
}
</div>
)
}
Link 컴포넌트를 이용한 라우팅
'use client'
import {useRouter} from 'next/navigation'
export default function DetailLink(){
let router = useRouter()
return (
<button onClick={()=>{ router.push('/') }}>버튼</button>
)
}
useRouter() 이용한 라우팅 (client component 에서만 이용 가능)
<button onClick={()=>{ router.back() }}>버튼</button> // 뒤로가기 <button onClick={()=>{ router.forward() }}>버튼</button> // 앞으로가기 <button onClick={()=>{ router.refresh() }}>버튼</button> // 새로고침
client component에서 DB데이터 가져오기
'use client'
export default function ListItem(){
useEffect(()=>{
let result = (서버에 요청해서 DB데이터 가져오는 코드)
},[])
return (
<div>{result}</div>
)
}
페이지 로드시 유저가 텅 빈 html을 먼저 보게 되고 조금 시간이 지나야 html 내용이 채워진다는 겁니다. 검색엔진 봇들이 페이지를 수집하려고 방문하면 텅 빈 html을 발견하고 실망하고 돌아갈 수 있기 때문에 검색엔진 노출 측면에서 단점이 있을 수 있습니다.
<button onClick={()=>{ router.prefetch('/어쩌구') }}>버튼</button>
router.prefetch('/어쩌구') 실행하면 '/어쩌구'의 내용을 미리 로드해줍니다. 그럼 그 페이지 방문할 때 매우 빠르게 방문할 수 있습니다. 빠른 사이트를 만들고 싶을 때 쓸 수 있는 유용한 기능입니다.
<Link href={'/어쩌구'}>링크</Link>
<Link href={'/어쩌구'} prefetch={false}>링크</Link>
다행히 server component에서도 저런 기능을 사용할 수 있는데 <Link href={'/어쩌구'}> 쓰면 태그가 화면에 보이는 순간 '/어쩌구' 페이지를 자동으로 미리 로드해줍니다. 자동으로 미리 로드하는게 싫으면 prefetch 속성을 false로 바꿔줍시다.

서버를 만들어야합니다 : 유저가 잘못된 데이터를 DB에 넣을 수도 있고 민감한 정보를 출력할수도 있어서 대리로 DB 입출력을 할수있는 프로그램(서버)를 만들어야 합니다.
서버 개발은 비슷하게 생긴 기능이 많기 때문에 혼동을 피하기 위해 기능마다 URL과 method이름을 붙여서 구분해야합니다.
URL은 개발자가 원하는 방향으로 작성을 하고
mehtod는 GET(출력) POST(입력) PUT(수정) DELETE(삭제) 4개 중에 마음대로 골라서 작성하면 됩니다(form 작성).
서버 예시 ex.pages/api/test.js
export default function handler(req, res) {
if (req.method === 'GET') {
res.status(200).json({ name: '안녕' });
}
if (req.method === 'POST') {
res.status(200).json({ name: '바보' });
}
}
// req = > 요청 , res => 응답
ex. 수정 page.js
import { ObjectId } from "mongodb";
import { connectDB } from "@/util/database.js"
export default async function Edit(props) {
let db = (await connectDB).db('forum')
let result = await db.collection('post').findOne({_id : ObjectId(props.params.id)});
return (
<div className="write">
<form action="어쩌구" method="POST">
<input name="title" defaultValue={result.title} />
<input name="content" defaultValue={result.content} />
<button type="submit">전송</button>
</form>
</div>
)
}
글작성 폼을 이용하고 connectDB로 데이터를 받아서 value값에 넣어준다( netx.js = defaultValue )
ex. 수정 서버.js
import { connectDB } from "@/util/database.js"
import { ObjectId } from "mongodb";
export default async function handler(req, res) {
if (req.method == 'POST'){
let dataToUpdate = {title : req.body.title, content : req.body.content}
let db = (await connectDB).db('forum')
let result = await db.collection('post').updateOne(
{_id : new ObjectId(req.body._id)},
{ $set : dataToUpdate}
);
console.log(result)
res.redirect(302, '/list')
}
}
- 요청.body에서 이것저것 꺼내서 바꿀데이터를 만들어서 updateOne() 안에 집어넣었습니다.
- 요청.body._id 꺼내서 게시물정보를 만들어서 updateOne() 안에 집어넣었습니다.
- document 수정하려면 updateOne 쓰면 됩니다. 값 변경말고 증가/감소도 가능함
- 서버에서 코드짜다가 필요한 데이터가 없으면 1. 유저에게 보내라고 하거나 2. DB에서 꺼내써도 됩니다.
ex. 삭제 page.js
fetch('/api/post/delete', {method : 'DELETE', body : result[i]._id})
삭제할 글의 id까지 넣어 서버에 보낸다
ex. 삭제 서버.js
import { connectDB } from "@/util/database"
import { ObjectId } from "mongodb";
export default async function handler(요청, 응답) {
if (요청.method == 'DELETE'){
try {
let db = (await connectDB).db('forum')
let result = await db.collection('post').deleteOne({_id : new ObjectId(요청.body._id)});
}
catch (error) {
응답.status(500)
}
만약에 result 결과가 이상하면 응답.status(500)
result 결과가 정상이면 응답.status(200)
}
}
서버로 array, object 전송하고 싶으면
JSON.stringify( {name : 'kim'} ) // JSON.stringify() 안에 담으면 JSON으로 변환
JSON.parse( '{"name" : "kim"}' ) // JSON에 붙은 따옴표를 제거해서 array, objec로 변환