Node.js, express with mongoDB로 application 만들기
1. vsc 기본 세팅
- MONGODB_CRUD 폴더 > server.js 파일 생성
- 터미널 경로 MONGODB_CRUD에서 npm init
npm init
- 필요한 packages 다운
npm install express morgan nodemon ejs body-parser dotenv mongoose axios
express :
morgan : HTTP request logger middleware for node.js (It logs requests.)
nodemon : 변경 사항있을 때 자동으로 server on (오직 js 파일 변경 시에만 ! html X )
ejs : 템플릿 엔진 (템플릿을 읽어 엔진의 문법과 설정에 따라 파일을 html 형식으로 변환시키는 모듈)
body-parser :
dotenv :
mongoose :
axios :
* 템플릿 엔진이란?
동적인 결과를 정적인 파일에 담기위해 사용 / 정적인 파일만을 서비스하면 필요없다.
JS 코드로 연산된 결과를 변수에 넣고 변수를 뷰 파일에서도 사용할 수 있게끔 한다.
템플릿 엔진은 클라이언트 요청에 따라 웹문서 들어가는 내용(결과)이 달라질 수 있어서 정적인 부분과 동적인 부분을 따로하기 위해 사용한다.
- package.json 의 "scripts" - "start": "node server.js" 를 -> nodemon server.js 로 변경
{
"name": "mongodb_crud",
"version": "1.0.0",
"description": "crud with express and mongodb",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon server.js" // 요로코롬
},
"keywords": [
"crud",
"mongodb"
],
"author": "emily",
"license": "ISC",
"dependencies": {
"axios": "^0.21.4",
"body-parser": "^1.19.0",
"dotenv": "^10.0.0",
"ejs": "^3.1.6",
"express": "^4.17.1",
"mongoose": "^6.0.7",
"morgan": "^1.10.0",
"nodemon": "^2.0.13"
}
}
MVC 패턴
Model, View, Controller 기반 폴더 구조 생성
- MONGODB_CRUD > assets 폴더 > css, js, img 폴더 생성
- MONGODB_CRUD > views 폴더 생성 (모든html 파일)
- MONGODB_CRUD > server 폴더 생성 (서버 측 코드 ex. 서비스, 모델, mongodb 연결)
- MONGODB_CRUD > server > controller, model, database, routes, services 폴더 생성
* database : database connection을 위한 폴더
* routers : router역할
* Model : mongodb로 작업, 데이터 유효성 검사, 데이터 처리 등
* controller : 리소스에 대한 사용자 요청
2. server.js 코드 작성 및 실행 / 필요한 packages 가져와서 설정
server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8080;
app.get('/', (req,res)=>{
res.send('CRUD with mongoDB');
})
app.listen(PORT, ()=>{
console.log(`Server is running on http://localhost:${PORT}`)
})
- MONGODB_CRUD > config.env 파일 생성 및 변수 작성
- server.js 에 dotenv 설정, path 정해주기 (const PORT 변수 보다 위에 선언 되어야함 **** )
const express = require('express');
const app = express();
const dotenv = require('dotenv');
dotenv.config({path:'config.env'})
const PORT = process.env.PORT || 8080;
app.get('/', (req,res)=>{
res.send('CRUD with mongoDB');
})
app.listen(PORT, ()=>{
console.log(`Server is running on http://localhost:${PORT}`)
})
-> PORT 3000으로 잘 찍힘
지금까지는 env 파일 이름을 모두 .env 라고 명명하고 server.js 에서 쓸 때는 dotenv.config(); 요렇게만 썼었다 !
env 파일 명이 .env 가 아니라면 dotenv.config({path:'[파일명].env'}) 를 써줘야 함 !
Modules
- morgan 가져오기
const express = require('express');
const app = express();
const morgan = require('morgan'); //1
const dotenv = require('dotenv');
dotenv.config({path:'config.env'});
const PORT = process.env.PORT || 8080;
// log requests
app.use(morgan("tiny")); //2
"tiny"는 morgan의 predefined format string이다
predefined format stirng으로는 dev, short, common, combined 등이 있다. 이 인자값에 따라 log도 다르게 된다. 개발 시에는 short / dev를 많이 쓰고 배포 시에는 common, combined를 많이 쓴다고 한다!
- bodyparser 가져오기 / ejs 설정
const express = require('express');
const app = express();
const morgan = require('morgan');
const bodyParser = require('body-parser'); // 1
const path = require('path'); //built-in
const dotenv = require('dotenv');
dotenv.config({path:'config.env'});
const PORT = process.env.PORT || 8080;
// log requests
app.use(morgan("tiny"));
// parse request to body-parser
app.use(bodyParser.urlencoded({extended:true})); // 2
// set view engine
app.set("view engine", "ejs"); // 3
// views 안에 ejs 폴더를 만들어 사용시 아래처럼 path 설정해줘야함
//app.set("views", path.resolve(__dirname, "views/ejs")) // 4 (이건optional)
app.get('/', (req,res)=>{
res.send('CRUD with mongoDB');
})
app.listen(PORT, ()=>{
console.log(`Server is running on http://localhost:${PORT}`)
})
ejs 파일들을 담긴 경로를 app.set('views', path.resolve(__dirname, "위치")) 요렇게 설정해 주기도 한다! 여기선 할 필요가 없어서 패스
이전까지는 nunjucks로 해왔는데 요 역할을 ejs 가 해주는 것 같다
- assets 설정
.
.
// load assets
app.use('/css', express.static(path.resolve(__dirname, "assets/css")));
app.use('/img', express.static(path.resolve(__dirname, "assets/img")));
app.use('/js', express.static(path.resolve(__dirname, "assets/js")));
.
.
이렇게 css 폴더 경로 설정을 하면 css/style.css 이런 식으로 경로 고민없이 css 폴더 안의 css 파일을 바로 쓸 수 있다.
img, js 도 마찬가지!
3. Views
views > index.ejs 파일 생성 & 코드 작성
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Application</title>
</head>
<body>
<!-- Header -->
<header id="header">
<nav>
<div class="container">
<div class="text-center">
<a href="/" class="nav-brand text-dark">User Management System</a>
</div>
</div>
</nav>
</header>
<!-- /Header -->
</body>
</html>
다시 서버를 키고 localhost:3000 들어가면 아직 res.send('CRUD ~~~') 요 String이 나온다.
이제 위에 만든 index.ejs 를 뿌리도록 코드 작성하기
server.js
// set view engine
app.set("view engine", "ejs");
// views 안에 ejs 폴더를 만들어 사용시 아래처럼 path 설정해줘야함
//app.set("views", path.resolve(__dirname, "views/ejs"))
.
.
.
app.get('/', (req,res)=>{
res.render('index');
})
index.ejs 파일을 적어주는데 .ejs 확장자를 안써줘도 되는 이유는 위에 app.set에서 view engine ejs 로 설정해놨기 때문! 예전에 html nunjucks로 했을 때 html로 적은 기억이 난다.
4. header footer 나누기
MONGDB_CRUD > views > include 폴더 생성 > _header.ejs, _footer.ejs 파일 생성
- _header.ejs 에 index.ejs top to /Header 까지 붙여넣기
- _footer.ejs 에 나머지 붙여넣기
- index.ejs 에 include 문법 넣기
_header.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Application</title>
</head>
<body>
<!-- Header -->
<header id="header">
<nav>
<div class="container">
<div class="text-center">
<a href="/" class="nav-brand text-dark">User Management System</a>
</div>
</div>
</nav>
</header>
<!-- /Header -->
_footer.ejs
</body>
</html>
index.ejs
<!-- include header -->
<%- include('include/_header.ejs')%>
<!-- /include header -->
<!-- include footer -->
<%- include('include/footer.ejs')%>
<!-- /include footer -->
5. Live Server Extenstion
서버를 다시 off -> on 하고 브라우저 새로고침하는 번거로움 없애기
views > index.html 파일 생성 & _header.ejs, _footer.ejs 안 코드 붙여넣기
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Application</title>
</head>
<body>
<!-- Header -->
<header id="header">
<nav>
<div class="container">
<div class="text-center">
<a href="/" class="nav-brand text-dark">User Management System</a>
</div>
</div>
</nav>
</header>
<!-- /Header -->
</body>
</html>
Live server 다운
Open with live server 클릭 -> 브라우저 열림 -> html 수정하면 자동으로 새로고침됨 !
5. Main section
views > index.html
main site 코드 추가 & 아이콘 추가
아이콘 추가 방법
google font awesome cdn !
https://cdnjs.com/libraries/font-awesome
첫번째 코드 주소 복사 -> index.html head section에 붙여넣기
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Application</title>
요고 ↓↓
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css" integrity="sha512-YWzhKL2whUzgiheMoBFwW8CKV4qpHQAEuvilg9FAn5VJUDwKZZxkJNuGM4XkWuk94WCrrwslk8yWNGmY1EduTA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
google font awesome icons 검색
https://fontawesome.com/v5.15/icons?d=gallery&p=2&m=free
user 검색 -> 원하는 icon 클릭 -> html 복사
index.html 에 복사한 코드 넣기
<!-- Main site -->
<main id="site-main">
<div class="container">
<div class="box-nav d-flex justify-between">
<a href="/add-user" class="border-shadow">
<span class="text-gradient">New User
<!-- 요고 ↓↓↓ -->
<i class="fas fa-user-circle"></i>
</span>
</a>
</div>
</div>
</main>
<!-- /Main site -->
index.html - table 추가
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Application</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css" integrity="sha512-YWzhKL2whUzgiheMoBFwW8CKV4qpHQAEuvilg9FAn5VJUDwKZZxkJNuGM4XkWuk94WCrrwslk8yWNGmY1EduTA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
<!-- Header -->
<header id="header">
<nav>
<div class="container">
<div class="text-center">
<a href="/" class="nav-brand text-dark">User Management System </a>
</div>
</div>
</nav>
</header>
<!-- /Header -->
<!-- Main site -->
<main id="site-main">
<div class="container">
<div class="box-nav d-flex justify-between">
<a href="/add-user" class="border-shadow">
<span class="text-gradient">New User
<i class="fas fa-user-circle"></i>
</span>
</a>
</div>
<!-- form handling-->
<form action="/" method="POST">
<table class="table">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>@Email</th>
<th>Gender</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Username</td>
<td>exameple@gmail.com</td>
<td>Female</td>
<td>Active</td>
<td>
<a href="#" class="btn border-shadow update">
<span class="text-gradient"><i class="fas fa-pencil-alt"></i></span>
</a>
<a class="btn border-shadow delete">
<span class="text-gradient"><i class="fas fa-times"></i></span>
</a>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</main>
<!-- /Main site -->
</body>
</html>
6. Styling HTML (CSS) + responsive application
- index.html <head> 안에 css추가
<link rel="stylesheet" href="../assets/css/style.css">
- font 가져오기
폰트 2개 select : font names = barlow - regular 400 , pt sans - regular 400
import 누르고 파란 부분만 복사 -> assets > css > style.css 에 붙여넣기
@import url('https://fonts.googleapis.com/css2?family=Barlow&family=PT+Sans&display=swap');
/* 변수 만들기 */
:root{
--dark:#2b2d42;
--light:#adb5bd;
--border:#dee2d6;
--border-btn:#edf2f4;
}
*{
padding:0;
margin:0;
box-sizing: border-box;
}
a{
text-decoration:none;
font-family: 'PT Sans', sans-serif;
}
쓸 폰트만 google 의 아래 파란 부분만 복사해서 사용
나머지 부분들 CSS 추가
* CSS 전 *
* CSS 후 *
7. Table & Form
Let's make this table reponsive !
/* make it responsive! */
@media only screen and (max-width:1024px){
table, thead, tbody, th, td, tr{
display: block;
}
thead tr{
position:absolute;
top:-9999px;
left:-9999px;
}
tr {border:1px solid var(--border);}
td{
border:none;
position:relative;
}
}
FORM
view > add_user.html 생성 및 코드 작성
- index.html 의 모든 코드 복사 & 붙여넣기 -> form handling 부분 코드 모두 삭제
form 코드 수정
<!-- form handling-->
<form method="POST" id="update_user">
</form>
브라우저 add_user.html로 들어가기
<!-- Main site -->
<main id="site-main">
<div class="container">
<div class="box-nav d-flex justify-between">
</div>
<!-- form handling-->
<form method="POST" id="update_user">
</form>
</div>
</main>
<!-- /Main site -->
</body>
</html>
main site 쪽 안에 불필요한 코드 삭제
입력 form 코드 작성
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CRUD Application</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css"
integrity="sha512-YWzhKL2whUzgiheMoBFwW8CKV4qpHQAEuvilg9FAn5VJUDwKZZxkJNuGM4XkWuk94WCrrwslk8yWNGmY1EduTA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="../assets/css/style.css" />
</head>
<body>
<!-- Header -->
<header id="header">
<nav>
<div class="container">
<div class="text-center">
<a href="/" class="nav-brand text-dark">User Management System </a>
</div>
</div>
</nav>
</header>
<!-- /Header -->
<!-- Main site -->
<main id="site-main">
<div class="container">
<div class="box-nav d-flex justify-between">
<div class="filter">
<a href="/"><i class="fas fa-angle-double-left"></i>All Users</a>
</div>
</div>
<div class="form-title text-center">
<h2 class="text-dark">
New User
</h2>
<span class="text-light">
Use the below form to create a new account
</span>
</div>
<!-- form handling-->
<form method="POST" id="update_user">
<div class="new_user">
<div class="form-group">
<label for="name" class="text-light">Name</label>
<input type="hidden" name="id" value="">
<input type="text" name="name" value="" placeholder="Name">
</div>
<div class="form-group">
<label for="Email" class="text-light">Email</label>
<input type="text" name="email" value="" placeholder="example@gmail.com">
</div>
<div class="form-group">
<label for="gender" class="text-light">Gender</label>
<div class="radio inline">
<input type="radio" name="gender" value="Male">
<label for="gender" class="radio-label">Male</label>
</div>
<div class="radio inline">
<input type="radio" name="gender" value="Female">
<label for="gender" class="radio-label">Female</label>
</div>
</div>
<div class="form-group">
<label for="gender" class="text-light">Status</label>
<div class="radio inline">
<input type="radio" name="status" value="Active">
<label for="gender" class="radio-label">Active</label>
</div>
<div class="radio inline">
<input type="radio" name="gender" value="Inactive">
<label for="gender" class="radio-label">Inactive</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn text-dark update">Save</button>
</div>
</div>
</form>
</div>
</main>
<!-- /Main site -->
</body>
</html>
8. Add styling
style.css (아까 index.html을 그대로 복사해서 add_user.html을 만듬 - 거기에 style.css 코드가 들어가 있다!)
.
.
.
/* ADD USER & UPDATE USER TEMPLATE */
.form-title{
margin-top:2em;
}
.form-title>h2{
padding:.5em 0;
}
.new_user{
max-width:786px;
margin:auto;
}
#update_user .form-group,
#add_user .form-group{
margin:.4em 0;
}
#update_user .form-group input[type="text"],
#add_user .form-group input[type="text"]{
width:100%;
padding:.6em 1em;
margin: .5em 0;
border:1px solid var(--border);
font-family: 'Barlow', sans-serif;
font-size:1em;
border-radius:.2em;
}
#update_user .form-group .radio,
#add_user .form-group .radio{
margin:1em 2em;
}
/* adding style to radio buttons */
.radio input[type="radio"]{
position: absolute;
opacity: 0;
}
.radio input[type="radio"] + .radio-label:before{
content:"";
background:var(--border-btn);
border-radius: 100%;
border:1px solid var(--border);
display: inline-block;
width:1em;
height:1em;
position: relative;
top:-0em;
margin-right:.5em;
vertical-align: top;
cursor: pointer;
text-align: center;
-webkit-transition: all 250ms ease;
transition:all 250ms ease;
}
.radio input[type="radio"]:checked + .radio-label:before{
background-color: #16db93;
box-shadow: inset 0 0 0 4px #e0ecef;
}
.radio input[type="radio"]:focus + .radio-label:before{
outline: none;
border-color: #16db93;
}
.radio input[type="radio"]:disabled + .radio-label:before{
box-shadow: inset 0 0 0 4px #e9ecef;
border-color: #b4b4b4;
background: #b4b4b4;
}
radio 클릭해도 아직 동작을 안한다 ! -> html lable id 설정해주기
9. Label 설정하기
add_user.html
<!-- form handling-->
<form method="POST" id="update_user">
<div class="new_user">
<div class="form-group">
<label for="name" class="text-light">Name</label>
<input type="hidden" name="id" value="">
<input type="text" name="name" value="" placeholder="Name">
</div>
<div class="form-group">
<label for="Email" class="text-light">Email</label>
<input type="text" name="email" value="" placeholder="example@gmail.com">
</div>
<div class="form-group">
<label for="gender" class="text-light">Gender</label>
<div class="radio inline">
<input type="radio" id="radio-2" name="gender" value="Male">
<label for="radio-2" class="radio-label">Male</label>
</div>
<div class="radio inline">
<input type="radio" id="radio-3" name="gender" value="Female">
<label for="radio-3" class="radio-label">Female</label>
</div>
</div>
<div class="form-group">
<label for="gender" class="text-light">Status</label>
<div class="radio inline">
<input type="radio" id="radio-4" name="status" value="Active">
<label for="radio-4" class="radio-label">Active</label>
</div>
<div class="radio inline">
<input type="radio" id="radio-5" name="status" value="Inactive">
<label for="radio-5" class="radio-label">Inactive</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn text-dark update">Save</button>
</div>
</div>
</form>
input 에 id를 추가, label for ="해당 id"
아래처럼 클릭이 가능해짐
10. Save 버튼 css 추가하기
style.css
#update_user .form-group .btn,
#add_user .form-group .btn{
width:100%;
padding: .9em 1em;
background-color: #16db93;
border:none;
font-family: 'PT Sans', sans-serif;
font-size: 1em;
cursor:pointer;
border-radius: .2em;
margin: .5em 0;
}
#update_user .form-group .btn:hover,
#add_user .form-group .btn:hover{
background-color: var(--dark);
color:var(--border);
}
11. html -> ejs
html을 ejs 템플릿 엔진으로 바꾸기
1) index.html의 Main site 부분 복사 -> index.ejs 의 header & footer 사이에 붙여넣기
2) index.ejs 의 tr 부분 잘라내기 -> views > include > _show.ejs 파일 만들어 붙여넣기 -> index.ejs 의 잘라내진 공간에 include 문법 ㄱ ㄱ
index.ejs
<!-- include header -->
<%- include('include/_header.ejs')%>
<!-- /include header -->
<!-- Main site -->
<main id="site-main">
<div class="container">
<div class="box-nav d-flex justify-between">
<a href="/add-user" class="border-shadow">
<span class="text-gradient">
New User
<i class="fas fa-user-circle"></i>
</span>
</a>
</div>
<!-- form handling-->
<form action="/" method="POST">
<table class="table">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>@Email</th>
<th>Gender</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<%- include('include/_show.ejs')%>
</tbody>
</table>
</form>
</div>
</main>
<!-- /Main site -->
<!-- include footer -->
<%- include('include/_footer.ejs')%>
<!-- /include footer -->
3) _header.ejs 에 font link & css link 추가
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD Application</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css"
integrity="sha512-YWzhKL2whUzgiheMoBFwW8CKV4qpHQAEuvilg9FAn5VJUDwKZZxkJNuGM4XkWuk94WCrrwslk8yWNGmY1EduTA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
css href 경로는 server.js 에서 이미 prefix해서 css/style.css 만 적기
=> localhost:3000 가면 css 적용된 ejs render 되어 있음
12. New User 클릭 -> add user form
views > add_user.ejs 파일 생성
index.ejs 의 include header & footer 코드 복붙
add_user.ejs에 add_user.html 의 main site 부분 전체 복사 , header & footer 사이에 붙여넣기
add_user.ejs에 복붙한 main site 중 form handling을 파일로 또 빼기
add_user.ejs
<!-- include header -->
<%- include('include/_header.ejs')%>
<!-- /include header -->
<!-- Main site -->
<main id="site-main">
<div class="container">
<div class="box-nav d-flex justify-between">
<div class="filter">
<a href="/"><i class="fas fa-angle-double-left"></i>All Users</a>
</div>
</div>
<div class="form-title text-center">
<h2 class="text-dark">
New User
</h2>
<span class="text-light">
Use the below form to create a new account
</span>
</div>
<!-- add user form -->
<%- include('include/_form.ejs')%>
</div>
</main>
<!-- /Main site -->
<!-- include footer -->
<%- include('include/_footer.ejs')%>
<!-- /include footer -->
server.js /add_user 추가
app.get('/', (req,res)=>{
res.render('index');
})
app.get('/add-user', (req,res)=>{
res.render('add_user');
})
index.ejs 의 New User <a tag> 에 이미 href="add-user" 적어놓음
=> localhost:3000 -> New User 클릭 -> New User (add_user.ejs) render 됨
All Users 돌아가기 버튼 눌르면 '/' 로 돌아가짐!
13. Update (User 정보 수정)
views > update_user.ejs 생성 & add_user.ejs 코드 복붙 & 일부 수정 (form handling부분 다시 가져와서 복붙! )
update_user.ejs
<!-- include header -->
<%- include('include/_header.ejs')%>
<!-- /include header -->
<!-- Main site -->
<main id="site-main">
<div class="container">
<div class="box-nav d-flex justify-between">
<div class="filter">
<a href="/"><i class="fas fa-angle-double-left"></i>All Users</a>
</div>
</div>
<div class="form-title text-center">
<h2 class="text-dark">Update User</h2>
<span class="text-light"> Use the below form to update an account </span>
</div>
<!-- add user form -->
<!-- form handling-->
<form method="POST" id="update_user">
<div class="new_user">
<div class="form-group">
<label for="name" class="text-light">Name</label>
<input type="hidden" name="id" value="" />
<input type="text" name="name" value="" placeholder="Name" />
</div>
<div class="form-group">
<label for="Email" class="text-light">Email</label>
<input
type="text"
name="email"
value=""
placeholder="example@gmail.com"
/>
</div>
<div class="form-group">
<label for="gender" class="text-light">Gender</label>
<div class="radio inline">
<input type="radio" id="radio-2" name="gender" value="Male" />
<label for="radio-2" class="radio-label">Male</label>
</div>
<div class="radio inline">
<input type="radio" id="radio-3" name="gender" value="Female" />
<label for="radio-3" class="radio-label">Female</label>
</div>
</div>
<div class="form-group">
<label for="gender" class="text-light">Status</label>
<div class="radio inline">
<input type="radio" id="radio-4" name="status" value="Active" />
<label for="radio-4" class="radio-label">Active</label>
</div>
<div class="radio inline">
<input type="radio" id="radio-5" name="status" value="Inactive" />
<label for="radio-5" class="radio-label">Inactive</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn text-dark update">Save</button>
</div>
</div>
</form>
</div>
</main>
<!-- /Main site -->
<!-- include footer -->
<%- include('include/_footer.ejs')%>
<!-- /include footer -->
_form.ejs 에 form id = update_user -> add_user로 변경
<!-- form handling-->
<form method="POST" id="add_user">
<div class="new_user">
<div class="form-group">
_show.ejs <a hre="#"> -> "/update-user" 로 변경
<tr>
<td>1</td>
<td>Username</td>
<td>exameple@gmail.com</td>
<td>Female</td>
<td>Active</td>
<td>
<a href="/update-user" class="btn border-shadow update">
<span class="text-gradient"><i class="fas fa-pencil-alt"></i></span>
</a>
<a class="btn border-shadow delete">
<span class="text-gradient"><i class="fas fa-times"></i></span>
</a>
</td>
</tr>
server.js '/update-user' 추가
app.get('/', (req,res)=>{
res.render('index');
})
app.get('/add-user', (req,res)=>{
res.render('add_user');
})
app.get('/update-user', (req,res)=>{
res.render('update_user');
})
편집 icon을 누르면 Update User page가 뜸 ! (Add USer 와 형식 똑같음)
=> 이제 html 파일들(index.html, add_user.html) 삭제해도 좋음!
14. Routes
MONGODB_CRUD > server > routes 폴더 생성 > router.js 파일 생성
server.js 에서 app.get 부분 복사 -> router.js 붙여넣기 + expresss, route 가져오기 + app -> route로 수정 + export
router.js
const express = require('express');
// const app = express(); app을 또 사용 X
const route = express.Router();
route.get('/', (req,res)=>{
res.render('index');
})
route.get('/add-user', (req,res)=>{
res.render('add_user');
})
route.get('/update-user', (req,res)=>{
res.render('update_user');
})
module.exports = route
server.js
.
.
.
// load assets
app.use('/css', express.static(path.resolve(__dirname, "assets/css")));
app.use('/img', express.static(path.resolve(__dirname, "assets/img")));
app.use('/js', express.static(path.resolve(__dirname, "assets/js")));
//load routers
app.use('/', require('./server/routes/router'));
app.listen(PORT, ()=>{
console.log(`Server is running on http://localhost:${PORT}`)
})
이렇게 router 사용할 때 보통 변수로 위의 router를 가져왔는데 여기에선 변수로 담지않고 그냥 바로 사용함
15. router에 callback function 따로 빼기
MONGODB_CRUD > server > services 폴더 생성 > render.js 폴더 생성 + 코드 작성
exports.homeRoutes = (req,res)=>{
res.render('index');
}
router.js
const express = require('express');
// const app = express(); app을 또 사용 X
const route = express.Router();
const services = require('../services/render')
route.get('/', services.homeRoutes)
route.get('/add-user', (req,res)=>{
res.render('add_user');
})
route.get('/update-user', (req,res)=>{
res.render('update_user');
})
module.exports = route
나머지도 바꾸기
render.js
exports.homeRoutes = (req,res)=>{
res.render('index');
}
exports.add_user = (req,res)=>{
res.render('add_user');
}
exports.update_user = (req,res)=>{
res.render('update_user');
}
router.js
const express = require('express');
// const app = express(); app을 또 사용 X
const route = express.Router();
const services = require('../services/render')
/**
* @description Root Route
* @method GET /
*/
route.get('/', services.homeRoutes)
/**
* @description add users
* @method GET /add-user
*/
route.get('/add-user', services.add_user)
/**
* @description for update users
* @method GET /update-user
*/
route.get('/update-user', services.update_user)
module.exports = route
16. MongoDB
local / cloud 중 큰 프로젝트는 cloud 사용하는게 더 좋다고 함 ! local의 경우 모든 데이터를 잃을 수도 있다고...
아래 순서대로 이제 진행하기 !
1) build your cluster
- 구글 로그인 -> new project -> project 이름 설정 -> create -> 해당명으로 project생성됨
- Build a Database 클릭 -> 무료버전 선택 -> aws, singapore 선택 -> 확인 -> cluster 생성 완료
2) Create your database user
왼쪽 Security - Database Access -> Add New Database User 클릭
3) Add IP Address to your Access List
왼쪽 메뉴 - Security - Network Access -> Add IP Address -> allow access from anywhere 클릭
It alows me to access to this database from any localhost
왼쪽 메뉴 Databases - Connect 클릭 -> Connect your application 선택 -> copy your application code
MongoDB my application과 연결하기
Connect to your cluster
vsc 로 돌아가서 MONGODB_CURD > config.env 파일 코드 추가
PORT=3000
MONGO_URI=mongodb+srv://admin:<아까 입력한 user password>@cluster0.loh1o.mongodb.net/<데이터베이스 이름>?retryWrites=true&w=majority
MONGODB_CRUD > server > database 폴더 > connection.js 파일 생성 & 코드 작성
const mongoose = require('mongoose');
const connectDB = async()=>{
try{
// mongoDB connection string
const con = await mongoose.connect(process.env.MONGO_URI,{
useNewUrlParser:true,
useUnifiedTopology:true,
// useFindAndModify:false,
// useCreateIndex:true
});
console.log(`MongoDB connected : ${con.connection.host}`)
}catch(err){
console.log(err.message);
process.exit(1); // 1==true
}
}
module.exports=connectDB;
* 위 두 옵션은 더이상 안쓴다고 함..
server.js
const connectDB = require('./server/database/connection');
// log requests
app.use(morgan("tiny"));
// mongoDB connection
connectDB();
17. API
MONGODB_CRUD > server > model > model.js 파일 생성 + 코드 작성
* schema allows you to define a shape and content of the document
const mongoose = require('mongoose');
let schema = new mongoose.Schema({
name:{
type:String,
required:true
},
email:{
type:String,
require:true,
unique:true
},
gender:String,
status:String
})
const Userdb = mongoose.model('userdb', schema);
module.exports=Userdb;
MONGODB_CRUD > server > controller > controller.js 파일 생성 + 코드 작성
let Userdb = require('../model/model');
// create and save new user
exports.create = (req,res)=>{
}
// retrieve and return all users / retreive and return a single user
exports.find = (req,res)=>{
}
// update a new identified user by userid
exports.update = (req,res)=>{
}
// delete a user with specified userid in the request
exports.delete = (req,res)=>{
}
server > routes > router.js
const controller = require('../controller/controller')
.
.
.
//API
route.post('/api/users', controller.create);
route.get('/api/users', controller.find);
route.put('/api/users/:id', controller.update);
route.delete('/api/users/:id', controller.delete);
mongoDB와 주고받기
CREATE
server > controller > controller.js
exports.create = (req,res)=>{
//validate request / body에 아무것도 없는 경우 예외처리
if(!req.body){
res.status(400).send({message:'Content can not be empty!'});
return;
}
// new user / create an instance
const user = new Userdb({
name:req.body.name,
email:req.body.email,
gender:req.body.gender,
status:req.body.status
})
// save user(the instance made above) in the DB
user
.save(user)
.then(data=>{
res.send(data)
})
.catch(err=>{
res.status(500).send({
message:err.message || "Some error occurred while creating a create operation"
})
})
}
create postman 으로 test 해보기
mongoDB - database - collection 확인해보기
users - 데이타베이스 이름
userdbs - document 이름
FIND (SELECT)
controller.js
exports.find 부분 수정
find() 모든 data return (지금 1개밖에 없어서 하나만 출력)
// retrieve and return all users / retreive and return a single user
exports.find = (req,res)=>{
Userdb.find()
.then(user =>{
res.send(user)
})
.catch(err=>{
res.status(500).send({
message:err.message || "Error Occurred while retrieving user information"
})
})
}
postman으로 test
Get Single User
여러 데이터들 중 특정 1user 만 find / get 해오기
postman 으로 데이터 몇개 더 create 해주기
// retrieve and return all users / retreive and return a single user
exports.find = (req,res)=>{
if(req.query.id){
const id = req.query.id;
Userdb.findById(id)
.then(data=>{
if(!data){
res.status(404).send({
message:`Not found user with ${id}`
})
}else{
res.send(data)
}
})
.catch(err=>{
res.status(500).send({
message:`error retrieving user with ${id}`
})
})
}else{
Userdb.find()
.then(user =>{
res.send(user)
})
.catch(err=>{
res.status(500).send({
message:err.message || "Error Occurred while retrieving user information"
})
})
}
}
postman
params로 id 적으면 해당 id data만 return
UPDATE
controller.js
// update a new identified user by userid
exports.update = (req,res)=>{
if(!req.body){
return res
.status(400)
.send({
message:"Data to update can not be empty"
})
}
const id = req.params.id;
Userdb.findByIdAndUpdate(id,req.body,{useFindAndModify:false})
.then(data=>{
if(!data){
res.status(404).send({
message : `Cannot update user with ${id}. Maybe user not found`
})
}else{
res.send(data)
}
})
.catch(err=>{
res.status(500).send({
message:'Error update user information'
})
})
}
postman
* url 뒤에 id 꼭 붙이기!
원래 actice 였던 상태가 inactive로 update 됨 !
mongoDB 도 변경되어 있음
DELETE
controller.js
exports.delete = (req,res)=>{
const id = req.params.id;
Userdb.findByIdAndDelete(id)
.then(data=>{
if(!data){
res.status(404).send({
message: `Cannot delete with id ${id}.`
})
}else{
res.send({
message:`User was deleted successfully!`
})
}
})
.catch(err=>{
res.status(500).send({
message:`Coule not delete User with id = ${id}`
});
});
}
postman test
mongoDB - 하나 있던 data 삭제됨
18. Using API
application main page에 mongoDB에 있는 모든 데이터 distribute 뿌리기
render.js
exports.homeRoutes = (req,res)=>{
res.render('index', {users:'New Data'});
}
index.ejs
</thead>
<tbody>
<!-- table -->
<%- include('include/_show.ejs')%>
</tbody>
</table>
</form>
<%=users%> // 추가
</div>
</main>
그럼 user라는 변수에 들어간 'New Data' 라는 값이 나온다 (서커 껏다가 켜주기)
이런 식으로 mongoDB에서 가져온 모든 data를 index.ejs에 뿌려보기
render.js
axios로 데이터 가져오기 - promise로 반환
const axios = require('axios');
exports.homeRoutes = (req,res)=>{
// make a get request to /api/users
axios.get('http://localhost:3000/api/users')
.then(function(response){
console.log(`response.data:`,response.data)
res.render('index', {users:response.data});
})
.catch(err=>{
res.send(err);
})
}
index.ejs
뿌릴 테이블은 _show.ejs에 들어가 있다. 이전에 적은 코드( <%=users%>) 삭제 / 원상복귀하기
<tbody>
<!-- table -->
<%- include('include/_show.ejs')%>
</tbody>
</table>
</form>
</div>
</main>
views > include > _show.ejs
users 라는 변수로 받은 내용을 for문으로 뿌리기
<% for(let i=0; i<users.length; i++){%>
<tr>
<td><%=i+1%></td>
<td><%=users[i].name%></td>
<td><%=users[i].email%></td>
<td><%=users[i].gender%></td>
<td><%=users[i].status%></td>
<td>
<a href="/update-user" class="btn border-shadow update">
<span class="text-gradient"><i class="fas fa-pencil-alt"></i></span>
</a>
<a class="btn border-shadow delete">
<span class="text-gradient"><i class="fas fa-times"></i></span>
</a>
</td>
</tr>
<%}%>
html의 <% %>요 문법과 ejs 문법이 좀 다름 ! 잘 보기
19. Create New User
_form.ejs
action 추가
<!-- form handling-->
<form action="/api/users" method="POST" id="add_user">
<div class="new_user">
<div class="form-group">
controller.js - create 부분
// save user(the instance made above) in the DB
user
.save(user)
.then(data=>{
//res.send(data)
res.redirect('/') // redirect로 수정
})
.catch(err=>{
res.status(500).send({
message:err.message || "Some error occurred while creating a create operation"
})
})
}
까지만 하고 add user 해보면 add한 ㅎㄷㄷ user가 잘 들어와있다!
여기서 jQuery로 user 성공적으로 생성되었따고 alert주기
google "jqueyr cdnjs" 검색 아래 들어가서 가장 위의 코드 복사
https://cdnjs.com/libraries/jquery
views > include > _footer.ejs
jquery script 넣고 커스텀 js위한 script 추가
<!-- jquery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- custom js file -->
<script src="js/index.js"></script>
</body>
</html>
MONGODB > assets > js > index.js 생성
server.js에서 이미 경로 저장해놓아서 js/index.js 로만 써도 된다. ↓↓
app.use('/js', express.static(path.resolve(__dirname, "assets/js")));
asstes > js > index.js
$('#add_user').submit(function(event){
alert('Data Inserted Successfully!')
})
처음써보는 jQuery........
이제 add new user하면 alert가 뜬다!
20. Update User
먼저 편집icon클릭 -> add_user.ejs 로 넘어갈 때 기존 user의 정보 불러오기
render.js
exports.update_user = (req,res)=>{
// let id = req.query.id;
// axios.get(`http://localhost:3000/api/users?id=${id}`)
axios.get('http://localhost:3000/api/users', {params:{id:req.query.id}})
.then(function(user_data){
console.log(`user_data.data=`, user_data.data)
res.render('update_user', {user:user_data.data})
})
.catch(err=>{
res.send(err);
})
}
_show.ejs - id pass하기
.
.
.
<td>
<a href="/update-user?id=<%=users[i]._id%>" class="btn border-shadow update">
.
.
.
update_user.ejs
<div class="form-group">
<label for="name" class="text-light">Name</label>
<input type="hidden" name="id" value="<%=user._id%>" />
<input type="text" name="name" value="<%=user.name%>" placeholder="emily" />
</div>
<div class="form-group">
<label for="Email" class="text-light">Email</label>
<input type="text" name="email" value="<%=user.email%>" placeholder="test@test.com" />
</div>
<div class="form-group">
<label for="gender" class="text-light">Gender</label>
<div class="radio inline">
<input type="radio" id="radio-2" name="gender" value="Male" >
<label for="radio-2" class="radio-label">Male</label>
</div>
<div class="radio inline">
<input type="radio" id="radio-3" name="gender" value="Female">
<label for="radio-3" class="radio-label">Female</label>
</div>
</div>
<div class="form-group">
<label for="gender" class="text-light">Status</label>
<div class="radio inline">
<input type="radio" id="radio-4" name="status" value="Active">
<label for="radio-4" class="radio-label">Active</label>
</div>
<div class="radio inline">
<input type="radio" id="radio-5" name="status" value="Inactive" >
<label for="radio-5" class="radio-label">Inactive</label>
</div>
</div>
<div class="form-group">
gender 와 status는 아래처럼 하면되는데 내 코드는 빨간색으로 나오면서 에러가 난다 ! 일단 요 부분 제외하고 넘어가기
assets > js > index.js
$('#update_user').submit(function(event){
event.preventDefault();
// 아래에서 this는 '#update_user'와 같음
let unindexed_array = $(this).serializeArray();
console.log('upindexed arrya=',unindexed_array)
})
update_user.ejs 에서 submit을 눌렀을 때 막기 & console.log 찍기
jquery에서 serializeArray()는 form 에서 <input name="" value=""> 를 객체 직렬화 함
data 라는 {} 객체 만들어서 위의 내용 name 와 value 넣기
$('#update_user').submit(function(event){
event.preventDefault();
// 아래에서 this는 '#update_user'와 같음
let unindexed_array = $(this).serializeArray();
let data = {}
$.map(unindexed_array,function(n,i){
data[n['name']]=n['value']
})
console.log('upindexed array=',unindexed_array)
console.log(data)
})
이제 data 안의 id를 얻어서 put API로 보내기
$('#update_user').submit(function(event){
event.preventDefault();
// 아래에서 this는 '#update_user'와 같음
let unindexed_array = $(this).serializeArray();
let data = {}
$.map(unindexed_array,function(n,i){
data[n['name']]=n['value']
})
console.log('upindexed array=',unindexed_array);
console.log(data);
let request = {
'url':`http://localhost:3000/api/users/${data.id}`,
'method':'PUT',
'data':data,
}
$.ajax(request).done(function(response){
alert('Data updated successfully!')
})
})
-> 수정이 되고 메인 페이지에도 적용된다!
21. Delete User
_show.ejs
data-id 추가
</a>
<a class="btn border-shadow delete" data-id=<%=users[i]._id%>>
<span class="text-gradient"><i class="fas fa-times"></i></span>
</a>
assets > js > index.js
if(window.location.pathname=='/'){
$ondelete = $('.table tbody td a.delete');
$ondelete.click(function(){
let id = $(this).attr('data-id')
let request = {
'url':`http://localhost:3000/api/users/${id}`,
'method':'DELETE'
}
if(confirm("Do you really want to delete this record?")){
$.ajax(request).done(function(response){
alert('Data deleted successfully!');
location.reload();
})
}
})
}
삭제하기까지 완성!
전체 코드 : https://github.com/ohse-emily/CRUD_application_with_nodejs_mongoDB
Reference : https://www.youtube.com/watch?v=W1Kttu53qTg
'mongoDB' 카테고리의 다른 글
[MongoDB] Mongoose Query 총 정리 (0) | 2021.11.15 |
---|---|
[MongoDB] options - lean, new, upsert (0) | 2021.11.15 |
[DB] MongoDB Index 개념과 설계 전략 (0) | 2021.11.12 |
[MongoDB] DAS, NAS, SAN, HDD, SSD, 인덱스란? (0) | 2021.11.11 |
[MongoDB] 몽고디비란? 기본 개념, 장점 단점 / CRUD (2) | 2021.09.23 |