[Node.js] for MacOS / VSCdoe + express + mysql + CRUD 예제
개요
- Node.js의 Express 프레임 워크로 MySQL에 연동해서 서버를 구축
구축방법
- Express 웹 서버 실행.
- MySQL 데이터베이스 Configuration 설정
- School 라는 샘플 모델 생성 후 컨트롤러 작성
- CRUD 작업을 처리하기 위한 routes를 정의
1. 테이블 생성하기
CREATE TABLE `school_tb` (
`id` int(11) NOT NULL,
`nm` varchar(100) DEFAULT NULL,
`age` varchar(100) DEFAULT NULL,
`class` varchar(100) DEFAULT NULL,
`grade` varchar(100) DEFAULT NULL,
PRIMARY KEY (`seq`)
)
2. 편한 방법으로 프로젝트 폴더 생성하기
// mkdir로 생성해도 된다.
3. 프로젝트 폴더에서 npm init 실행
npm init
엔터를 계속 치면 프로젝트 내부에 package.json 파일이 생성된다.
4. Express, mysql, body-parser 모듈을 설치
npm install express mysql body-parser --save
package-lock.json 파일이 생성되고 안에 의존성관련 내용들이 있다.
5. Express 웹 서버 세팅
server.js 파일을 생성 후 내용을 입력한다.
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get("/", (req, res)=>{
res.json({message: "Hello Node!"});
});
// 포트넘버 설정
app.listen(3000, ()=>{
console.log("Server is running on port 3000.");
})
- Express, bodyParser 모듈 import. (Express: Restapi 구축을 위한 프레임워크 / Body-parser: request를 분석하고, routes에 엑세스 해야 하는 req.body 개체를 만들기 위해 사용)
- express app을 만든다음 app.use()를 사용해서 body-parser 미들웨어 추가
- 테스트 하기 쉽게 간단한 get 경로 정의
- 포트 3000에서 request 요청 수신
6. 실행 테스트
- 터미널 실행 테스트
//노드 실행
node server.js
//결과 console.log
Server is running on port 3000.
- 브라우저 실행 테스트
//출력 확인
{"message":"Hello Node!"}
7.MySQL configuration 작성 및 연동
- app/config 폴더를 생성후 안에 db.config.js파일을 생성한다. 그리고 데이터베이스 계정 접속 정보 내용을 추가한다.
module.exports = {
HOST: "localhost",
USER: "root",
PASSWORD: "1234",
DB: "study"
};
8. model 정의
- app/model 폴더를 생성후 school.model.js 파일을 생성한다. 이 파일에는 학급 객체에 대한 생성자를 정의하고, 데이터베이스 연결을 사용해서 CRUD 기능일 실행한다.
작성할 CURD Function
1. 새 학생 생성
2. id로 학생 테이블 조회
3. 전체 테이블 조회
4. id로 학생 데이터 업데이트
5. id로 학생 데이터 삭제
6. 전체 고객 데이터 삭제
school.model.js 작성
const sql = require("./db.js");
//생성자
const School = function(school){
this.nm = school.nm;
this.age = school.age;
this.grade = school.grade;
this.class = school.class;
};
// school 인서트
School.insert = (newSchool, result)=>{
sql.query("INSERT INTO school SET ?", newSchool, (err, res)=>{
if(err){
console.log("error: ", err);
result(err, null);
return;
}
console.log("Insert school: ",{id:res.inseertId, ...newSchool });
result(null, {id: res.inseertId, ...newSchool});
});
};
// school id로 조회
School.findOne = (schoolID, result)=>{
console.log("::::findOne "+schoolID);
console.log("::::result "+result);
sql.query('SELECT * FROM school WHERE id = ?',schoolID, (err, res)=>{
if(err){
console.log("error: ", err);
result(err, null);
return;
}
if(res.length){
console.log("found school: ", res[0]);
result(null, res[0]);
return;
}
// 결과가 없을 시
result({kind: "not_found"}, null);
});
};
// school 전체 조회
School.getAll = result =>{
sql.query('SELECT * FROM school ', (err, res)=>{
if(err){
console.log("error: ", err);
result(err, null);
return;
}
console.log("school: ", res);
result(null, res);
});
};
// school id로 수정
School.update = (id, school, result)=>{
console.log("::::update "+id);
console.log("::::school "+school);
sql.query('UPDATE school SET nm = ?, age = ?, grade = ?, class = ? WHERE id = ?',
[school.age, school.grade, school.class, id], (err, res)=>{
if(err){
console.log("error: ", err);
result(err, null);
return;
}
if(res.affectedRows ==0){
// id 결과가 없을 시
result({kind: "not_found"}, null);
return;
}
console.log("update school: ", {id:id, ... school});
result(null, {id:id, ...school});
});
};
// school id로 삭제
School.remove = (id, result)=>{
console.log("::::remove "+id);
console.log("::::result "+result);
sql.query('DELETE FROM school WHERE id = ?',id, (err, res)=>{
if(err){
console.log("error: ", err);
result(err, null);
return;
}
if(res.affectedRows ==0){
// id 결과가 없을 시
result({kind: "not_found"}, null);
return;
}
console.log("deleted school with id: ", id);
result(null, res);
});
};
// school 전체 삭제
School.removeAll = result =>{
sql.query('DELETE FROM school',(err, res)=>{
if(err){
console.log("error: ", err);
result(err, null);
return;
}
if(res.affectedRows ==0){
// id 결과가 없을 시
result({kind: "not_found"}, null);
return;
}
console.log('deleted ${res.affectedRows} school ');
result(null, res);
});
};
module.exports = School;
9.Routes 정의
HTTP 요청(GET, POST, PUT, DELETE)을 받았을 때 어떻게 응답할지 route 정의하는 것
- /school: GET, POST, DELETE
- /school/:schoolID: GET, PUT, DELETE
app/routes 폴더 생성하고 school.routes.js 파일을 작성한다.
module.exports = app =>{
const school = require("../controllers/school.controller.js");
// 인서트
app.post("/school", school.insert);
// 전체 조회
app.get("/school", school.findAll);
// id로 조회
app.get("/school/:schoolId", school.findOne);
// id로 수정
app.put("/school/:schoolId", school.update);
// id로 삭제
app.delete("/school/:schoolId", school.delete);
// 전체 삭제
app.delete("/school", school.deleteAll);
};
작성한 routes.js 파일을 server.js에 포함시킨다.
server.js
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get("/", (req, res)=>{
res.json({message: "Hello Node!"});
});
require("./app/routes/school.routes.js")(app);
// 포트넘버 설정
app.listen(3000, ()=>{
console.log("Server is running on port 3000.");
})
10. Controller 작성
app/controller 폴더를 생성하고 school.controller.js 파일을 작성한다. 컨트롤레 안에 CRUD function을 구현할 것
const School = require("../models/school.model.js");
// 새 객체 생성
exports.insert = (req,res)=>{
if(!req.body){
res.status(400).send({
message: "Content can not be empty!"
});
};
const school = new School({
nm: req.body.nm,
age: req.body.age,
grade: req.body.grade,
class: req.body.class
});
// 데이터베이스에 저장
School.insert(school, (err, data) =>{
if(err){
res.status(500).send({
message:
err.message || "Some error occured while creating th School."
});
}else {
res.send("저장을 성공 하였습니다.");
}
})
};
// 전체 조회
exports.findAll = (req,res)=>{
School.getAll((err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while retrieving school."
});
else res.send(data);
});
};
// id로 조회
exports.findOne = (req,res)=>{
School.findOne(req.params.schoolId, (err, data) => {
if (err) {
if (err.kind === "not_found") {
res.status(404).send({
message: `Not found School with id ${req.params.schoolId}.`
});
} else {
res.status(500).send({
message: "Error retrieving School with id " + req.params.schoolId
});
}
} else res.send(data);
});
};
// id로 갱신
exports.update = (req,res)=>{
// Validate Request
if (!req.body) {
res.status(400).send({
message: "Content can not be empty!"
});
}
School.update(
req.params.schoolId,
new School(req.body),
(err, data) => {
if (err) {
if (err.kind === "not_found") {
res.status(404).send({
message: `Not found School with id ${req.params.schoolId}.`
});
} else {
res.status(500).send({
message: "Error updating School with id " + req.params.schoolId
});
}
} else res.send(data);
}
);
};
// id로 삭제
exports.delete = (req,res)=>{
School.remove(req.params.schoolId, (err, data) => {
if (err) {
if (err.kind === "not_found") {
res.status(404).send({
message: `Not found School with id ${req.params.schoolId}.`
});
} else {
res.status(500).send({
message: "Could not delete School with id " + req.params.schoolId
});
}
} else res.send({ message: `School was deleted successfully!` });
});
};
// 전체 삭제
exports.deleteAll = (req,res)=>{
School.removeAll((err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while removing all school."
});
else res.send({ message: `All School were deleted successfully!` });
});
};
11. API 테스트 (with postman)
VS code 터미널에서 다음 명령어를 입력해서 실행한다.
node server.js // npm start 도 가능.
#서버 구동시 아래의 에러 발생시 해결방법
Error: listen EADDRINUSE: address already in use :::3000
찾기
lsof -i :포트번호
ex) lsof -i :3000
//포트 목록이 나온다.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 2309 devmk 23u IPv6 0xd4b4f63f02931517 0t0 TCP *:hbci (LISTEN)
kill하기 (pid 입력)
kill -9 PID번호
ex) kill -9 2309
PID입력하여 비정상으로 종료된 중복 포트를 제거한다.
다시 서버를 구동하면 아래의 메세지가 출력된다면 정상이다.
- POST /school 인서트
- GET /school 전체 칼럼 조회
- GET /school/:id 단건조회
- PUT /school/:id 단건수정
- DELETE /school/:id 단건삭제
- DELETE /school/ 전체삭제
본문의 출처
https://expressjs.com/ko/
https://1-day-1-coding.tistory.com/51