이전 포스팅 -> 2021.10.10 - [capstone] - 캡스톤 일지 ~10.05
캡스톤 일지 ~10.05
전날에는 tensorflow.js를 한번 훑어보고 다음 날 노드 공부를 시작하였다. 이전에 노드 공부하며 들었던 인프런 강의 중 괜찮았던 강의가 몇 개 생각나 다시 들을 예정 나 이렇게나 인프런 잘 이용
juran-devblog.tistory.com
이전에 듣던 TDD 노드 강의 드디어 완강!
1) READ
📄 index.js
// limit만큼 사용자 조회
app.get('/users', (req, res) => {
req.query.limit = req.query.limit || 10;
const limit = parseInt(req.query.limit, 10);
if (Number.isNaN(limit)) {
return res.status(400).end(); 예외1) 숫자가 아닐 경우 400 반환
}
res.json(users.slice(0, limit)); //res.body
});
// 아이디로 사용자 조회
app.get('/users/:id', function(req, res) {
const id = parseInt(req.params.id, 10);
if (Number.isNaN(id)) return res.status(400).end(); // 예외1)숫자가 아닐 경우 400 반환
const user = users.filter((user) => { // 특정 조건에 맞는 객체 array 반환
return user.id === id
})[0];
// const user = users.filter((user) => user.id === id)[0];
if (!user) return res.status(404).end(); // 예외2) 유저가 없을 경우 404 반환
res.json(user);
});
📄 index.spec.js
// limit만큼 사용자 조회
describe('GET /users는', () => {
describe('성공 시', () => {
it('유저 객체를 담은 배열을 응답한다.', (done) => { // 비동기 처리
request(app)
.get('/users')
.end((err, res) => {
console.log(res.body);
res.body.should.be.instanceOf(Array);
done();
});
});
it('최대 limit 갯수만큼 응답한다', (done) => {
request(app)
.get('/users?limit=2')
.end((err, res) => {
console.log(res.body);
res.body.should.have.lengthOf(2)
done();
});
});
});
describe('실패 시', () => {
it('예외1) limit이 숫자형이 아니면 400을 응답한다.', (done) => {
request(app)
.get('/users?limit=two')
.expect(400)
.end(done);
});
});
});
// 사용자 아이디 조회
describe('GET /users/:id는', () => {
describe('성공 시', () => {
it('id가 1인 유저 객체를 반환한다.', (done) => {
request(app)
.get('/users/1')
.end((err, res) => {
console.log(res.body)
res.body.should.have.property('id', 1);
done();
});
});
});
describe('실패 시', () => {
it('예외1) id가 숫자가 아닐 경우 400으로 응답한다', (done) => {
request(app)
.get('/users/one')
.expect(400)
.end(done);
});
it('예외2) id로 유저를 찾을 수 없는 경우 404로 응답한다', (done) => {
request(app)
.get('/users/999')
.expect(404)
.end(done)
});
});
});
2) DELETE
📄 index.js
app.delete('/users/:id', (req, res) => {
const id = parseInt(req.params.id, 10);
if (Number.isNaN(id)) return res.status(400).end(); // 예외1) 숫자가 아닐 경우 400 반환
users = users.filter(user => user.id !== id); // 해당 아이디가 아닌 유저만 반환
res.status(204).end();
});
📄 index.spec.js
describe('DELETE /users/:id', () => {
describe('성공 시', () => {
it('1) 204를 응답한다', (done) => {
request(app)
.delete('/users/1')
.expect(204)
.end(done)
});
});
describe('실패 시', () => {
it('예외1) id가 숫자가 아닐 경우 400으로 응답한다.', (done) => {
request(app)
.delete('/users/one')
.expect(400)
.end(done)
});
});
});
3) CREATE
📄 index.js
app.post('/users', (req, res) => {
const name = req.body.name;
if(!name) return res.status(400).end(); // 예외1) 이름 누락시 400 반환
const isConflic = users.filter(user => user.name === name).length
if (isConflic) return res.status(409).end(); // 예외2) 이름 중복 시 409 반환
const id = Date.now();
const user = {id, name};
users.push(user);
res.status(201).json(user);
});
📄 index.spec.js
describe('POST /users', () => {
describe('성공 시', () => {
let name = 'daniel',
body;
before(done => { // 중복되는 코드 묶어서 처음 한번에 실행
request(app)
.post('/users')
.send({name})
.expect(201)
.end((err, res) => {
body = res.body;
done();
});
});
it('생성된 유저 객체를 반환한다.', () => {
body.should.have.property('id');
});
it('입력한 name을 반환한다.', () => {
body.should.have.property('name', name);
});
});
describe('실패 시', () => {
it('예외1) name 파리미터 누락 시 400을 반환한다.', (done) => {
request(app)
.post('/users')
.send({})
.expect(400)
.end(done)
});
it('예외2) name이 중복일 경우 409를 반환한다.', (done) => {
request(app)
.post('/users')
.send({name: 'daniel'})
.expect(409)
.end(done)
});
});
});
4) UPDATE
📄 index.js
app.put('/users/:id', (req, res) => {
const id = parseInt(req.params.id, 10);
if (Number.isNaN(id)) return res.status(400).end(); // 예외1) 숫자가 아닐 경우 400 반환
const name = req.body.name;
if(!name) return res.status(400).end(); // 예외2) 이름 누락시 400 반환
const isConflic = users.filter(user => user.name === name).length // 예외4) 이름 중복시 409 반환
if (isConflic) return res.status(409).end();
const user = users.filter(user => user.id === id)[0];
if (!user) return res.status(404).end(); // 예외3) 없는 유저일때 404 반환
user.name = name;
res.json(user);
});
📄 index.spec.js
describe('PUT /users/:id', () => {
describe('성공 시', () => {
it('변경된 name을 응답한다.', (done) => {
const name = 'chally';
request(app)
.put('/users/3')
.send({name})
.end((err, res) => {
res.body.should.have.property('name', name);
done();
});
});
});
describe('실패 시', () => {
it('예외1) 정수가 아닌 id인 경우 400을 반환한다.', (done) => {
request(app)
.put('/users/one')
.expect(400)
.end(done)
});
it('예외2) name이 누락될 경우 400를 반환한다.', (done) => {
request(app)
.put('/users/one')
.send({})
.expect(400)
.end(done)
});
it('예외3) 없는 유저일 경우 404를 응답한다.', (done) => {
request(app)
.put('/users/999')
.send({name: 'foo'})
.expect(404)
.end(done)
});
it('예외4) name이 중복될 경우 400를 반환한다.', (done) => {
request(app)
.put('/users/3')
.send({name: 'bek'})
.expect(409)
.end(done)
});
});
});
* req.body -> express 모듈에서는 body 지원하지 않음으로 body-parser, multer(이미지 데이터 처리)등의 미들웨어 필요
http://expressjs.com/ko/api.html#req.body
Express 4.x - API 참조
Express 4.x API express() Creates an Express application. The express() function is a top-level function exported by the express module. var express = require('express') var app = express() Methods express.json([options]) This middleware is available in Ex
expressjs.com
express4.x 이전
const bodyParser = require('body-parser');
app.user(bodyParser.json());
app.user(bodyParser.urlencoded({ extended: true}));
express4.x 이후부터는 body parser 기본 제공
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
* 테스트코드 .only() 해당 부분만 테스트 실행
1) 🗂 api > 🗂 user > 📄 index.ctrl.js
라우팅 작업에 필요한 메서드를 작성한 컨트롤러
// 컨트롤러
var users = [
{id: 1, name: 'alice'},
{id: 2, name: 'bek'},
{id: 3, name: 'chris'},
]
const index = function (req, res) {
req.query.limit = req.query.limit || 10;
const limit = parseInt(req.query.limit, 10);
if (Number.isNaN(limit)) {
return res.status(400).end();
}
res.json(users.slice(0, limit));
};
const show = function(req, res) {
const id = parseInt(req.params.id, 10);
if (Number.isNaN(id)) return res.status(400).end(); // 예외1) 숫자가 아닐 경우 400 반환
const user = users.filter((user) => user.id === id)[0];
if (!user) return res.status(404).end(); // 예외2) 유저가 없을 경우 404 반환
res.json(user);
}
const destroy = (req, res) => {
const id = parseInt(req.params.id, 10);
if (Number.isNaN(id)) return res.status(400).end();
users = users.filter(user => user.id !== id);
res.status(204).end();
}
const create = (req, res) => {
const name = req.body.name;
if(!name) return res.status(400).end(); // 예외1) 이름 누락
const isConflic = users.filter(user => user.name === name).length // 예외2) 이름 중복
if (isConflic) return res.status(409).end();
const id = Date.now(); // db에서 아이디 생성
const user = {id, name};
users.push(user);
res.status(201).json(user);
}
const update = (req, res) => {
const id = parseInt(req.params.id, 10);
if (Number.isNaN(id)) return res.status(400).end(); // 예외1) 정수가 아닌 아이디
const name = req.body.name;
if(!name) return res.status(400).end(); // 예외2) 이름 누락
const isConflic = users.filter(user => user.name === name).length // 예외4) 이름 중복
if (isConflic) return res.status(409).end();
const user = users.filter(user => user.id === id)[0];
if (!user) return res.status(404).end(); // 예외3) 없는 유저
user.name = name;
res.json(user);
}
module.exports = {
// index: index,
// show: show,
// destroy: destroy,
// create: create,
// update: update
index, show, destroy, create, update // ES6 지원
}
2) 🗂 api > 🗂 user > 📄 index.js
user 관련 작업을 모아둔 user 폴더의 메인 파일로 사용하는 url에 대한 라우팅 설정
// '/users/...' api 라우팅 설정
const express = require('express');
const router = express.Router();
const ctrl = require('./user.ctrl');
router.get('/', ctrl.index);
router.get('/:id', ctrl.show);
router.delete('/:id', ctrl.destroy);
router.post('/', ctrl.create);
router.put('/:id', ctrl.update);
module.exports = router; // router 객체 export
3) 📄 index.js
생성한 user 라우터 메인 index.js에서 호출
const user = require('./api/user'); // 해당 폴더에서 index.js 알아서 가져옴
app.use('/users', user); // /users 관련 라우팅 모두 담당
📑 package.json
{
"scripts": {
"test": "NODE_ENV=test mocha api/user/user.spec.js",
"start": "node index.js"
},
...
}
테스트 실행 시 노드 환경변수 생성하여 테스트 환경임을 알려줌
📑 index.js
if (process.env.NODE_ENV !== 'test') {
app.use(morgan('dev'));
}
테스트 환경일때만 morgan 로그 남김
npm i sqlite3 --save
npm i sequelize --save
사용할 데이터베이스는 파일 형태의 데이터베이스 sqlite3 (이전에 파이썬할때도 써봤었음) https://www.npmjs.com/package/sqlite3
노드에서 ORM 사용하기 sequlize https://www.npmjs.com/package/sequelize
sequelize
Multi dialect ORM for Node.JS
www.npmjs.com
📄 models.js
const Sequelize = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite', // 파일 형식의 데이터베이스 sqlite
storage: './db.sqlite',
// logging: false
});
const User = sequelize.define('User', {
name: {
type: Sequelize.STRING,
unique: true // 제약조건
},
});
module.exports = {Sequelize, sequelize, User};
사용할 데이터베이스 연동과 객체 생성
🗂 bin > 📄 sync-db.js
const models = require('../models');
module.exports = () => {
const options = {
force: process.env.NODE_ENV === 'test' ? true : false
};
return models.sequelize.sync({options});
}
데이터베이스 동기화
- force: true 테스트 환경일 때(package.json 스크립트에서 테스트 시 노드 환경변수를 test로 설정) 동기화할때마다 DB 초기화
- force: false 동기화할때마다 DB 초기화하지 않고 내용 유지
🗂 bin > 📄 app.js
const app = require('../index');
const syncDB = require('./sync-db');
const port = 3000;
syncDB().then(_ =>{ // 비동기
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
})
index.js에서 앱 구동부분을 빼고 해당 스크립트에서 데이터베이스 동기화와 앱 구동 한번에 실행
-> 📄 package.json scripts
"scripts": {
"test": "...",
"start": "node bin/www.js"
},
🗂 api > 🗂 user > 📄 index.ctrl.js 기존의 사용자 CRUD 코드 수정
const models = require('../../models');
1) READ
// limit만큼 조회
models.User.findAll({
limit: limit
})
.then(users => {
res.json(users);
});
// 아이디로 사용자 조회
models.User.findOne({
where: {
id: id
}
}).then(user => {
if (!user) return res.status(404).end();
res.json(user);
});
2) DELETE
models.User.destroy({
where: {id}
}).then(() => {
res.status(204).end();
})
3) CREATE
models.User.create({name})
.then(user => { // 해당 이름을 가진 유저 생성
res.status(201).json(user);
})
.catch(err => {
if (err.name === 'SequelizeUniqueConstraintError') {
return res.status(409).end();
}
res.status(500).end();
});
models.js에서 작성한 unique: true 조건에 의해 입력값이 중복인 경우 SequelizeUniqueConstraintError 발생 -> 409 반환
4) UPDATE
models.User.findOne({where : {id}}) // 해당 이이디의 유저 찾음
.then(user => {
if(!user) return res.status(404).end();
user.name = name; // 이름 변경
user.save()
.then(_=> {
res.json(user);
})
.catch(err => {
if (err.name === 'SequelizeUniqueConstraintError') {
return res.status(409).end();
}
res.status(500).end();
});
});
테스트 코드 모두 통과하면 끝!
노드 강의 드디어 완강해따
ORM 시퀄라이저 안쓰고 그냥 DB connection에 작성한 쿼리 실행하도록 할것 같긴 한디..
그래도 알아두면 좋으니까 또 혹시몰라 쓰게될지도
노드 재밌구먼 자바는 언제 다시 시작하지..
[캡스톤 일지] ~ 11.04(1) 웹 쿠키 이용한 장바구니 기능 구현 (0) | 2021.11.08 |
---|---|
[캡스톤 일지] ~10.26 미디어파이프 오류 해결과 노드 socket.io 예제 (0) | 2021.10.27 |
[캡스톤 일지] ~10.16 robotjs 이용한 마우스 커서 이동 (0) | 2021.10.16 |
[캡스톤 일지] ~10.13 미디어파이프 이용한 정적제스처 인식과 문제 발생 (0) | 2021.10.14 |
[캡스톤 일지] ~10.05 NodeJS TDD 웹개발 훑어보기(1) (0) | 2021.10.10 |
댓글 영역