게시판을 만들려면 유저들이 작성한 게시물을 영구적으로 보관할 곳이 필요합니다. 메모장이나 엑셀로도 가능하지만, 실제 서비스에서는 데이터베이스를 사용합니다.
데이터베이스의 장점:
사용자 테이블
+----+--------+-----+
| ID | 이름 | 나이|
+----+--------+-----+
| 1 | 김철수 | 25 |
| 2 | 이영희 | 30 |
+----+--------+-----+
주문 테이블
+----+---------+--------+
| ID | 사용자ID| 상품 |
+----+---------+--------+
| 1 | 1 | 노트북 |
| 2 | 2 | 마우스 |
+----+---------+--------+
특징:
대표 DBMS:
// MongoDB 문서 예시
{
_id: ObjectId("..."),
name: "김철수",
age: 25,
orders: [
{ product: "노트북", date: "2024-01-01" },
{ product: "마우스", date: "2024-01-02" }
]
}
특징:
대표 NoSQL:
Database (forum)
└── Collection (post)
├── Document { title: "첫 글", content: "내용1" }
├── Document { title: "두번째 글", content: "내용2" }
└── Document { title: "세번째 글", content: "내용3" }
용어 정리:
✅ 무료 티어 선택 (M0 Sandbox)
✅ 서버 위치: Seoul (ap-northeast-2)
✅ 클러스터 이름: 기본값 사용
Database Access 메뉴:
admin / password123⚠️ 주의: 이 계정은 몽고DB 로그인 계정과 다름
Network Access 메뉴:
0.0.0.0/0 직접 입력Database 이름: forum
Collection 이름: post
Browse Collections → Create Database에서 생성
npm install mongodb@5
Connect 버튼 → Drivers 선택:
mongodb+srv://DB접속아이디:DB접속비번@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority
const { MongoClient } = require('mongodb')
let db
const url = 'mongodb+srv://admin:password123@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority'
new MongoClient(url).connect().then((client) => {
console.log('DB연결성공')
db = client.db('forum')
// DB 연결 후 서버 시작
app.listen(8080, () => {
console.log('http://localhost:8080 에서 서버 실행중')
})
}).catch((err) => {
console.log('DB연결실패:', err)
})
Browse Collections → Insert Document:
// 첫 번째 문서
{
title: "첫 게시물",
content: "안녕하세요. 첫 번째 글입니다."
}
// 두 번째 문서
{
title: "두번째 글임",
content: "MongoDB 좋네요"
}
app.get('/insert', (요청, 응답) => {
db.collection('post').insertOne({
title: '서버에서 저장한 글',
content: '내용입니다'
})
응답.send('저장완료')
})
{
_id: ObjectId("507f1f77bcf86cd799439011"),
title: "게시물 제목",
content: "게시물 내용"
}
ObjectId 특징:
// ❌ 나쁜 예: 하나의 문서에 모든 게시물
{
posts: [
{ title: "글1", content: "내용1" },
{ title: "글2", content: "내용2" },
// ... 100만 개의 글
]
}
// ✅ 좋은 예: 각각 별도 문서
{ title: "글1", content: "내용1" }
{ title: "글2", content: "내용2" }
{ title: "글3", content: "내용3" }
이유: MongoDB는 문서 찾기는 빠르지만, 문서 내 특정 데이터 찾기는 느림
app.get('/list', async (요청, 응답) => {
let result = await db.collection('post').find().toArray()
응답.send(result)
})
let 과일들 = ['사과', '바나나', '오렌지']
console.log(과일들[0]) // '사과'
console.log(과일들[1]) // '바나나'
console.log(과일들[2]) // '오렌지'
let 사람 = {
name: '김철수',
age: 25,
city: '서울'
}
console.log(사람.name) // '김철수'
console.log(사람['age']) // 25
// DB에서 가져온 데이터 형태
[
{
_id: ObjectId("..."),
title: "첫 게시물",
content: "내용1"
},
{
_id: ObjectId("..."),
title: "두번째 글",
content: "내용2"
}
]
app.get('/list', async (요청, 응답) => {
let result = await db.collection('post').find().toArray()
// 첫 번째 글 제목
console.log(result[0].title)
// 두 번째 글 내용
console.log(result[1].content)
// 모든 글 제목
result.forEach(글 => {
console.log(글.title)
})
응답.send(result[0].title)
})
// 동기적 실행 (순서대로)
console.log('1번')
console.log('2번')
console.log('3번')
// 비동기적 실행
console.log('1번')
db.collection('post').find().toArray() // 시간이 걸림
console.log('3번') // DB 작업 완료 전에 실행됨
// ❌ 문제가 있는 코드
app.get('/list', (요청, 응답) => {
let result = db.collection('post').find().toArray()
console.log(result) // Promise 객체 출력
응답.send(result[0].title) // 에러 발생
})
// ✅ 올바른 코드
app.get('/list', async (요청, 응답) => {
let result = await db.collection('post').find().toArray()
console.log(result) // 실제 데이터 출력
응답.send(result[0].title) // 정상 작동
})
규칙:
await를 사용하려면 함수에 async 키워드 필요await는 비동기 작업이 완료될 때까지 기다림app.get('/api/posts', async (요청, 응답) => {
try {
let posts = await db.collection('post').find().toArray()
응답.json({
success: true,
data: posts,
count: posts.length
})
} catch (error) {
응답.status(500).json({
success: false,
message: '데이터 조회 실패',
error: error.message
})
}
})
app.get('/api/posts/:id', async (요청, 응답) => {
try {
let { ObjectId } = require('mongodb')
let post = await db.collection('post').findOne({
_id: new ObjectId(요청.params.id)
})
if (post) {
응답.json({ success: true, data: post })
} else {
응답.status(404).json({ success: false, message: '게시물을 찾을 수 없습니다' })
}
} catch (error) {
응답.status(500).json({ success: false, error: error.message })
}
})
app.post('/api/posts', async (요청, 응답) => {
try {
let newPost = {
title: 요청.body.title,
content: 요청.body.content,
author: 요청.body.author,
createdAt: new Date()
}
let result = await db.collection('post').insertOne(newPost)
응답.json({
success: true,
message: '게시물 저장 완료',
insertedId: result.insertedId
})
} catch (error) {
응답.status(500).json({ success: false, error: error.message })
}
})
// 특정 제목으로 검색
let result = await db.collection('post').find({ title: '첫 게시물' }).toArray()
// 작성자로 검색
let result = await db.collection('post').find({ author: '김철수' }).toArray()
// 여러 조건
let result = await db.collection('post').find({
author: '김철수',
title: /게시물/
}).toArray()
// 최신순 정렬
let result = await db.collection('post')
.find()
.sort({ _id: -1 })
.toArray()
// 개수 제한
let result = await db.collection('post')
.find()
.limit(5)
.toArray()
// 건너뛰기 (페이징)
let result = await db.collection('post')
.find()
.skip(10)
.limit(5)
.toArray()
db.collection('컬렉션명').find().toArray()배열[인덱스]객체.속성명 또는 객체['속성명']async/await 사용 필수