※ express에 대한 설명은 아래의 링크를 참고 바란다.
express 프레임워크로 웹 서버 구축하기
간단하게 express 사용하여 웹 서버를 구축해보았다. 하지만 사용자의 데이터를 받어서 데이터베이스에 저장을 하거나 데이터베이스에서 동적인 데이터를 보여주는 것이 아닌 단순히 고정적인 데이터만 사용자에게 보여주는 웹 서버였다. 이번에는 MySQL
이라는 데이터베이스와 연동하여 게시판 웹 서버를 구축해보았다.
필자는 회원가입 및 로그인 기능이 없는 간단한 게시판 웹이므로 게시글 데이터를 저장하는 테이블과 게시글의 파일 데이터를 저장하는 테이블을 만들었다. 다음의 코드를 MySQL
에서 실행한다.
DROP TABLE IF EXISTS `board`;
CREATE TABLE `board` (
`bnum` int NOT NULL,
`id` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8_bin NOT NULL,
`title` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8_bin NOT NULL,
`content` varchar(500) CHARACTER SET utf8mb3 COLLATE utf8_bin NOT NULL,
`writedate` date NOT NULL,
PRIMARY KEY (`bnum`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
DROP TABLE IF EXISTS `file`;
CREATE TABLE `file` (
`fnum` int NOT NULL AUTO_INCREMENT,
`bnum` int NOT NULL,
`savefile` varchar(45) CHARACTER SET utf8mb3 NOT NULL,
`filetype` varchar(45) CHARACTER SET utf8mb3 NOT NULL,
`writedate` date DEFAULT NULL,
PRIMARY KEY (`fnum`),
KEY `board_bnum_fk_idx` (`bnum`),
CONSTRAINT `board_bnum_fk` FOREIGN KEY (`bnum`) REFERENCES `board` (`bnum`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
MySQL
데이터베이스 연동은 간단하다. Node.js
에서 MySQL
을 사용할려면 다음 명령어를 명령 프롬프트에 입력하여 패키지를 설치한다.
npm install mysql2
필자의 같은 경우는 작업 폴더 안에 config
라는 폴더를 별도로 생성하고 해당 폴더 안에 mysql.js
파일을 만들어서 모듈 형식으로 코드를 작성하였다. 이렇게 코드를 작성한 이유는 나중에 웹 서버를 모듈 형식으로 구성할 때도 편하게 사용할 수 있기 때문이다. 우선 설치한 라이브러리를 포함시켜 준다.
var mysql = require("mysql2");
연동할 데이터베이스의 주소와 포트 및 로그인할 계정과 엑세스할 데이터베이스를 설정한다.
var db_info = {
host: "localhost", // 데이터베이스 주소
port: "3306", // 데이터베이스 포트
user: "root", // 로그인 계정
password: "1234", // 비밀번호
database: "react_node_board", // 엑세스할 데이터베이스
};
모듈 형식으로 코드를 작성하면 데이터베이스와의 연동은 끝이 난다.
module.exports = {
init: function () {
return mysql.createConnection(db_info);
},
connect: function (conn) {
conn.connect(function (err) {
if (err) console.error("mysql connection error : " + err);
else console.log("mysql is connected successfully!");
});
},
};
MySQL
데이터베이스 연동 전체 코드는 다음과 같다.
var mysql = require("mysql2");
var db_info = {
host: "localhost",
port: "3306",
user: "root",
password: "1234",
database: "react_node_board",
};
module.exports = {
init: function () {
return mysql.createConnection(db_info);
},
connect: function (conn) {
conn.connect(function (err) {
if (err) console.error("mysql connection error : " + err);
else console.log("mysql is connected successfully!");
});
},
};
express를 사용하여 단순히 게시글을 읽기 및 쓰기와 수정 및 삭제기능을 하는 백엔드 서버를 구현할 것이다. api
형식으로 구현할 것이며 추가적으로 이미지 업로드기능과 cors
설정은 별도로 설정하기 않고 모두 오픈하는 방식으로 구현해볼 것이다.
다음의 명령어를 입력하여 이미지 업로드 및 파일을 읽는 라이브러리를 설치한다.
npm install fs
npm install cors
npm install multer
npm install express
설치가 완료되면 해당 라이브러리와 mysql.js
파일을 코드에 포함시킨다. 사용자로 부터 데이터 입력 받은 것을 parsing
을 하기 위해 body-parser
라이브러리도 함께 추가한다.
const express = require("express");
const multer = require("multer");
const cors = require("cors");
const fs = require("fs");
const bodyParser = require("body-parser");
const db = require("./config/mysql.js");
다음의 코드를 작성하여 mysql.js
의 init함수를 호출하여 데이터베이스와 connection
을 생성하고 express
를 정의한다.
const app = express();
const conn = db.init();
사용자가 이미지를 업로드할 경우 저장할 폴더가 없을 경우 multer
라이브러리를 사용하여 자동으로 생성할 수 있도록 설정한다.
const upload = multer({
storage: multer.diskStorage({
destination: function (req, file, callback) {
console.log(file),
fs.existsSync("./uploads/") ||
fs.mkdirSync("./uploads/", { recursive: !0 }),
callback(null, "./uploads/");
},
filename: function (req, file, callback) {
callback(null, file.originalname);
},
}),
});
express
에서 body-parser
미들웨어를 사용할 수 있도록 설정한다.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
cors
를 전체로 허용하도록 설정한다.
app.use(cors());
마지막으로 서버의 포트 및 아이피를 설정한다.
app.set("port", process.env.PORT || 3000); // 포트 설정
app.set("host", process.env.HOST || "0.0.0.0"); // 아이피 설정
API
형식으로 사용자로 부터 데이터를 입력받아 데이터베이스에 수신받고 전송해주는 웹 서버를 구현하면 된다. 다음은 데이터베이스로 부터 게시글 데이터를 가져오 API
이다. MySQL
데이터베이스와 통신하는 방법은 query(sql, params, callback)
의 형태로 사용되며 각각의 파라미터의 역할은 다음과 같다.
?
를 이용하여 서버에서 정의된 변수를 사용 가능.// 게시글 목록 보기
app.get("/view", function (req, res) {
var sql = "select * from board";
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else res.send(result);
});
});
사용자로부터 게시글에 대한 데이터를 받아 데이터베이스에 저장하는 api
를 구현하였다. body는 해당 페이지의 body태그를 의미하며 upload.single("img")
는 input태그에서 type이 file이고 이름이 img인 태그의 파일을 불러온다. 다음은 데이터베이스에 저장된 전체 게시글 수에서 1을 더하여 게시글 번호를 생성한 후 데이터베이스에 게시글 데이터를 저장하는 코드이다.
// 게시글 쓰기
app.post("/insert", upload.single("img"), function (req, res) {
var body = req.body;
var sql = "SELECT count(*)+1 as bnum FROM board ";
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else {
var sql =
"insert into board (bnum,id,title,content,writedate) values(?,?,?,?,NOW())";
var params = [result[0].bnum, body.id, body.title, body.content];
conn.query(sql, params, function (err) {
if (err) console.log("query is not excuted: " + err);
else if (req.file != null) {
// 만약 업로드 된 파일이 있다면
var sql =
"insert into file (bnum,savefile,filetype,writedate) values (?,?,?,now())";
var params = [body.bnum, req.file.originalname, req.file.mimetype];
conn.query(sql, params, function (err) {
if (err) console.log("query is not excuted: " + err);
else res.sendStatus(200);
});
} else res.sendStatus(200);
});
}
});
});
사용자가 게시글 목록에서 읽고 싶은 게시글을 클릭하였을 경우 해당 게시글에 대한 번호를 받아서 상세 데이터를 보내주는 api
이다.
// 게시글 보기
app.get("/read/:bnum", function (req, res) {
var sql = "select * from board where bnum=" + req.params.bnum;
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else res.send(result);
});
});
다음의 코드는 사용자가 게시글을 수정하였을 경우 데이터베이스에서 데이터를 수정하는 api
이다.
// 게시글 수정
app.post("/update/:bnum", function (req, res) {
var body = req.body;
var sql =
"update board set id=?, title=?, content=? where bnum=" + req.params.bnum;
var params = [body.id, body.title, body.content];
conn.query(sql, params, function (err) {
if (err) console.log("query is not excuted: " + err);
else res.sendStatus(200);
});
});
다음은 데이터베이스에서 게시글 데이터를 삭제하는 api
이다.
// 게시글 삭제
app.get("/delete/:bnum", function (req, res) {
var sql = "delete from board where bnum=" + req.params.bnum;
conn.query(sql, function (err) {
if (err) console.log("query is not excuted: " + err);
else res.sendStatus(200);
});
});
게시글의 번호를 입력받아 해당 게시글에서 업로드된 이미지파일이 있다면 해당 이미지파일 보여주는 api
이다.
// 이미지 파일 불러오기
app.get("/img/:bnum", function (req, res) {
var sql = "select * from file where bnum=" + req.params.bnum;
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else if (result.length != 0) {
fs.readFile("uploads/" + result[0].savefile, function (err, data) {
res.writeHead(200, { "Context-Type": "text/html" });
res.end(data);
});
} else res.sendStatus(200);
});
});
전제 코드는 다음과 같다.
const express = require("express");
const multer = require("multer");
const cors = require("cors");
const fs = require("fs");
const bodyParser = require("body-parser");
const db = require("./config/mysql.js");
const app = express();
const conn = db.init();
const upload = multer({
storage: multer.diskStorage({
destination: function (req, file, callback) {
console.log(file),
fs.existsSync("./uploads/") ||
fs.mkdirSync("./uploads/", { recursive: !0 }),
callback(null, "./uploads/");
},
filename: function (req, file, callback) {
callback(null, file.originalname);
},
}),
});
app.set("port", process.env.PORT || 3000);
app.set("host", process.env.HOST || "0.0.0.0");
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// 게시글 목록 보기
app.get("/view", function (req, res) {
var sql = "select * from board";
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else res.send(result);
});
});
// 게시글 쓰기
app.post("/insert", upload.single("img"), function (req, res) {
var body = req.body;
var sql = "SELECT count(*)+1 as bnum FROM board ";
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else {
var sql =
"insert into board (bnum,id,title,content,writedate) values(?,?,?,?,NOW())";
var params = [result[0].bnum, body.id, body.title, body.content];
conn.query(sql, params, function (err) {
if (err) console.log("query is not excuted: " + err);
else if (req.file != null) {
// 만약 업로드 된 파일이 있다면
var sql =
"insert into file (bnum,savefile,filetype,writedate) values (?,?,?,now())";
var params = [body.bnum, req.file.originalname, req.file.mimetype];
conn.query(sql, params, function (err) {
if (err) console.log("query is not excuted: " + err);
else res.sendStatus(200);
});
} else res.sendStatus(200);
});
}
});
});
// 게시글 보기
app.get("/read/:bnum", function (req, res) {
var sql = "select * from board where bnum=" + req.params.bnum;
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else res.send(result);
});
});
// 게시글 수정
app.post("/update/:bnum", function (req, res) {
var body = req.body;
var sql =
"update board set id=?, title=?, content=? where bnum=" + req.params.bnum;
var params = [body.id, body.title, body.content];
conn.query(sql, params, function (err) {
if (err) console.log("query is not excuted: " + err);
else res.sendStatus(200);
});
});
// 게시글 삭제
app.get("/delete/:bnum", function (req, res) {
var sql = "delete from board where bnum=" + req.params.bnum;
conn.query(sql, function (err) {
if (err) console.log("query is not excuted: " + err);
else res.sendStatus(200);
});
});
// 이미지 파일 불러오기
app.get("/img/:bnum", function (req, res) {
var sql = "select * from file where bnum=" + req.params.bnum;
conn.query(sql, function (err, result) {
if (err) console.log("query is not excuted: " + err);
else if (result.length != 0) {
fs.readFile("uploads/" + result[0].savefile, function (err, data) {
res.writeHead(200, { "Context-Type": "text/html" });
res.end(data);
});
} else res.sendStatus(200);
});
});
// 서버 동작중인 표시
app.listen(app.get("port"), app.get("host"), () =>
console.log(
"Server is running on : " + app.get("host") + ":" + app.get("port")
)
);
개꿀 정보 감사합니다.