일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 서울파티룸
- mysql
- 웹개발
- 구로파티룸
- 코딩
- 취미
- 일
- 존경하는위인
- 보드게임점수계산
- 스페이스우일
- 착한코딩
- 파티룸
- 옥길그릭요거트
- 스컬킹점수
- 휴식
- 옥길동요거트
- 광명파티룸
- 해외여행
- 부천파티룸
- 스컬킹
- 옥길파티룸
- 보드게임점수
- MBTI
- 옥길요거트
- 개발자
- 그릭요거트
- 옥길동파티룸
- 스컬킹점수계산
- 가장존경하는인물
- 부천공간대여
- Today
- Total
SIMPLE & UNIQUE
4-1강 : 회원 가입 기능 구현, 비밀번호 단반향 암호화 본문
4_1 목표 : 회원가입 페이지에서 회원 정보를 입력하고, 회원가입 버튼을 누르면 아이디 중복체크를 한다. 중복체크가 완료되면 회원 가입 API를 호출한다. 비밀번호를 단반향 암호화 처리해 DB에 삽입한다.
## 참고 ##
회원정보 관리는 개념이 중요하다. 회원가입 > 로그인 > 사용자 세션을 관리하는 프로세스는 아래와 같다.
1. 회원가입 폼에서 아이디, 비밀번호 등 정보를 입력받는다.
2. 비밀번호를 제외한 모든 데이터는 그대로 DB에 삽입한다.
비밀번호는 복호화할 수 없는 단방향으로 암호화해 DB에 넣는다. (암호화 방식은 bcript가 처리)
3. 사용자는 로그인 할 때, 아이디와 비밀번호를 입력한다.
아이디, 비밀번호를 DB에 저장된 값과 비교해야한다. 아이디는 그대로 비교하고, 비밀번호는 2.에서와 동일한
방식으로 암호화해 비교한다.
4. 비교한 값이 동일하면 로그인 처리를 해야한다.
쿠키에 jwt(토큰기반인증)을 이용해 토큰을 만들어 저장한다. (비밀키 사용)
세션 유효 시간을 세팅한다.
5. 로그인이 된 상태면 software tool 리스트 페이지로, 아니라면 로그인 페이지로 페이지 이동시킨다.
App.js 라우팅 페이지에 쿠키값 인증 로직을 넣는다. 페이지를 로딩할 때마다 유효한 세션인지 인증한다.
1. react 경로에 회원가입 페이지를 추가한다.
1) 아래와 같이 리액트 경로(C:\react200\client\src\components)에 Register 폴더를 만들고, Register.js파일을 생성한다.
2) Register.js 에 아래 코드를 붙여 넣는다.
import React, { Component } from 'react';
import axios from "axios";
import Swal from 'sweetalert2';
import $ from 'jquery';
class Register extends Component {
constructor (props) {
super(props);
this.state = {
}
}
submitClick = async (type, e) => {
this.email_val_checker = $('#email_val').val();
this.email2_val_checker = $('#email2_val').val();
this.pwd_val_checker = $('#pwd_val').val();
this.pwd_cnf_val_checker = $('#pwd_cnf_val').val();
this.name_val_checker = $('#name_val').val();
this.org_val_checker = $('#org_val').val();
this.major_val_checker = $('#major_val').val();
this.phone1_val_checker = $('#phone1_val').val();
this.phone2_val_checker = $('#phone2_val').val();
this.phone3_val_checker = $('#phone3_val').val();
this.fnValidate = (e) => {
var pattern1 = /[0-9]/;
var pattern2 = /[a-zA-Z]/;
var pattern3 = /[~!@#$%^&*()_+|<>?:{}]/;
if(this.email_val_checker === '') {
$('#email_val').addClass('border_validate_err');
this.sweetalert('이메일 주소를 다시 확인해주세요.', '', 'info', '닫기')
return false;
}
if(this.email_val_checker.search(/\s/) !== -1) {
$('#email_val').addClass('border_validate_err');
this.sweetalert('이메일 공백을 제거해 주세요.', '', 'info', '닫기')
return false;
}
$('#email_val').removeClass('border_validate_err');
if(this.email2_val_checker ==='') {
$('#email2_val').addClass('border_validate_err');
this.sweetalert('이메일 주소를 다시 확인해주세요.', '', 'info', '닫기')
return false;
}
$('#email2_val').removeClass('border_validate_err');
if(this.pwd_val_checker ==='') {
$('#pwd_val').addClass('border_validate_err');
this.sweetalert('비밀번호를 입력해주세요.', '', 'info', '닫기')
return false;
}
if(this.pwd_val_checker !=='') {
var str = this.pwd_val_checker;
if(str.search(/\s/) !== -1) {
$('#pwd_val').addClass('border_validate_err');
this.sweetalert('비밀번호 공백을 제거해 주세요.', '', 'info', '닫기')
return false;
}
if(!pattern1.test(str) || !pattern2.test(str) || !pattern3.test(str)
|| str.length < 8 || str.length > 16) {
$('#pwd_val').addClass('border_validate_err');
this.sweetalert('8~16자 영문 대 소문자\n숫자, 특수문자를 사용하세요.', '', 'info', '닫기')
return false;
}
}
$('#pwd_val').removeClass('border_validate_err');
if(this.pwd_cnf_val_checker ==='') {
$('#pwd_cnf_val').addClass('border_validate_err');
this.sweetalert('비밀번호 확인을 입력해주세요.', '', 'info', '닫기')
return false;
}
if(this.pwd_val_checker !== this.pwd_cnf_val_checker) {
$('#pwd_val').addClass('border_validate_err');
$('#pwd_cnf_val').addClass('border_validate_err');
this.sweetalert('비밀번호가 일치하지 않습니다.', '', 'info', '닫기')
return false;
}
$('#pwd_cnf_val').removeClass('border_validate_err');
if(this.name_val_checker ==='') {
$('#name_val').addClass('border_validate_err');
this.sweetalert('성명을 입력해주세요.', '', 'info', '닫기')
return false;
}
if(this.name_val_checker.search(/\s/) !== -1) {
$('#name_val').addClass('border_validate_err');
this.sweetalert('성명에 공백을 제거해 주세요.', '', 'info', '닫기')
return false;
}
$('#name_val').removeClass('border_validate_err');
if(this.org_val_checker ==='') {
$('#org_val').addClass('border_validate_err');
this.sweetalert('소속기관을 입력해주세요.', '', 'info', '닫기')
return false;
}
if(this.org_val_checker.search(/\s/) !== -1) {
$('#org_val').addClass('border_validate_err');
this.sweetalert('소속기관에 공백을 제거해 주세요.', '', 'info', '닫기')
return false;
}
$('#org_val').removeClass('border_validate_err');
if(this.major_val_checker ==='') {
$('#major_val').addClass('border_validate_err');
this.sweetalert('전공을 입력해주세요.', '', 'info', '닫기')
return false;
}
if(this.major_val_checker.search(/\s/) !== -1) {
$('#major_val').addClass('border_validate_err');
this.sweetalert('전공에 공백을 제거해 주세요.', '', 'info', '닫기')
return false;
}
$('#major_val').removeClass('border_validate_err');
if(this.phone1_val_checker ==='' || this.phone2_val_checker ===''
|| this.phone3_val_checker ==='') {
$('#phone1_val').addClass('border_validate_err');
$('#phone2_val').addClass('border_validate_err');
$('#phone3_val').addClass('border_validate_err');
this.sweetalert('휴대전화 번호를 입력해주세요.', '', 'info', '닫기')
return false;
}
$('#phone1_val').removeClass('border_validate_err');
$('#phone2_val').removeClass('border_validate_err');
$('#phone3_val').removeClass('border_validate_err');
return true;
}
if(this.fnValidate()){
this.state.full_email = this.email_val_checker+'@'+this.email2_val_checker
axios.post('/api/register?type=dplicheck', {
is_Email: this.email_val_checker+'@'+this.email2_val_checker
})
.then( response => {
try {
const dupli_count = response.data.json[0].num;
if(dupli_count !== 0){
$('#email_val').addClass('border_validate_err');
$('#email2_val').addClass('border_validate_err');
this.sweetalert('이미 존재하는 이메일입니다.', '', 'info', '닫기')
}else{
$('#email_val').removeClass('border_validate_err');
$('#email2_val').removeClass('border_validate_err');
this.fnSignInsert('signup', e)
}
} catch (error) {
this.sweetalert('작업중 오류가 발생하였습니다.', error, 'error', '닫기')
}
})
.catch( response => { return false; } );
}
this.fnSignInsert = async (type, e) => {
var jsonstr = $("form[name='frm']").serialize();
jsonstr = decodeURIComponent(jsonstr);
var Json_form = JSON.stringify(jsonstr).replace(/\"/gi,'')
Json_form = "{\"" +Json_form.replace(/\&/g,'\",\"').replace(/=/gi,'\":"')+"\"}";
try {
const response = await fetch('/api/register?type='+type, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: Json_form,
});
const body = await response.text();
if(body === "succ"){
this.sweetalert('회원가입이 완료되었습니다.', '', 'info', '닫기')
this.props.history.push('/');
}else{
this.sweetalert('작업중 오류가 발생하였습니다.', body, 'error', '닫기');
}
} catch (error) {
this.sweetalert('작업중 오류가 발생하였습니다.', error, 'error', '닫기');
}
}
};
emailKeyPress = (e) => {
$('#email_val').removeClass('border_validate_err');
};
pwdKeyPress = (e) => {
$('#pwd_val').removeClass('border_validate_err');
};
pwdCnfKeyPress = (e) => {
$('#pwd_cnf_val').removeClass('border_validate_err');
};
nameKeyPress = (e) => {
$('#name_val').removeClass('border_validate_err');
};
handleSubmit = (e) => {
e.preventDefault();
};
mustNumber = (id) => {
var pattern1 = /[0-9]/;
var str = $('#'+id).val();
if(!pattern1.test(str.substr(str.length - 1, 1))){
$('#'+id).val(str.substr(0, str.length-1));
}
}
sweetalert = (title, contents, icon, confirmButtonText) => {
Swal.fire({
title: title,
text: contents,
icon: icon,
confirmButtonText: confirmButtonText
})
}
render () {
return (
<div>
<section className="sub_wrap" >
<article className="s_cnt re_1 ct1">
<div className="li_top">
<h2 className="s_tit1">회원가입</h2>
<form method="post" name="frm">
<div className="re1_wrap">
<div className="re_cnt ct2">
<table className="table_ty1">
<tr className="re_email">
<th>이메일</th>
<td>
<input id="email_val" type="text" name="is_Useremail1"
placeholder="이메일을 입력해주세요." onKeyPress={this.emailKeyPress}/>
<span className="e_goll">@</span>
<select id="email2_val" name="is_Useremail2" className="select_ty1">
<option value="">선택하세요</option>
<option value='naver.com'>naver.com</option>
<option value='hanmail.net'>hanmail.net</option>
<option value='nate.com'>nate.com</option>
<option value='hotmail.com'>hotmail.com</option>
<option value='gmail.com'>gmail.com</option>>
<option value='yahoo.co.kr'>yahoo.co.kr</option>
<option value='yahoo.com'>yahoo.com</option>
</select>
</td>
</tr>
<tr>
<th>비밀번호</th>
<td>
<input id="pwd_val" type="password" name="is_Password"
placeholder="비밀번호를 입력해주세요." onKeyPress={this.pwdKeyPress} />
</td>
</tr>
<tr>
<th>비밀번호 확인</th>
<td>
<input id="pwd_cnf_val" type="password" name="is_Password"
placeholder="비밀번호를 다시 입력해주세요." onKeyPress={this.pwdCnfKeyPress}/>
</td>
</tr>
<tr>
<th>성명</th>
<td>
<input id="name_val" type="text" name="is_Username"
placeholder="성명을 입력해주세요." onKeyPress={this.nameKeyPress}/>
</td>
</tr>
<tr>
<th>소속 기관</th>
<td>
<input id="org_val" type="text" name="is_Organization"
placeholder="소속 기관명을 입력해주세요." />
</td>
</tr>
<tr>
<th>전공</th>
<td>
<input id="major_val" type="text" name="is_Usermajor"
placeholder="전공을 입력해주세요." />
</td>
</tr>
<tr className="tr_tel">
<th>핸드폰</th>
<td>
<select id="phone1_val" name="is_Userphone1" className="select_ty1">
<option value="">선택</option>
<option value="010">010</option>
<option value="011">011</option>
<option value="016">016</option>
<option value="017">017</option>
<option value="018">018</option>
<option value="019">019</option>
</select>
<span className="tel_dot">-</span>
<input id="phone2_val" name="is_Userphone2" max="9999"
maxlength="4" onChange={(e) => this.mustNumber("phone2_val")}/>
<span className="tel_dot">-</span>
<input id="phone3_val" name="is_Userphone3" max="9999"
maxlength="4" onChange={(e) => this.mustNumber("phone3_val")}/>
</td>
</tr>
</table>
</div>
</div>
<div className="btn_confirm">
<div className="bt_ty bt_ty2 submit_ty1"
onClick={(e) => this.submitClick('signup', e)}>회원가입</div>
</div>
</form>
</div>
</article>
</section>
</div>
);
}
}
export default Register;
2. node 경로(C:\react200)에 회원가입 관련 코드를 추가한다.
1) server.js 파일을 아래와 같이 수정한다.
var express = require('express');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var swtoolRouter = require("./routes/SwtoolRout");
var fileuploadRouter = require("./routes/UploadRout");
var usersRouter = require("./routes/UsersRout");
require("./routes/BatchRout");
var app = express();
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use("/api/Swtool", swtoolRouter);
app.use("/api/upload", fileuploadRouter);
app.use(express.static("./uploads"));
app.use("/api/register", usersRouter);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
2) 단방향 암호화를 사용하기 위해, node 경로(C:\react200)에 bcrypt 패키지를 설치한다.
npm install bcrypt --save
3) 노드경로 C:\react200\routes에 UsersRout.js 파일을 추가해 아래 코드를 붙여넣는다. 회원가입 정보를 삽입하고 아이디 중복체크를 조회하는 mapper정보가 추가됐다.
var express = require('express');
var router = express.Router();
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');
const saltRounds = 10;
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
router.post('/', (req, res, next) => {
var type = req.query.type;
if(type == "signup"){
//회원가입 정보 삽입
try {
// Mysql Api 모듈(CRUD)
var dbconnect_Module = require('./dbconnect_Module');
//Mysql 쿼리 호출정보 입력
req.body.mapper = 'UserMapper';//mybatis xml 파일명
req.body.crud = 'insert';//select, insert, update, delete 중에 입력
req.body.mapper_id = 'insertUser';
var myPlaintextPassword = req.body.is_Password;
if(myPlaintextPassword != '' && myPlaintextPassword != undefined ){
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(myPlaintextPassword, salt, function(err, hash) {
req.body.is_Password = hash;
router.use('/', dbconnect_Module);
next('route')
});
});
}else{
router.use('/', dbconnect_Module);
next('route')
}
} catch (error) {
console.log("Module > dbconnect error : "+ error);
}
}else if(type == "dplicheck"){
//이메일 중복체크
try {
// Mysql Api 모듈(CRUD)
var dbconnect_Module = require('./dbconnect_Module');
//Mysql 쿼리 호출정보 입력
req.body.mapper = 'UserMapper';//mybatis xml 파일명
req.body.crud = 'select';//select, insert, update, delete 중에 입력
req.body.mapper_id = 'selectUserDpliCheck';
router.use('/', dbconnect_Module);
next('route')
} catch (error) {
console.log("Module > dbconnect error : "+ error);
}
}
});
module.exports = router;
4) 노드경로 C:\react200\models에 UserMapper.xml 파일을 생성하고 아래 코드를 붙여넣는다. 회원가입 정보를 삽입하고 아이디 중복체크를 조회하는 쿼리가 추가됐다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="UserMapper">
<insert id="insertUser">
INSERT INTO react.react_user
(
username
, userorg
, useremail
, userpassword
, usermajor
, userphone
, userflag
, reg_date
, reg_user
, update_date
, update_user
)
VALUES (
#{is_Username}
, #{is_Organization}
, CONCAT(#{is_Useremail1}, '@', #{is_Useremail2})
, #{is_Password}
, #{is_Usermajor}
, CONCAT(#{is_Userphone1}, '-', #{is_Userphone2},'-', #{is_Userphone3})
, 'Y'
, DATE_FORMAT(now(), '%Y%m%d%H%i%s')
, CONCAT(#{is_Useremail1}, '@', #{is_Useremail2})
, DATE_FORMAT(now(), '%Y%m%d%H%i%s')
, CONCAT(#{is_Useremail1}, '@', #{is_Useremail2})
)
</insert>
<select id="selectUserDpliCheck">
SELECT
count(*) as num
FROM
react.react_user
WHERE useremail = #{is_Email}
</select>
</mapper>
5) workbench를 열어 아래 쿼리를 실행해, 사용자 정보 테이블을 생성한다.
use react;
CREATE TABLE `react_user` (
`username` varchar(100) DEFAULT NULL COMMENT '사용자 이름',
`userorg` varchar(100) DEFAULT NULL COMMENT '소속기관',
`useremail` varchar(100) COMMENT '이메일',
`userpassword` varchar(100) DEFAULT NULL COMMENT '로그인 비밀번호',
`usermajor` varchar(100) DEFAULT NULL COMMENT '전공',
`userphone` varchar(100) DEFAULT NULL COMMENT '휴대전화번호',
`userflag` varchar(100) DEFAULT NULL COMMENT '승인여부',
`reg_date` varchar(100) DEFAULT NULL COMMENT '등록날짜',
`reg_user` varchar(100) DEFAULT NULL COMMENT '등록자',
`update_date` varchar(100) DEFAULT NULL COMMENT '수정날짜',
`update_user` varchar(100) DEFAULT NULL COMMENT '수정자',
PRIMARY KEY (`useremail`)
);
ALTER TABLE react.react_user convert to charset utf8;
6) 회원가입 페이지 정상적으로 완료되면, 다음과 같이 중복체크 쿼리와 회원정보 삽입 쿼리가 정상적으로 로그에 출력된다.
7) 회원가입이 완료되면 로그인 화면으로 페이지를 이동한다.
https://taling.me/Talent/Detail/19341
강의 문의 : ljung5@naver.comhttps://taling.me/Talent/Detail/19341
'탈잉 강의 자료 > 2020_비전공자도 가능한 웹 프로젝트' 카테고리의 다른 글
4-3강 : 이메일 인증을 사용한, 비밀번호 재설정 구현 (0) | 2020.08.03 |
---|---|
4-2강 : 로그인 기능 구현, 쿠키로 로그인 세션 관리 (0) | 2020.08.03 |
3-4강 : 배치(스케줄러) 구현, EC2서버에 배포 (0) | 2020.08.03 |
3-3강 : EC2서버에 NODE 설치, 서버 구동 후 외부 접속 (0) | 2020.08.03 |
3-2강 : AWS 웹 서버 인스턴스 생성, SFTP & SSH 연결 (1) | 2020.08.03 |