컴퓨터를 공부하고자 마음먹은지 N일차
[131일차]crud방명록 만들기-2 sequelizeORM 리팩토링하기 본문
이전글: api만들기 (express)
구조 설명
우선 내가 만들 api에 사용할 메소드는,
get post put 그리고 delete메소드다.
보안상 문제 해결하기
기존 http모듈을 쓰면, password를 실어서 post요청을 보낸다면,
중간자 공격 에 취약하다. 중간에서 데이터를 가로챈다면,
게시글 삭제나, 수정 에 이용한 password정보가 그대로 탈취될것이다.
그래서 express에서 https모듈을 사용하여 취약한 보안을 보충한다.
https모듈을 사용할 때에는 mkcert를사용해서 인증서를 받아올 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | const express = require("express"); const server = express(); const bodyParser = require("body-parser"); const cors = require("cors"); const morgan = require("morgan"); const https = require("https"); const router = require("./router"); const fs = require("fs") const port = 4000; //body-parser설정 server.use(bodyParser.json()); //클라이언트에 해당하는 주소만 포함하고 get, post, put, delete, option메소드만 허용! server.use(cors({ "Access-Control-Allow-Origin": "https://127.0.0.1:3000", "methods": "GET,PUT,POST,DELETE,OPTIONS" })); //기록용 server.use(morgan('dev')); //라우터사용 server.use('/posts', router); //https 통신 사용 https .createServer( { key: fs.readFileSync('/Users/parkjisang/Desktop/dev/key.pem', 'utf-8'), cert: fs.readFileSync('/Users/parkjisang/Desktop/dev/cert.pem', 'utf-8'), }, server ) .listen(port); | cs |
데이터베이스를 활용해서 리팩토링하기
기존에 있던 데이터 방식은, 서버를 실행시키는 스크립트 내에서
계속 참조값(배열 또는객체)에다 값을 변경만 계속 해주고 있었다.
즉 서버를 실행시키는 스크립트가 꺼지면 할당한 데이터도 사라진다.
그래서 fileSystem모듈을 활용해 특정파일에다 직접 기록 하는 방법도 있지만,
그 또한 데이터 구조에 한계가 있다.
그래서 sequelize orm을 활용해 데이터베이스에 접근하는 방식으로 리팩토링 할것이다.
사실상 리팩토링이아닌 전체적으로 갈아엎어야한다.
시퀄라이즈 cli 를활용하면 아주 간편하게 데이터베이스와 연결할 수 있다. 또한 mvc모델을 설계하는데 아주 용이하다.
테이블 구조 짜기
이전 글에서 보면 알겠지만 , 내가 만든서버에는 회원가입도 필요없다.
이름과 비밀번호 입력란, 그리고 내용을 입력할 뿐이다.
각커멘트마다 primary key를 가지게 한다.
id | name | password | comment |
---|---|---|---|
1 | '코공' | 'ab123' | '코공코공' |
이런식으로 게시글이 생성 되면 INSERT TO 명령이 일어나고
삭제 되면 DELETE 명령어, 수정되면 UPDATE,
그리고 읽어올떄는 SELECT id, name, comment FROM table를 불러올것이다.
기존서버에 sequalize ORM 입히기
우선 npm으로 mysql2와 sequelize를 설치해줘야한다.
sequelize-cli까지 설치가 되면,
프로젝트 폴더에 가서 부트스트랩핑해준다.
npx sequelize-cli init
부트스트래핑하면, mvc모델의 형태대로 디렉토리를 나누어준다.
앞에 구상한 테이블 형태대로 모델을 만들어준다.
그럼 우리가 가장 먼저해야 할 짓은,
config를 수정하는것이다.
config파일은 js파일로 만들어서 환경변수를 활용할 수 있다.
config파일 수정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | require("dotenv").config(); module.exports ={ "development": { "username": "root", "password": process.env.MYSQL_PASSWORD, "database": "simple-crud", "host": "127.0.0.1", "dialect": "mysql" }, "test": { "username": "root", "password": process.env.MYSQL_PASSWORD, "database": "simple-crud", "host": "127.0.0.1", "dialect": "mysql" }, "production": { "username": "root", "password": process.env.MYSQL_PASSWORD, "database": "simple-crud", "host": "127.0.0.1", "dialect": "mysql" } } | cs |
모델만들기
config파일을 저러한 형태대로 수정이 끝났다면,
model을 만들어준다.
sequelize-cli에서는 id(자동으로 증가하고 프라이머리키임),createdAt칼럼을 만들어준다.
그래서 cli명령어를 통해 만들 칼럼은, name, password, comment이다.
셋다 문자열로 저장할거임.
여기서 유의할 점은 만약 테이블 이름을 posts로 할거다? 그러면,
모델이름을 post로 해줘야한다
post모델로 생성된 데이터의 모음이라 해서 migration을 진행하면,
__자동으로 뒤에 복수형 's'를 붙여준다.
그건 좀있다 지켜보자구!
npx sequelize-cli model:generate --name post --attributes name:string,password:string,comment:string
이 명령어를 보면 대충 어떤형태로 내가 원하는 테이블을 만들 때,
요긴하게 사용가능 할지 짐작이 가능할거다.
성공적으로 모델이 만들어졌다고 뜨고, 모델로 향해보면,
이렇게 post라는 모델이 생긴걸 확인할 수 있다.
migration 진행하기
아까 post라는 모델을 만들었으나,
아직까진 내 db에 직접 영향이 가지는 않는다.
내 db까지 영향을 주게하려면 어떻게 해야될까?
답은 migration다!
npx sequelize-cli db:migrate
위 명령어를 실행시켜 진행해보자.
그리고 내 데이터베이스에 들어가서 어떤 일이 벌어졌는지 확인해보자.
일단 뭔가 성공했다는 메세지를 볼 수 있다.
어머나?!?! posts라는 테이블이 만들어진걸 확인가능하다.
테이블의 상태를 봐도 친절하게 updatedAt까지 만들어 준걸 볼 수있다.
게다가 id라는 칼럼을 넣고 primary키로 설정하는것은 물론 auto increment 까지 해준다! 대박!
만들어진 model을 토대로 controller만들기(mvc모델)
mvc모델에 대해서 간략하게 설명하자면,
controller는 model에게 명령을 내린다.
model은 database를 건든다.
지금의 기능을 구현하기 위해서는 이정도설명으로 족하다.
모델은 cli가 이미 만들어줬다. 이게 orm의 편리함이다.
그렇다면 우리는 controller를 잘 만들어주기만하면된다.
controller에서 요청과 응답을 전부처리해버리자. 아니면 콜백도 쓰고 여간 복잡한게 아니다.
라우터코드
라우터에서 요청과 응답을 처리하려면 복잡하게 왔다갔다해야함.
그래서 controler가 만들어졌다 치고 코드를 짜보면,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const express = require("express"); const router = express.Router(); const controler = require("../controler"); //모든 포스트 불러오기 router.get('/', controler.get); //새로운 게시물 작성 router.post('/write' ,controler.write); //수정할 때 패스워드 확인 router.post('/modify', controler.postPw); //삭제할 때 패스워드 확인 router.post('/delete', controler.postPw); //수정 router.put('/modify', controler.modify); //삭제 router.delete('/delete', controler.delete) module.exports = router; | cs |
컨트롤러 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | const {post} = require("../models"); module.exports = { get: async (req, res)=>{ /* attributes를 설정해준 이유는 절대로 클라이언트는 비밀번호같은 개인정보를 받아선 안된다. 또한 다른데이터는 굳이 쓸 일이 없어서다 select name,comment from posts와 똑같다 */ const result = await post.findAll({ attributes: ['name', 'comment'] }); res.send(result); }, write: async (req, res)=>{ //body를 구조분해 할당한다. const {name, password, comment} = req.body; //posts테이블에 요청받은 body를 기준으로 새로운데이터를 생성한다. await post.create({ name, password, comment }); res.status(201).send('ok'); }, //수정 삭제에서 비밀번호 확인 postPw: async (req, res)=>{ const {id, password} = req.body; const result = await post.findOne({ //비밀번호검증을 한번 거쳤기때문에 패스워드를 던져줘도 괜찮 attributes:['id','name','password','comment'], where:{ id,password } }) if(result){ res.status(200).send(result); }else{ res.status(400).send('invalid'); } }, //수정 modify: async (req, res)=>{ const {id, name, password, comment} = req.body; await post.update({ name,password,comment },{ where:{ id } }) res.send('수정완료'); }, //삭제작업은 신중하기에 password를 한번 더 확인하는걸 추천 delete: async (req, res)=>{ await post.destroy({ where:{ id:req.body.id, } }) res.send('삭제완료') }, } | cs |
sequelize ORM은 mvc모델로 앱을 설계할때 특화돼있는걸 볼수있다.
실제로 이렇게 중대한사항인 삭제와 수정 을처리할 때 별다른 보안조치를 하지않으면,
아무나 글을 막 삭제하고 수정하는 사태가 발생할 수 있다.
직접 실습할 때는 쿠키와 세션 그리고 토큰등을 통해 좀더 보안을 올려보자
'🗄Database' 카테고리의 다른 글
[225일차]인덱스 (0) | 2021.04.26 |
---|---|
[198일차]물리삭제와 논리삭제 (0) | 2021.03.31 |