Mini Project(부동산 매물 등록)

이지우·2024년 6월 23일
0

멋사

목록 보기
10/16

소개

목표

지금까지 배운 JavaScript를 이용하여 부동산 매물 등록/목록 페이지 구현하기

  1. 등록 페이지에서 작성한 내용은 DB에 저장
  2. DB에서 내용을 가져와 목록에 보여주기
  3. 저장된 내용 수정/삭제 기능 구현

  • 김현호
  • 이지우

구현 결과

  1. 등록 페이지에서 작성한 내용은 DB에 저장
    -> mongoDB로 연동 완료
    -> 저장 후 목록 페이지에 바로 추가된 내용 보이도록 함

  2. DB에서 내용을 가져와 목록에 보여주기
    -> 저장된 내용 중 필요한 부분을 넘겨주어 보여주도록 함

  3. 저장된 내용 수정/삭제 기능 구현
    -> 해당 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)

삭제 시 alert


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)

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:_id)

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 능력에 또한 놀라게 되었다.)
  • 이지우
이번 기회에 처음부터 설계, 구현해보면서 지금까지 배운 내용을 어떤 부분에 어떻게 적용할지 고민하면서 내것으로 만드는 시간이 되었던것 같습니다.
profile
노력형 인간

0개의 댓글