핵심 개념: 유저가 DB와 직접 통신하면 위험하므로 서버를 거쳐야 함
동작 순서:
1. 유저가 글작성페이지에서 글을 작성해서 서버로 전송
2. 서버는 글을 받아서 검증
3. 서버가 DB에 저장
<form class="form-box" action="/add" method="POST">
<h4>글쓰기</h4>
<input name="title">
<input name="content">
<button type="submit">전송</button>
</form>
핵심 포인트:
<input>에 name 속성 필수 (서버로 데이터 전송용)action="/add": 전송할 URLmethod="POST": 전송 방식app.use(express.json())
app.use(express.urlencoded({extended:true}))
const { ObjectId } = require('mongodb')
app.post('/add', async (요청, 응답) => {
if (요청.body.title == '') {
응답.send('제목안적었는데')
} else {
try {
await db.collection('post').insertOne({
title : 요청.body.title,
content : 요청.body.content
})
응답.redirect('/list')
} catch (e) {
console.log(e)
응답.send('DB에러남')
}
}
})
핵심 포인트:
요청.body: 유저가 폼으로 보낸 데이터insertOne(): DB에 새 document 생성try-catch: 에러 처리if문: 유효성 검사app.get('/detail/:id', async (요청, 응답) => {
try {
let result = await db.collection('post').findOne({
_id : new ObjectId(요청.params.id)
})
if (result == null) {
응답.status(400).send('그런 글 없음')
} else {
응답.render('detail.ejs', { result : result })
}
} catch (){
응답.send('이상한거 넣지마라')
}
})
<!-- list.ejs -->
<a href="/detail/<%= 글목록[i]._id %>">
<%= 글목록[i].title %>
</a>
핵심 포인트:
:id: URL 파라미터 문법요청.params.id: URL에서 입력받은 값findOne(): 특정 document 1개 찾기<form class="form-box" action="/edit" method="POST">
<h4>수정하기</h4>
<input name="id" value="<%= result._id %>" style="display:none">
<input name="title" value="<%= result.title %>">
<input name="content" value="<%= result.content %>">
<button type="submit">전송</button>
</form>
app.get('/edit/:id', async (요청, 응답) => {
let result = await db.collection('post').findOne({
_id : new ObjectId(요청.params.id)
})
응답.render('edit.ejs', {result : result})
})
app.post('/edit', async (요청, 응답)=>{
await db.collection('post').updateOne(
{ _id : new ObjectId(요청.body.id) },
{ $set : {
title : 요청.body.title,
content : 요청.body.content
}
}
)
응답.redirect('/list')
})
핵심 포인트:
value 속성: input에 기본값 설정updateOne(): document 수정$set: 특정 필드만 업데이트| 기능 | 문법 | 설명 |
|---|---|---|
| 데이터 저장 | insertOne({}) | 새 document 생성 |
| 데이터 조회 | findOne({}) | document 1개 찾기 |
| 데이터 수정 | updateOne({}, {$set:{}}) | document 수정 |
| URL 파라미터 | app.get('/url/:변수') | 동적 URL 처리 |
| 폼 데이터 | 요청.body | POST로 받은 데이터 |
| URL 파라미터 값 | 요청.params | URL에서 받은 값 |
핵심: 직접 악성 유저가 되어서 테스트해보고 모든 예외상황에 대비하기
npm install method-override
// server.js 상단에 추가
const methodOverride = require('method-override')
app.use(methodOverride('_method'))
<form action="/edit?_method=PUT" method="POST">
<!-- method="POST"는 그대로 유지 -->
</form>
// 서버에서 PUT 요청 받기
app.put('/edit', (요청, 응답) => {
// PUT 요청 처리 로직
})
// 덮어쓰기
{ $set: { like: 1 } }
// 숫자 증감
{ $inc: { like: 1 } } // +1
{ $inc: { like: -1 } } // -1
// 곱셈
{ $mul: { like: 2 } }
// 필드 삭제
{ $unset: { like: "" } }
// 조건에 맞는 모든 document 수정
db.collection('post').updateMany(
{ title: '멍청아' },
{ $set: { title: '착한친구야' } }
)
// 조건부 수정 (숫자 비교)
db.collection('post').updateMany(
{ like: { $gt: 5 } }, // like > 5
{ $set: { like: 100 } }
)
| 연산자 | 의미 | 예시 |
|---|---|---|
$gt | 초과 (>) | { like: { $gt: 5 } } |
$gte | 이상 (>=) | { like: { $gte: 5 } } |
$lt | 미만 (<) | { like: { $lt: 10 } } |
$lte | 이하 (<=) | { like: { $lte: 10 } } |
$ne | 같지 않음 (!=) | { like: { $ne: 0 } } |
document.querySelectorAll('.delete')[0].addEventListener('click', function(){
fetch('/delete?docid=' + '<%= 글목록[0]._id %>', {
method: 'DELETE',
})
})
app.delete('/delete', async (요청, 응답) => {
let result = await db.collection('post').deleteOne({
_id: new ObjectId(요청.query.docid)
})
응답.send('삭제완료')
})
주의: AJAX 요청시 응답.redirect(), 응답.render() 사용 금지
<!-- list.ejs -->
<span class="delete" data-id="<%= 글목록[i]._id %>">🗑️</span>
document.querySelectorAll('.delete')[0].addEventListener('click', function(e){
fetch('/delete?docid=' + e.target.dataset.id, {
method: 'DELETE',
})
})
장점: 글 순서가 바뀌어도 정확한 삭제 가능
// JSON 데이터 받기
fetch('/URL').then((r) => r.json())
.then((result) => { console.log(result) })
// 텍스트 데이터 받기
fetch('/URL').then((r) => r.text())
.then((result) => { console.log(result) })
fetch('/URL')
.then((r) => {
if(r.status == 200) {
return r.json()
} else {
// 서버 에러코드 전송시 실행할 코드
}
})
.then((result) => {
// 성공시 실행할 코드
}).catch((error) => {
// 인터넷 문제 등으로 실패시 실행할 코드
console.log(error)
})
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
// 간단한 GET 요청
axios.get('/URL').then((r) => { console.log(r) })
.catch(() => { /* 에러 처리 */ })
document.querySelectorAll('.delete')[0].addEventListener('click', function(e){
fetch('/delete?docid=' + e.target.dataset.id, {
method: 'DELETE',
})
.then((r) => r.text())
.then((r) => {
// 삭제 완료 후 해당 글 숨기기
e.target.parentElement.parentElement.style.display = 'none'
})
})
$set, $inc, $gt 등으로 다양한 수정 가능