지금까지 배운 JavaScript를 이용하여 부동산 매물 등록/목록 페이지 구현하기
등록 페이지에서 작성한 내용은 DB에 저장
-> mongoDB로 연동 완료
-> 저장 후 목록 페이지에 바로 추가된 내용 보이도록 함
DB에서 내용을 가져와 목록에 보여주기
-> 저장된 내용 중 필요한 부분을 넘겨주어 보여주도록 함
저장된 내용 수정/삭제 기능 구현
-> 해당 db의 id로 연동하여 form 태그에 작성된 내용을 가져와 수정되도록 함
-> 버튼 클릭 시 해당 데이터의 id를 가져와 삭제되도록 함
목표했던 큰 틀 3가지는 모두 구현 완료
데이터의 유연성을 위하여 mongoDB 사용
_id
: 자동 생성됨title
: 글 제목content
: 글 내용wirter
: 작성자region
: 지역 (선택항목으로 지정함)server.js
const express = require('express');
const app = express();
const MongoClient = require('mongodb').MongoClient;
const ObjectId = require('mongodb').ObjectId;
const bodyParser = require('body-parser');
const path = require('path');
let db;
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'ejs');
// mongoDB 연결
const mongoURI = '<<url>>' ;
MongoClient.connect(mongoURI)
.then(client => {
console.log('Connected to Database');
db = client.db('<<db이름>>');
// server 8080포트로 열기
app.listen(8080, () => {
console.log('Server running on port 8080');
});
})
.catch(err => {
console.log(err)
});
// 목록 페이지 라우터
app.get('/list', (req, res) => {
// DB를 Array형식으로(posts) data로 넘겨 list.ejs에서 data 변수로 사용 가능
db.collection('post').find().toArray()
.then(posts => {
res.render('list.ejs', { data: posts });
})
.catch(error => console.error(error));
});
// 추가 페이지 라우터
app.get('/enter', (req, res) => {
res.render('enter'); // .ejs 생략 가능
});
// id별로 수정 페이지 라우터
app.get('/edit/:id', (req, res) => {
const postId = new ObjectId(req.params.id);
// db에서 _id를 objectid로 생성하여 해당 내용을 edit.ejs로 넘김
db.collection('post').findOne({ _id: postId })
.then(post => {
res.render('edit', { data: post });
})
.catch(error => console.error(error));
});
// 수정 페이지 라우터(db에 수정내용 업데이트)
app.post('/edit', (req, res) => {
const postId = new ObjectId(req.body.id);
const updatedPost = {
title: req.body.title,
content: req.body.content,
date: req.body.someDate,
writer: req.body.writer,
region: req.body.region
};
db.collection('post').updateOne({ _id: postId }, { $set: updatedPost })
.then(result => {
console.log('Post updated successfully');
res.redirect('/list');
})
.catch(error => console.error(error));
});
// 삭제
app.post('/delete', (req, res) => {
const postId = new ObjectId(req.body._id);
db.collection('post').deleteOne({ _id: postId })
.then(result => {
console.log('Post deleted successfully');
res.status(200).send();
})
.catch(error => {
console.error('Error deleting post:', error);
res.status(500).send();
});
});
// db에 내용 추가
app.post('/save', (req, res) => {
const newPost = {
title: req.body.title,
content: req.body.content,
date: req.body.date,
writer: req.body.writer,
region: req.body.region
};
db.collection('post').insertOne(newPost)
.then(result => {
console.log('New post added successfully');
res.redirect('/list'); // 목록 페이지가 보이도록 함
})
.catch(error => console.error(error));
});
list.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Post List</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
.card-body {
padding: 1.5rem;
background-color: #f8f9fa;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
transition: box-shadow 0.3s ease;
}
.card-body:hover {
box-shadow: 0 0 20px rgba(0,0,0,0.2);
}
.btn-edit {
margin-right: 0.5rem;
}
.sidebar {
background-color: #f0f0f0;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.sidebar h4 {
margin-bottom: 20px;
}
.sidebar .btn-write {
width: 100%;
margin-bottom: 10px;
}
</style>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="#">Post List</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/enter">Write</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<div class="row">
<div class="col-md-3">
<div class="sidebar">
<h4>Options</h4>
<!-- Write Button -->
<a href="/enter" class="btn btn-primary btn-write">Write</a>
</div>
</div>
<div class="col-md-9">
<h2 class="mb-4">Post List</h2>
<div class="row row-cols-1 row-cols-md-2 g-4">
<% data.forEach(post => { %>
<div class="col">
<div class="region-background region-<%= post.region %>">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title"><%= post.title %></h5>
<p class="card-text"><%= post.content %></p>
<p class="card-text"><strong>Region:</strong> <%= post.region %></p>
<p class="card-text"><strong>Writer:</strong> <%= post.writer %></p>
<p class="card-text"><strong>Date:</strong> <%= post.date %></p>
<a href="/edit/<%= post._id %>" class="btn btn-warning btn-edit">Edit</a>
<button class="btn btn-danger btn-delete" data-id="<%= post._id %>">Delete</button>
</div>
</div>
</div>
</div>
<% }); %>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
$('.btn-delete').click(function(e) {
e.preventDefault();
var postId = $(this).attr('data-id');
if (confirm('Are you sure you want to delete this post?')) {
$.ajax({
type: 'POST',
url: '/delete',
data: { _id: postId },
success: function(response) {
alert('Post deleted successfully');
window.location.href = '/list';
},
error: function(xhr, textStatus, errorThrown) {
console.log('Error deleting post:', textStatus);
}
});
}
});
});
</script>
</body>
</html>
enter.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Post</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
.form-container {
max-width: 600px;
margin: auto;
background-color: #f8f9fa;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
transition: box-shadow 0.3s ease;
}
.form-container:hover {
box-shadow: 0 0 20px rgba(0,0,0,0.2);
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/list">Post List</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/enter">Write</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4 form-container">
<h2 class="mb-4">Edit Post</h2>
<form action="/edit" method="POST">
<input type="hidden" name="id" value="<%= data._id %>">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" value="<%= data.title %>" required>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" id="content" name="content" rows="5" required><%= data.content %></textarea>
</div>
<div class="mb-3">
<label for="someDate" class="form-label">Date</label>
<input type="date" class="form-control" id="someDate" name="someDate" value="<%= data.date %>" required>
</div>
<div class="mb-3">
<label for="region" class="form-label">Region</label>
<input type="text" class="form-control" id="region" name="region" value="<%= data.region %>" required>
</div>
<div class="mb-3">
<label for="writer" class="form-label">Writer</label>
<input type="text" class="form-control" id="writer" name="writer" value="<%= data.writer %>" required>
</div>
<button type="submit" class="btn btn-warning">Update</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
edit.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Post</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
.form-container {
max-width: 600px;
margin: auto;
background-color: #f8f9fa;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
transition: box-shadow 0.3s ease;
}
.form-container:hover {
box-shadow: 0 0 20px rgba(0,0,0,0.2);
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/list">Post List</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/enter">Write</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4 form-container">
<h2 class="mb-4">Edit Post</h2>
<form action="/edit" method="POST">
<input type="hidden" name="id" value="<%= data._id %>">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" value="<%= data.title %>" required>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" id="content" name="content" rows="5" required><%= data.content %></textarea>
</div>
<div class="mb-3">
<label for="someDate" class="form-label">Date</label>
<input type="date" class="form-control" id="someDate" name="someDate" value="<%= data.date %>" required>
</div>
<div class="mb-3">
<label for="region" class="form-label">Region</label>
<input type="text" class="form-control" id="region" name="region" value="<%= data.region %>" required>
</div>
<div class="mb-3">
<label for="writer" class="form-label">Writer</label>
<input type="text" class="form-control" id="writer" name="writer" value="<%= data.writer %>" required>
</div>
<button type="submit" class="btn btn-warning">Update</button>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
몽고DB, 노드JS, 서버와의 연동이 이렇게 부드럽게 잘 연결됨에 놀라웠다. 거기에 덧붙혀 오류의 많은 부분을 챗GPT의 도움을 받으면서 챗GPT의 Assistant 능력에 또한 놀라게 되었다.)
이번 기회에 처음부터 설계, 구현해보면서 지금까지 배운 내용을 어떤 부분에 어떻게 적용할지 고민하면서 내것으로 만드는 시간이 되었던것 같습니다.