일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 자바기초
- node
- nodeJS
- 웹해킹
- mongoose
- 웹기초
- Express
- materialize
- 자바
- 자바문제풀이
- 그래프
- 이진트리
- gitbash
- 워게임
- CTF
- bootstrap
- 포렌식
- 웹개발
- 웹해킹기초
- 워게임추천
- 써니나타스
- GIT
- node.js
- 포렌식워게임
- NavBar
- 이진탐색트리
- 자료구조
- MongoDB
- wargame.kr
- 뷰
- Today
- Total
보안 전공생의 공부
게시판 만들기 / User 생성, 수정 시 error 처리 (1) 본문
· flash : 변수처럼 이름과 값(문자열, 숫자, 배열, 객체 등 어떠한 형태의 값이라도 사용 가능)을 저장할 수 있는데,
한 번 생성되면 사용될 때까지 서버 메모리상에 저장이 되어 있다가 한 번 사용되면 사라지는 형태의 data
-> connect-flash package를 이용하여 flash
· regex(Regular Expression, 정규 표현식) : 특정규칙을 가진 문자열의 집할을 표현하는 데 사용하는 형식 언어
(출처 :https://ko.wikipedia.org/wiki/%EC%A0%95%EA%B7%9C_%ED%91%9C%ED%98%84%EC%8B%9D)
문자열이 특정한 형식을 가지고 있는지 아닌지를 판단하기 위해 사용함
->User의 username, password, name, email 항목들이 특정한 형식의 값만 저장할 수 있도록 함
· package 설치
express-session은 connect-flash를 실행하기 위해 필요한 package이다.
· User.js 수정
- User schema
trim은 문자열 앞뒤에 빈칸이 있는 경우 제거해주는 옵션
match에 regex가 들어가서 값이 regex에 부합하지 않으면 에러메시지를 낸다.
◆ 정규표현식 : 문자열에 특정한 규칙에 맞는 문자열이 있는지 알아보는 표현식
1. 정규식 리터럴("/"로 감싸는 패턴) 사용
/^.{4,12}$/
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
스크립트가 불러와질 때 컴파일됨
-> 정규식이 상수일 때 사용
2. RegExp객체의 생성자 함수 호출
RegExp("^.{4,12}$")
RegExp("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
정규식이 실행되는 시점에 컴파일됨
-> 정규식의 패턴이 변경될 수 있는 경우 or 사용자 입력과 같이 다른 출처로부터 패턴을 가져와야 하는 경우에 사용
◆ 정규표현식에서의 특수문자
^ : 문자열의 시작 위치에 대응(줄 바꿈 문자 바로 다음 부분과도 대응)
$ : 문자열의 끝 위치에 대응(줄 바꿈 문자 바로 앞 부분과도 대응)
. : 어떠한 문자열이라도 상관없음 의미
{n} : 앞 표현식이 n번 나타나는 부분에 대응 (n은 반드시 양의 정수)
ex) /a{2}/ ->caandy의 a에 대응O , candy의 a에 대응X
\w : 영숫자 문자에 대응
[A-Za-z0-9_]와 동일
[xyz] : 문자셋(Character set)
괄호 안의 어떤 문자(escape sequence까지 포함)와도 대응
하이픈(-)으로 문자의 범위 지정 가능
ex) [a-d] ->[abcd]와 똑같이 작동
- 출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Regular_Expressions
- password validation
▷수정 후의 코드
// password validation
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,16}$/;
const passwordRegexErrorMessage = 'Should be minimum 8 characters of alphabet and number combination!';
userSchema.path('password').validate(function(v) {
var user = this;
// create user
if(user.isNew){
if(!user.passwordConfirmation){
user.invalidate('passwordConfirmation', 'Password Confirmation is required.');
}
if(!passwordRegex.test(user.password)){
user.invalidate('password', passwordRegexErrorMessage);
}
else if(user.password !== user.passwordConfirmation) {
user.invalidate('passwordConfirmation', 'Password Confirmation does not matched!');
}
}
// update user
if(!user.isNew){
if(!user.currentPassword){
user.invalidate('currentPassword', 'Current Password is required!');
}
else if(!bcrypt.compareSync(user.currentPassword, user.originalPassword)){
user.invalidate('currentPassword', 'Current Password is invalid!');
}
if(user.newPassword && !passwordRegex.test(user.newPassword)){
user.invalidate("newPassword", passwordRegexErrorMessage);
}
else if(user.newPassword !== user.passwordConfirmation) {
user.invalidate('passwordConfirmation', 'Password Confirmation does not matched!');
}
}
});
앞서 정규표현식을 변수 passwordRegex로 선언하였다.
정규표현식.test(문자열)함수 : 문자열에 정규표현식을 통과하는 부분이 있으면 -> true,
아니면 false를 반환
->false가 반환되면 model.invalidate 함수가 호출된다.
· index.js 수정
새로 설치한 package로부터 flash, session을 선언하였다.
req.flash(문자열, 저장할 값)형태로 저장할 값(숫자, 문자열, 오브젝트 등 가능)을 해당 문자열에 저장한다.
이때 배열로 저장되기 때문에 같은 문자열을 중복해서 사용하면 순서대로 배열에 저장된다.
req.flash(문자열)인 경우 - 해당 문자열에 저장된 값들을 배열로 불러온다.
<- 저장된 값이 없으면 빈 배열([ ])을 return
sesssion은 서버에서 접속자를 구분시키는 역할을 한다.
서로 다른 사용자를 구분하여 서버에서 필요한 값들을 따로 관리하게 된다.
- 옵션부분 : {secret:'MySecret', resave:true, saveUninitialized:true}
-> secret : session을 hash화하는 데 사용되는 값(일종의 비밀번호)
아무값이나 넣으면 된다.
resave : 재저장을 계속 할 것인지에 대한 옵션
매 request마다 세션에 변동이 있든 없든 무조건 다시 저장하는 것
default : true
saveUninitialized : 세션이 세션store에 저장되기 전 uninitialized한 상태(request가 들어올 때, 해당 request에서 새로 생성된 session에 아무 작업이 이루어지지 않은 상황)의 session을 강제 저장
아무 내용 없는 session이 계속 저장될 수 있음
default : true-> client의 서버 방문 횟수에 따라 등급을 구분 짓는 기능 구현 가능
(참조 : https://dalkomit.tistory.com/72 , https://fierycoding.tistory.com/36 )
· user.js 수정
// routes/users.js
const express = require('express');
const router = express.Router();
const User = require('../models/User');
// Index
router.get('/', function(req, res){
User.find({})
.sort({username:1})
.exec(function(err, users){
if(err) return res.json(err);
res.render('users/index', {users:users});
});
});
// New
router.get('/new', function(req, res){
const user = req.flash('user')[0] || {};
const errors = req.flash('errors')[0] || {};
res.render('users/new', { user:user, errors:errors });
});
// create
router.post('/', function(req, res){
User.create(req.body, function(err, user){
if(err){
req.flash('user', req.body);
req.flash('errors', parseError(err));
return res.redirect('/users/new');
}
res.redirect('/users');
});
});
// show
router.get('/:username', function(req, res){
User.findOne({username:req.params.username}, function(err, user){
if(err) return res.json(err);
res.render('users/show', {user:user});
});
});
// edit
router.get('/:username/edit', function(req, res){
const user = req.flash('user')[0];
const errors = req.flash('errors')[0] || {};
if(!user){
User.findOne({username:req.params.username}, function(err, user){
if(err) return res.json(err);
res.render('users/edit', { username:req.params.username, user:user, errors:errors });
});
}
else {
res.render('users/edit', { username:req.params.username, user:user, errors:errors });
}
});
// update
router.put('/:username', function(req, res, next){
User.findOne({username:req.params.username})
.select('password')
.exec(function(err, user){
if(err) return res.json(err);
// update user object
user.originalPassword = user.password;
user.password = req.body.newPassword? req.body.newPassword : user.password;
for(const p in req.body){
user[p] = req.body[p];
}
// save updated user
user.save(function(err, user){
if(err){
req.flash('user', req.body);
req.flash('errors', parseError(err));
return res.redirect('/users/'+req.params.username+'/edit');
}
res.redirect('/users/'+user.username);
});
});
});
// destroy
router.delete('/:username', function(req, res){
User.deleteOne({username:req.params.username}, function(err){
if(err) return res.json(err);
res.redirect('/users');
});
});
module.exports = router;
// functions
function parseError(errors){
const parsed = {};
if(errors.name == 'ValidationError'){
for(const name in errors.errors){
const validationError = errors.errors[name];
parsed[name] = { message:validationError.message };
}
}
else if(errors.code == '11000' && errors.errmsg.indexOf('username') > 0) {
parsed.username = { message:'This username already exists!' };
}
else {
parsed.unhandled = JSON.stringify(errors);
}
return parsed;
}
user 생성 시에 에러가 있는 경우 new페이지에 에러와 기존에 입력했던 값을 보여준다.
( 이때, 이 값들은 create route에서 생성된 flash로부터 받아옴 )
flash는 배열이 오는데 이 프로그램에서는 하나 이상의 값이 저장되는 경우가 없고, 있더라도 오류이므로 무조건 [0]의 값을 읽어오게 하였다.
값이 없다면 (처음 new 페이지에 들어온 경우) | | { }을 사용하여 빈 오브젝트를 넣어 user/new 페이지를 생성한다.
user 생성시 오류가 있으면 user, error flash를 만들고 new페이지로 redirect 한다.
user 생성시 발생하는 오류는
1. User model의 userSchema에 설정해둔 validation을 통과하지 못한 경우
2. mongoDB에서 오류를 내는 경우
-> 두 경우의 error객체의 형식은 상이하므로 parseError라는 함수를 따로 만들어서 err을 분석하고 일정한 형식으로 만든다
user flash가 있으면 -> 1. update에서 오류가 생겨 edit으로 다시 돌아온 경우
(그래서 기존에 입력했던 값으로 form에 값들을 생성해야 함 -> | | { } 사용 X )
없으면 -> 2. 처음 들어온 경우로 가정
req.flash(문자열)인 경우 - 해당 문자열에 저장된 값들을 배열로 불러온다.
<- 저장된 값이 없으면 빈 배열([ ])을 return
2. 처음 접속하는 경우 (!user : user 변수에 값이 없음) -> DB에서 값을 찾아 form에 기본 값을 생성(=form을 user의 DB값들로 채움)
1. update에서 오류가 생겨 edit 페이지로 돌아온 경우 -> user 변수의 값을 flash에서 받아옴
user을 사용해 값을 생성함(=update에서 입력했던 값으로 form을 채움)
username : req.params.username
render시 username을 따로 req.params.username로 보내준다.
user.username이 (1) 해당 user의 username일수도, (2) user flash에서 받는 username일수도 있기 때문에
username은 주소에서 찾은 username을 따로 보내준다.
mongoose에서 내는 에러와 mongoDB에서 내는 에러의 형태가 다르기 때문에
에러의 형태를 항목이름: { message: "에러메세지" }로 통일시켜주는 함수이다.
if - mongoose의 model validation error
else if - mongoDB에서 username이 중복되는 error
(error code 11000 : key값이 중복될 때 발생하는 error)
else - 그 외의 error을 처리함
'WEB > Node' 카테고리의 다른 글
게시판 만들기 / login 기능 - passport package (0) | 2021.12.07 |
---|---|
게시판 만들기 / User 생성, 수정 시 error 처리 (2) (0) | 2021.12.01 |
게시판 만들기 / password 암호화 -bcrypt (0) | 2021.11.17 |
게시판 만들기 / 회원가입 (0) | 2021.11.16 |
게시판 만들기 / front end + scope + breadcrumb +bootstrap grid (0) | 2021.10.19 |