1. Sequelize 설치
> npm i sequelize mysql2
> npm i -g sequelize-cli (한번만)
> sequelize init
config
migrations
models
seeders
2. MySQL DB 스키마 생성
SCHEMAS 탭에서 우측마우스 클릭 Create schemas 선택
-Name 입력
-utf8mb4 / utf8mb4_unicode_ci 선택
-> CREATE SCHEMA `gowoon_chat` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ;
3. dotenv 설정 및 DB 연결
.env 파일 생성
> npm i dotenv
# 포트설정
PORT=3001 #개발환경 포트 : bin/www process.env.PORT
WAS_PORT=3000 #실제서비스 포트
NODE_ENV=development # models/index process.env.NODE_ENV
# 시크릿키
JWT_SECRET=gowoon427520
COOKIE_SECRET=gowoon427520
MYSQL_AES_KEY=AFDKFKDWYIRDFDFDNCGMFKDHXTPEREF!MDLLJFDS$
# DB환경설정
# development
DB_USER = root
DB_PASS = 427520
DB_NAME =gowoon_chat
DB_HOST = 127.0.0.1
DB_PORT = 3306
# test
TEST_DB_USER = root
TEST_DB_PASS = 427520
TEST_DB_NAME = modu_chat
TEST_DB_HOST = 127.0.0.1
TEST_DB_PORT = 3306
# production
PRO_DB_USER = root
PRO_DB_PASS = 427520
PRO_DB_NAME = modu_chat
PRO_DB_HOST = 127.0.0.1
PRO_DB_PORT = 3306
#AWS S3 스토리지 연결정보
S3_BUCKET=hangowoon-bucket
S3_ACCESS_KEY_ID=AKIAYECRYAMHR5ZPV5ES
S3_ACCESS_SECRET_KEY=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
app.js에서 추가 설정
//dotenv 어플리케이션 환경설정관리 팩키지 참조 및 구성하기
require('dotenv').config();
1) config.json 파일 수정
"development": {
"username": "root",
"password": "******",
"database": "gowoon_chat",
"host": "127.0.0.1",
"dialect": "mysql"
},
index.js에서 적용
//DB연결 환경설정정보 변경처리//관련정보 수정
//const config = require('../config/config.js')[env];
const config = require(path.join(__dirname,'..','config','config.json'))[env];
2) config.js 파일 생성
module.exports = {
"development":{
"username": process.env.DB_USER,
"password": process.env.DB_PASS,
"database": process.env.DB_NAME,
"host": process.env.DB_HOST,
"port": Number(process.env.DB_PORT),
"dialect": "mysql",
"timezone": "+09:00"
},
"test": {
"username": process.env.TEST_DB_USER,
"password": process.env.TEST_DB_PASS,
"database": process.env.TEST_DB_NAME,
"host": process.env.TEST_DB_HOST,
"port": Number(process.env.TEST_DB_PORT),
"dialect": "mysql"
},
"production": {
"username": process.env.PRO_DB_USER,
"password": process.env.PRO_DB_PASS,
"database": process.env.PRO_DB_NAME,
"host": process.env.PRO_DB_HOST,
"port": Number(process.env.PRO_DB_PORT),
"dialect": "mysql"
}
}
index.js에서 적용
//DB연결 환경설정정보 변경처리//관련정보 수정
const config = require('../config/config.js')[env];
//const config = require(path.join(__dirname,'..','config','config.json'))[env];
4. Model 환경 설정
models / index.js 내용 작성
const path = require('path');
const Sequelize = require('sequelize');
//개발모드 환경설정
const env = process.env.NODE_ENV || 'development';
//DB연결 환경설정정보 변경처리//관련정보 수정
const config = require(path.join(__dirname,'..','config','config.json'))[env];
//데이터 베이스 객체
const db= {};
//DB연결정보로 시퀄라이즈 ORM 객체 생성
const sequelize = new Sequelize(config.database,config.username,config.password,config);
//DB 처리 객체에 시퀄라이즈 정보 맵핑처리
//이후 DB객체를 통해 데이터 관리가능해짐
db.sequelize = sequelize; //DB연결정보를 포함한 DB제어 객체속성(CRUD)
db.Sequelize = Sequelize; //Sequelize팩키지에서 제공하는 각종 데이터 타입 및 관련 객체정보를 제공함
//회원모델 모듈파일 참조하고 db속성정의하기
//db.Member = require('./member.js')(sequelize,Sequelize);
//db객체 외부로 노출하기
module.exports = db;
app.js 추가 설정
//// ORM 기반 DB 연결 정보 참조하기 / DB객체 안에 Sequelize 불러옴
var sequelize = require('./models/index').sequelize;
var app = express();
//// mysql과 자동연결처리 및 모델기반 물리 테이블 생성처리제공
sequelize.sync();
5. MVC 패턴 : Model
models / article.js 생성
module.exports = function(sequelize, DataTypes){
return sequelize.define('article',
{
article_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
comment: '게시글고유번호',
},
board_type_code: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '게시판고유번호 1:공지사항게시판,2:일반사용자 게시판',
},
title: {
type: DataTypes.STRING(200),
allowNull: false,
comment: '글제목',
},
article_type_code: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '게시글유형코드 0:일반게시글 1:고정게시글',
},
contents: {
type: DataTypes.TEXT,
allowNull: true,
comment: '글내용',
},
view_count: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '조회수',
},
ip_address: {
type: DataTypes.STRING(50),
allowNull: false,
comment: '작성자IP주소',
},
is_display_code: {
type: DataTypes.TINYINT,
allowNull: false,
comment: '게시여부 0:게시안함 1:게시함',
},
reg_date: {
type: DataTypes.DATE,
allowNull: false,
comment: '등록일시',
},
reg_member_id: {
type: DataTypes.INTEGER,
allowNull: false,
comment: '등록자고유번호',
},
edit_date: {
type: DataTypes.DATE,
allowNull: true,
comment: '수정일시',
},
edit_member_id: {
type: DataTypes.INTEGER,
allowNull: true,
comment: '수정자고유번호',
}
},
{
sequelize,
tableName: 'article', // 기본 테이블명 옵션이 복수형이 아닌 여기 지정한 테이블명으로 생성됨
timestamps: false,
comment: '게시글정보',
indexes: [
{
name: 'PRIMARY',
unique: true,
using: 'BTREE',
fields: [{ name: 'article_id' }], // 여러개의 컬럼이 프라이머리키인경우(복합키){}추가하여 설정가능
},
],
});
}
models / index.js 추가 설정
//회원모델 모듈파일 참조하고 db속성정의하기
db.Article = require('./article.js')(sequelize,Sequelize);
6. MVC 패턴 : Controller (Router)
라우팅 구현
7. MVC 패턴 : View
뷰 구현
8. C/R/U/D ORM DB 프로그래밍
1) ORM db 객체 선언
routes/ article.js
// ORM DB객체 참조
var db = require('../models/index');
2) 게시글 생성하기
let registedArticle = await db.Article.create(article);
routes/ article.js
//신규 목록 페이지 호출
//http://localhost:3001/article/create
router.get('/create',async(req,res,next)=>{
res.render('article/create');
});
//신규 목록 작성 완료 후 목폭 페이지 이동처리
//http://localhost:3001/article/create
router.post('/create',async(req,res,next)=>{
// 작성 목록 추출
// view 파일에서 추출 데이터 확인
let title = req.body.title;
let contents = req.body.contents;
let articleTypeCode = req.body.articleTypeCode;
let isDisplayCode = req.body.isDisplayCode;
// DB 테이블에 저장할 json 단일데이터 구조 정의
// 속성명은 데이터 모델(models/article.js)의 속성명과 동일
let article ={
board_type_code:2,
title,
article_type_code:articleTypeCode,
contents,
view_count:0,
ip_address:"111,111,111,111",
is_display_code:isDisplayCode,
reg_date:Date.now(),
reg_member_id:0,
edit_date:Date.now(),
edit_member_id:0
}
// 신규 게시글 정보가 등록
// create()메소드는 등록된 db의 데이터를 반환
let registedArticle = await db.Article.create(article);
// 작성 환료 후 목록 페이지 이동
res.redirect('/article/list');
});
3) 전체 게시글 목록 불러오기
let article = await db.Article.findAll();
routes/ article.js
//게시글 목폭 페이지 호출
//http://localhost:3001/article/list
router.get('/list',async(req,res,next)=>{
// article 테이블의 모든 게시글 목록을 조회해옴
let article = await db.Article.findAll();
// 조회결과 모든 게시글 데이터를 뷰에 전달
res.render('article/list',{ articles });
});
4) 생성된 게시글 뷰에 연결하기
view/ article.ejs
<tbody class="hoverTblBody">
<% for(var i =0;i < articles.length;i++){ %>
<tr>
<td><%=articles.length-i%></td>
<td><a href="/article/modify/<%=articles[i].article_id%>"><%=articles[i].title%></a></td>
<td>
<% if(articles[i].article_type_code == 0){%>
일반 게시글
<%} else {%>
고정 게시글
<%} %>
</td>
<td><%=articles[i].view_count%></td>
<td><%=articles[i].ip_address%></td>
<td>
<% if(articles[i].is_display_code == 1){%>
게시함
<%} else {%>
게시안함
<%} %>
</td>
<td><%=moment(articles[i].edit_date).format('YYYY-MM-DD HH:mm:ss')%></td>
</tr>
<% } %>
</tbody>
***날짜 패키지 추가 설치***
> npm i moment
moment 패키지 참조하기
routes / article.js
// 날짜 변환 패키지 참조
var moment = require('moment');
moment 속성 전달하기
routes / article.js
//게시글 목폭 페이지 호출
//http://localhost:3001/article/list
router.get('/list',async(req,res,next)=>{
// article 테이블의 모든 게시글 목록을 조회해옴
let articles = await db.Article.findAll();
// 조회결과 모든 게시글 데이터를 뷰에 전달
res.render('article/list',{ articles,moment });
});
5) 단일게시글 수정하기
라우터 설정
단일게시글 불러오기
let article = await db.Article.findOne({
where:{article_id : articleId }
});
routes / article.js
//수정 목록 페이지 호출
//http://localhost:3001/article/modify/1
router.get('/modify/:aid',async(req,res,next)=>{
// 게시글 고유번호 추출
let articleId = req.params.aid;
// 게시글 고유번호를 기준으로 단일 게시글 정보 조회
let article = await db.Article.findOne({
where:{article_id : articleId }
});
// db데이터 받아서 뷰에 전달
res.render('article/modify',{ article, moment });
});
뷰 설정
단일게시글 불러오기
view/ article/ modify.ejs
<form action="/article/modify/:aid" method="post" id="articleCreateForm">
<div class="ibox-content m-b-sm border-bottom">
<!-- 1번째 열 -->
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">게시글 제목</label>
<input type="text" name="title" id="title" class="form-control" value="<%=article.title%>">
</div>
</div>
</div>
<!-- 2번째 열 -->
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">글내용</label>
<textarea class="form-control" name="contents" id="contents" rows="10" cols="5"><%=article.contents%></textarea>
</div>
</div>
</div>
<!-- 3번째 열 -->
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<label class="control-label">게시글유형</label>
<select name="articleTypeCode" id="articleTypeCode" class="form-control">
<option value="0" <% if(article.article_type_code == 0){%> selected<%} %> >일반 게시글</option>
<option value="1" <% if(article.article_type_code == 1){%> selected<%} %> >고정 게시글</option>
</select>
</div>
</div>
<div class="col-sm-4">
<div class="form-group">
<label class="control-label">게시여부</label>
<select name="isDisplayCode" id="isDisplayCode" class="form-control">
<option value="0" <% if(article.is_display_code == 0){%> selected<%} %> >게시안함</option>
<option value="1" <% if(article.is_display_code == 1){%> selected<%} %> >게시함</option>
</select>
</div>
</div>
<div class="col-sm-4">
<div class="form-group">
<label class="control-label">작성일</label>
<input type="text" class="form-control" disabled value="<%=moment(article.edit_date).format('YYYY-MM-DD HH:mm:ss')%>">
</div>
</div>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary">저장</button>
<a href="/article/list" class="btn btn-info">목록</a>
<a href="#" class="btn btn-danger">삭제</a>
</div>
</div>
</form>
라우터 설정
단일게시글 수정하기
let updatedCnt = await db.Article.update(updateArticle,{
where: {article_id:articleId}
});
routes / article.js
// 수정 목록 작성 완료 후 목록 페이지 이동 처리
//http://localhost:3001/article/modify/1
router.post('/modify/:aid',async(req,res,next)=>{
// 게시글 고유번호 추출
let articleId = req.params.aid;
// 수정할 데이터 변수 할당
let title = req.body.title;
let contents = req.body.contents;
let articleTypeCode = req.body.articleTypeCode;
let isDisplayCode = req.body.isDisplayCode;
// 수정하고자 하는 단일 데이터 정의
let updateArticle = {
title,
article_type_code:articleTypeCode,
contents,
ip_address:"222.222.222.222",
is_display_code:isDisplayCode,
edit_date:Date.now(),
edit_member_id:0,
}
//단일 게시글 정보 수정처리 ORM
let updatedCnt = await db.Article.update(updateArticle,{
where: {article_id:articleId}
});
//수정 완료 후 목록 페이지 이동
res.redirect('/article/list');
});
6) 단일게시글 삭제하기
삭제 버튼 뷰 설정
<div class="text-center">
<button type="submit" class="btn btn-primary">저장</button>
<a href="/article/list" class="btn btn-info">목록</a>
<a href="#" id="btnDelete" class="btn btn-danger">삭제</a>
<input type="hidden" id="articleId" value="<%=article.article_id%>"/>
</div>
삭제버튼 스크립트 설정
<script>
$("#btnDelete").click(function(){
if(confirm("해당 게시글을 삭제하시겠습니까?")){
// 확인 팝업내 확인 버튼을 누르면 하기 코드가 실행
// 취소를 투르면 하기 콛를 건너뜀
location.href="/article/delete?aid="+$("#articleId").val();
}
});
</script>
삭제 라우터 설정
// 목록 삭제 후 목록 페이지 이동 처리
router.get('/delete',async(req,res,next)=>{
// 삭제 고유번호 추출
let articleId = req.query.aid;
// DB 삭제 처리
let deletedCnt = await db.Article.destroy({
where:{article_id:articleId}
});
// 삭제 완료 후 목록 페이지로 이동
res.redirect('/article/list');
});