Lsiron
MySQL로 게시판 CRUD 구현해보기. (Node.js, Express) 본문
mysql와 mysql workbench 설치, Node.js와 Express 기본 설정이 되어있다는 가정하에 MySQL로 게시판 CRUD 짜는 방법을 알아보자.
1. 패키지 설치
터미널에 아래와 같이 명령어를 입력하여, mysql2를 설치해주자.
$ npm install express mysql2
2. MySQL 데이터베이스 설정
MySQL 데이터베이스에 아래와 같이 테이블을 생성해보자.
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
위 코드를 Mysql workbench에 입력해보자.
workbench에서 테이블이 만들어 진 것을 확인 할 수 있다.
이제 간단히 CRUD를 구현할 app.js 파일과 html 요소를 입력해줄 index.ejs, edit.ejs 파일을 만들어주자.
( 단, ejs파일은 views 폴더 안에 만들어줄 것. - edit.ejs는 게시글을 수정 하기위한 용도로 쓸 예정. )
먼저 app.js로 가서 mysql 라이브러리를 불러와 주고 mysql에 연결해보자. ( express는 미리 설치한 것으로 가정 )
const express = require('express');
// mysql 모듈 불러오기
const mysql = require('mysql2');
const app = express();
// MySQL 연결 설정
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '비밀번호입니다',
database: 'firstdabase'
});
// 데이터베이스 연결
db.connect((err) => {
if (err) {
throw err;
}
console.log('MySQL Connected...');
});
app.listen(3000, () => {
console.log(`Server running on port 3000`);
});
편의상 db 변수에 담아주도록 하겠다. ( 기본코드는 connection 변수에 담음 )
당연히 코드는 https://www.npmjs.com/package/mysql 이 사이트에서 기본코드를 긁어와서 내 입맛대로 바꿔주었다.
ejs 템플릿엔진을 사용할 수 있도록 아래 명령어를 입력하여 모듈을 다운받아주자.
$ npm i ejs
수정과 삭제 시, form태그에서 method를 override 시킬 수 있도록 터미널에 아래와 같이 명령어를 입력하여 모듈을 다운받아주자.
$ npm i method-override
이제 각 모듈을 불러오기 위한 코드와, 클라이언트에서 보낸 body값을 서버에서 받기 위해 각종 미들웨어를 불러와주자.
const express = require('express');
const mysql = require('mysql2');
const app = express();
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '비밀번호입니다',
database: 'firstdabase'
});
db.connect((err) => {
if (err) {
throw err;
}
console.log('MySQL Connected...');
});
// body값 받아오기 위한 미들웨어
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// methodOverride
app.use(methodOverride('_method'));
// View engine 설정
app.set('view engine', 'ejs');
app.listen(3000, () => {
console.log(`Server running on port 3000`);
});
기본 셋팅이 모두 완료된다. 코드를 한번 입력해보자.
먼저 CRUD중에, C를 구현해보자.
index.ejs로 가서 posts 경로로 post 요청을 보내도록 폼 태그를 하나 만들어주자.
내용물은 테이블을 만들 때 넣었던 title과 content 그리고 submit 버튼이 있으면 되겠다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Example</title>
</head>
<body>
<h1>CRUD Example</h1>
<form action="/posts" method="post">
<h2>Create Post</h2>
<input type="text" name="title" placeholder="Title">
<textarea name="content" placeholder="Content"></textarea>
<button type="submit">Create Post</button>
</form>
</body>
</html>
이제 클라이언트를 구성 해 주었으니 클라이언트에서 보내는 정보를 받을 서버에서 코드를 구현해주자.
다시 app.js로 가보자.
// 게시글 생성
app.post('/posts', (req, res) => {
const { title, content } = req.body;
const sql = 'INSERT INTO posts (title, content) VALUES (?, ?)';
db.query(sql, [title, content], (err, result) => {
if (err) {
return res.status(500).send('Server error');
}
res.redirect('/');
});
});
/posts 경로를 통해 post 요청을 받았고, title과 content를 name 속성을 통해 body값으로 받아 주었다.
받은 body값을 변수에 넣어주고, posts 테이블의 title과 content 속성값으로 '?' 파라미터를 넣어 준 뒤 INSERT 쿼리문을 설정해주자.
그리고 이 INSERT 쿼리문을 sql 변수에 넣어주자.
속성 값을 '?' 로 하는 이유는 보안을 위해서다. title과 content가 들어 갈 파라미터라고 생각하면 된다.
sql 변수를 db.query 쿼리문에 담아서 db에 반영 시켜준다.
(db.query문 안의 배열에 담긴 title과 content는 sql 변수에 넣어 주었던 '?' 파라미터에 들어 감)
db.query 코드 또한 외운게 아니라, https://www.npmjs.com/package/mysql
사이트에서 긁어온 것이다.
데이터가 잘 생성되는 것을 알 수 있다.
이제 CRUD중, R을 구현 해 보자.
app.get('/', (req, res) => {
const sql = 'SELECT * FROM posts';
db.query(sql, (err, results) => {
if (err) {
return res.status(500).send('Server error');
}
res.render('index.ejs', { posts: results });
});
});
전체 게시판을 조회할 수 있는 코드를 만들었다.
posts 테이블의 정보를 모두 불어올 수 있도록 SELECT 쿼리문을 설정 해 주자.
db.query 쿼리문에 담아서 db에 반영 시켜준뒤, 결과값을 posts 변수에 담아 index.ejs로 보내주었다.
index.ejs로 가서 태그를 만들어주자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Example</title>
</head>
<body>
<h1>CRUD Example</h1>
<form action="/posts" method="post">
<h2>Create Post</h2>
<input type="text" name="title" placeholder="Title">
<textarea name="content" placeholder="Content"></textarea>
<button type="submit">Create Post</button>
</form>
<h2>Posts</h2>
<% posts.forEach(function(post) { %>
<div class="post">
<h3><%= post.title %></h3>
<p><%= post.content %></p>
서버로부터 받은 posts 변수를 이용하여, db에 있는 게시글을 조회할 수 있도록 하였다.
posts 변수를 사용하여 forEach 메서드로 게시글을 각각 조회 할 수 있도록 구현 하였다. 작동은 아주 잘 된다.
이제 CRUD 중, U를 구현 해 보자.
게시글 수정은 단일 게시글 사이트로 들어가서 수정할 수 있도록 구현 해 보겠다.
먼저 단일 게시글을 조회 할 수 있도록 app.js 서버로 가서 조회 코드를 만들어주자.
경로는 posts에서 게시글의 고유 id를 통해 edit 정적경로 로 이동해 줄 수 있도록 코드를 구현 해 보겠다.
또한 후에, 게시글 고유 id 링크로 요청을 보낼 수 있도록 result 값을 게시판 전체 조회를 했을때와 같이 post변수에 담아 edit.ejs로 넘겨주겠다.
app.get('/posts/:id/edit', (req, res) => {
const { id } = req.params;
const sql = 'SELECT * FROM posts WHERE id = ?';
db.query(sql, [id], (err, result) => {
if (err) {
return res.status(500).send('Server error');
}
if (result.length === 0) {
return res.status(404).send('Post not found');
}
res.render('edit.ejs', { post: result[0] });
});
});
posts 테이블의 id 속성만 조회하는 조건을 담은 SELECT 쿼리 문을 sql 변수에 담았다.
그리고 sql 변수를 db.query 쿼리문으로 db에 반영시키도록 해 주었다.
왜 여기서 result[0] 으로 post 변수를 넘겨주었냐면,
result를 console.log() 로 찍어보면 위와 같이 기본적으로 배열 안에 담긴다. 그냥 result로만 넘기면 에러가 발생한다.
참고로 전체 게시판을 조회할 때는 아래와 같이 배열에 데이터가 모두 담긴다.
때문에 forEach 메서드를 사용하여 데이터를 조회할 수 있도록 했던 것이다.
index.ejs로 가서 edit 페이지로 이동 할 수 있도록 버튼을 하나 만들어주자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Example</title>
</head>
<body>
<h1>CRUD Example</h1>
<form action="/posts" method="post">
<h2>Create Post</h2>
<input type="text" name="title" placeholder="Title">
<textarea name="content" placeholder="Content"></textarea>
<button type="submit">Create Post</button>
</form>
<h2>Posts</h2>
<% posts.forEach(function(post) { %>
<div class="post">
<h3><%= post.title %></h3>
<p><%= post.content %></p>
<form action="/posts/<%= post.id %>/edit" method="get">
<button type="submit">Edit</button>
</form>
<% }); %>
</div>
</body>
</html>
고유 아이디로 들어갈 수 있도록 해야하기 때문에 edit 버튼을 위한 form태그의 경로를 만들 때 미리 조회를 구현할 때 만들어진 forEach 메서드를 이용했다.
이제 edit.ejs로 가서 게시글을 수정할 수 있는 태그들을 만들어보자.
text와 content를 수정 할 수 있도록 하고, 다시 전체 게시판으로 돌아가는 버튼도 구현 해 주겠다.
<!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>
</head>
<body>
<h1>Edit Post</h1>
<form action="/posts/<%= post.id %>?_method=PUT" method="post">
<input type="text" name="title">
<textarea name="content"></textarea>
<button type="submit">Update Post</button>
</form>
<form action="/" method="get">
<button type="submit">Back to Posts</button>
</form>
</body>
</html>
다시 app.js 서버로 돌아와서 수정을 하기위한 API를 만들어주자.
id값을 params로 받아오고 title과 content를 name 속성을 통해 body값으로 받아오자.
id는 WHERE 조건에 사용 할 것이고, title과 content는 UPDATE 쿼리문에서 수정하기 위한 SET 속성값 에 집어넣도록 하겠다.
이후 db.query 문을 통해 db에 반영만 해 주면 수정완료이다.
app.put('/posts/:id', (req, res) => {
const { id } = req.params;
const { title, content } = req.body;
const sql = 'UPDATE posts SET title = ?, content = ? WHERE id = ?';
db.query(sql, [title, content, id], (err, result) => {
res.redirect('/');
});
});
UPDATE 쿼리문을 sql 변수에 담았고 db.query 문을 통해 db에 반영 시켰다.
1234로 기입했던 게시글이 수정이 잘 되는것을 확인 할 수 있다.
마지막으로 CRUD 중, D를 구현 해 보겠다.
삭제는 전체 페이지에서 할 수 있도록 구현을 해 보겠다.
app.delete('/posts/:id', (req, res) => {
const { id } = req.params;
const sql = 'DELETE FROM posts WHERE id = ?';
db.query(sql, [id], (err, result) => {
res.redirect('/');
});
});
id를 params로 받아오고, 해당 id 속성을 삭제한 DELETE 쿼리문을 sql 변수에 저장한 후, db.query문을 통해 db에 반영 시켜 주었다.
이제 index.js로 가서 delete 버튼을 눌렀을때 delete 요청을 보내도록 해주자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Example</title>
</head>
<body>
<h1>CRUD Example</h1>
<form action="/posts" method="post">
<h2>Create Post</h2>
<input type="text" name="title" placeholder="Title">
<textarea name="content" placeholder="Content"></textarea>
<button type="submit">Create Post</button>
</form>
<h2>Posts</h2>
<% posts.forEach(function(post) { %>
<div class="post">
<h3><%= post.title %></h3>
<p><%= post.content %></p>
<form action="/posts/<%= post.id %>?_method=DELETE" method="post">
<button type="submit">Delete</button>
</form>
<form action="/posts/<%= post.id %>/edit" method="get">
<button type="submit">Edit</button>
</form>
<% }); %>
</div>
</body>
</html>
이제 버튼을 누르면 삭제가 될 것이다.
123으로 수정했던 게시글이 삭제버튼을 누르자, 삭제가 되고, db 상에서도 삭제가 된 것을 확인 할 수있다.
예외처리를 마친 app.js 서버의 전체 코드는 아래와 같다.
const express = require('express');
const mysql = require('mysql2');
const methodOverride = require('method-override');
const app = express();
// MySQL 연결 설정
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '비밀번호입니다',
database: 'firstdabase'
});
// 데이터베이스 연결
db.connect((err) => {
if (err) {
throw err;
}
console.log('MySQL Connected...');
});
// body값 받아오기 위한 미들웨어
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// methodOverride
app.use(methodOverride('_method'));
// View engine 설정
app.set('view engine', 'ejs');
// 게시글 생성
app.post('/posts', (req, res) => {
const { title, content } = req.body;
const sql = 'INSERT INTO posts (title, content) VALUES (?, ?)';
db.query(sql, [title, content], (err, result) => {
if (err) {
return res.status(500).send('Server error');
}
res.redirect('/');
});
});
// 게시글 조회 (전체)
app.get('/', (req, res) => {
const sql = 'SELECT * FROM posts';
db.query(sql, (err, results) => {
if (err) {
return res.status(500).send('Server error');
}
console.log(results)
res.render('index.ejs', { posts: results });
});
});
// 게시글 조회 (단일)
app.get('/posts/:id/edit', (req, res) => {
const { id } = req.params;
const sql = 'SELECT * FROM posts WHERE id = ?';
db.query(sql, [id], (err, result) => {
if (err) {
return res.status(500).send('Server error');
}
if (result.length === 0) {
return res.status(404).send('Post not found');
}
console.log(result)
res.render('edit.ejs', { post: result[0] });
});
});
// 게시글 업데이트
app.put('/posts/:id', (req, res) => {
const { id } = req.params;
const { title, content } = req.body;
const sql = 'UPDATE posts SET title = ?, content = ? WHERE id = ?';
db.query(sql, [title, content, id], (err, result) => {
if (err) {
return res.status(500).send('Server error');
}
if (result.affectedRows === 0) {
return res.status(404).send('Post not found');
}
res.redirect('/');
});
});
// 게시글 삭제
app.delete('/posts/:id', (req, res) => {
const { id } = req.params;
const sql = 'DELETE FROM posts WHERE id = ?';
db.query(sql, [id], (err, result) => {
if (err) {
return res.status(500).send('Server error');
}
if (result.affectedRows === 0) {
return res.status(404).send('Post not found');
}
res.redirect('/');
});
});
// 서버 시작
app.listen(3000, () => {
console.log(`Server running on port 3000`);
});
'데이터베이스 > MySQL' 카테고리의 다른 글
MySQL 서브쿼리(ANY, ALL) (0) | 2024.07.13 |
---|---|
MySQL의 Index (0) | 2024.07.11 |
집계함수와 제어키워드 (0) | 2024.07.10 |
구글 클라우드를 통한 MySQL 배포하기 (0) | 2024.07.09 |
정규화와 역정규화 그리고 이상현상, 함수의 종속성 (2) | 2024.07.09 |