[Node.js-03] CRUD, Ajax

Comely·2025년 3월 12일

Node.js

목록 보기
3/14

글작성 기능 설계

핵심 개념: 유저가 DB와 직접 통신하면 위험하므로 서버를 거쳐야 함

동작 순서:
1. 유저가 글작성페이지에서 글을 작성해서 서버로 전송
2. 서버는 글을 받아서 검증
3. 서버가 DB에 저장


1. 글작성 페이지 만들기

write.ejs 폼 구조

<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": 전송할 URL
  • method="POST": 전송 방식

2. 서버에서 글 받기 및 저장

필수 설정

app.use(express.json())
app.use(express.urlencoded({extended:true}))
const { ObjectId } = require('mongodb')

글 저장 API

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문: 유효성 검사

3. 상세페이지 구현

URL 파라미터 활용

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개 찾기
  • 예외처리: null 체크, try-catch

4. 글 수정 기능

수정 페이지 (edit.ejs)

<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})
})

수정 처리 API

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: 특정 필드만 업데이트
  • 숨겨진 input으로 글 ID 전송

주요 문법 정리

기능문법설명
데이터 저장insertOne({})새 document 생성
데이터 조회findOne({})document 1개 찾기
데이터 수정updateOne({}, {$set:{}})document 수정
URL 파라미터app.get('/url/:변수')동적 URL 처리
폼 데이터요청.bodyPOST로 받은 데이터
URL 파라미터 값요청.paramsURL에서 받은 값

예외처리 체크리스트

  • 빈 값 입력 검증
  • 잘못된 ID 형식 처리
  • 존재하지 않는 글 접근
  • DB 연결 에러 처리
  • 과도하게 긴 내용 제한

핵심: 직접 악성 유저가 되어서 테스트해보고 모든 예외상황에 대비하기

Method Override와 AJAX 삭제 기능

Method Override로 PUT, DELETE 요청하기

1. 라이브러리 설치 및 설정

npm install method-override
// server.js 상단에 추가
const methodOverride = require('method-override')
app.use(methodOverride('_method'))

2. 폼에서 PUT/DELETE 요청 보내기

<form action="/edit?_method=PUT" method="POST">
  <!-- method="POST"는 그대로 유지 -->
</form>
// 서버에서 PUT 요청 받기
app.put('/edit', (요청, 응답) => {
  // PUT 요청 처리 로직
})

UpdateOne 추가 문법

기본 수정 연산자들

// 덮어쓰기
{ $set: { like: 1 } }

// 숫자 증감
{ $inc: { like: 1 } }    // +1
{ $inc: { like: -1 } }   // -1

// 곱셈
{ $mul: { like: 2 } }

// 필드 삭제
{ $unset: { like: "" } }

여러 document 동시 수정

// 조건에 맞는 모든 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 } }

AJAX 삭제 기능 구현

1. 첫 번째 글 삭제 기능

document.querySelectorAll('.delete')[0].addEventListener('click', function(){
  fetch('/delete?docid=' + '<%= 글목록[0]._id %>', {
    method: 'DELETE',
  })
})

2. 서버에서 삭제 처리

app.delete('/delete', async (요청, 응답) => {
  let result = await db.collection('post').deleteOne({ 
    _id: new ObjectId(요청.query.docid) 
  })
  응답.send('삭제완료')
})

주의: AJAX 요청시 응답.redirect(), 응답.render() 사용 금지


Dataset을 활용한 개선된 삭제 기능

HTML에 데이터 숨기기

<!-- 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',
  })
})

장점: 글 순서가 바뀌어도 정확한 삭제 가능


AJAX 고급 활용

서버 응답 데이터 처리

// 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)
})

Axios 라이브러리 사용

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
// 간단한 GET 요청
axios.get('/URL').then((r) => { console.log(r) })
.catch(() => { /* 에러 처리 */ })

UI 개선: 삭제 후 애니메이션

삭제 완료 후 HTML 숨기기

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'
  })
})

렌더링 방식 비교

서버사이드 렌더링 (SSR)

  • 서버에서 완성된 HTML 생성해서 전송
  • 새로고침 발생
  • SEO 친화적

클라이언트사이드 렌더링 (CSR)

  • 서버에서 데이터만 받아서 프론트엔드에서 HTML 생성
  • 새로고침 없이 부드러운 UI
  • React 같은 라이브러리 활용

핵심 정리

  1. Method Override: 폼에서도 PUT, DELETE 요청 가능
  2. UpdateOne 연산자: $set, $inc, $gt 등으로 다양한 수정 가능
  3. AJAX: 새로고침 없이 서버 통신
  4. Dataset: HTML에 데이터 숨겨서 동적 처리
  5. 에러 처리: 항상 실패 케이스 고려
  6. 점진적 개발: 하나의 기능 완성 후 확장
profile
App, Web Developer

0개의 댓글