[React] 서버 구현 - 2 (회원 가입 구현, 비밀번호 암호화)
이전글 : 2022.06.08 - [React] - [React] 서버 구현 - 1 (mongoDB 연결, 유저 모델 생성)
[React] 서버 구현 - 1 (mongoDB 연결, 유저 모델 생성)
이전글 : 2022.06.07 - [NodeJS] - [NodeJS] Express와 Sequelize-cli를 사용한 Access 및 Refresh 토큰 구현 [NodeJS] Express와 Sequelize-cli를 사용한 Access 및 Refresh 토큰 구현 2022.06.02 - [NodeJS] - [..
gguzuck.tistory.com
이번에는 유저 모델을 설정했으니, 회원가입부터 구현해봅시다.
서버를 다루는 부분은 index.js 부분이기 때문에,
이부분을 수정해주면 되겠죠?
다만, 이제부터는 클라이언트와의 통신이 불가피합니다.
클라이언트 : 웹
서버 : 내부
라고 생각하면,
웹에서 정보가 입력되어야지,
내부적으로 저장을 하던, 실행을 하던 하겠죠?
그렇다면 어떻게 통신을 하느냐.
이에 맞는 가장 좋은 모듈이 있습니다.
Body-Parser 라는 모듈입니다.
바디 파서는 클라이언트-서버 통신용 모듈인데,
아이디,패스워드 정보 등을 클라이언트에서 받아 서버로 보내주는 형식을 지닙니다.
그러면 설치해봅시다.
# npm i body-parser --save
당연히 바디 파서도 똑같이 설치할 수 있습니다.
그러면 이제 입력하도록 하죠.
// ../server/index.js
const bodyParser = require('body-parser') // 클라 - 서버 통신
const { User } = require("./models/User") // User 모델
// body-parser가 클라이언트에서 오는 정보를 서버에서 분석해서 가져올 수 있게 하는 것
// application/x-www-form-urlencoded 타입 데이터
// application/json 타입 데이터
app.use(express.urlencoded({extended: true}));
app.use(express.json());
...
// 회원가입을 위한 라우팅
app.post('/register', (req, res) => {
// 회원가입 할 때 필요한 정보들 client에서 가져오면
// 해당 데이터를 데이터베이스에 넣어준다.
// 현재는 클라이언트는 구현하지 않았으니 postman 을 통해 값을 넣을 예정.
// req.body안에는 json 형식으로 정보들이 들어있음(id, pw) (body-parser)
const user = new User(req.body)
// mongoDB 메소드, save해주면 Usermodel에 저장됨
user.save((err,userInfo) => {
if(err){
console.log("회원가입 저장 에러 발생")
return res.json({registerSuccess:false, err})
}
console.log("에러 없음, 성공 확인됨")
//status(200)은 성공을 의미
return res.status(200).json ({registerSuccess: true })
})
})
저번에 만들어준 User 모델을 import 해주고,
바디파서를 사용해줍시다.
이제 포스트 맨을 들어가서,
POST 형식으로 바꾼다음,
localhost:5000/register
링크로 들어가서,
JSON 형식으로
모델에서 설정해준 방식,
{
"username" : "Test"
"usermail" : "Test@gmail.com"
"password" : "test"
}
을 입력해주고,
registerSuccess : true
라는 값이 아래 터미널에 생기면 성공입니다.
회원가입은 이런식으로 진행됬으니,
이제 모델에 저장된 정보로 로그인을...
하기전에 mongoDB를 보게되면,
비밀번호가 그대로 원문으로 노출되어 있습니다.
이는 암호화를 통해 저장하지 않으면
이후에 보안성에 문제가 생길수있습니다.
아이디와 비밀번호를 사용하는 웹사이트는
이러한 보안 하나하나가 매우 중요하기 때문에,
우리가 아무리 아마추어라도 이를 지켜봅시다.
가장 흔한 방식이자 좋은 방식인 bcrypt를 사용합시다.
사실 보안 방식은 Nodejs에서 사용한 방식도 있지만,
bcrypt 라는 알고리즘도 추천하는 방식중 하나입니다.
먼저 이 모듈을 설치해야 사용이 가능하니 설치하도록 하죠.
# npm i bcrypt --save
설치방법은 당연히 동일합니다.
그럼 당연히 사용하려면 import를 사용해야겠지요
그럼 어디서 사용을 하느냐,
바로 User 모델에서 사용합니다.
원래는 index에서 패스워드를 불러와서,
패스워드를 암호화 해서,
암호화된 패스워드를 저장한다.
로 진행을 합니다만,
User 모델에서도 저장을 하는 방법이 존재합니다.
// ../server/models/User.js
const bcrypt = require('bcrypt')
const saltRounds = 10;
...
// DB에 저장할때 비밀번호 암호화
userSchema.pre('save', function(next) {
//비밀번호 암호화
var user = this;
if(user.isModified('password')) { // pw변경시에만 해쉬값 넣도록
bcrypt.genSalt(saltRounds, function(err, salt) {
if(err) return next(err) //에러나오면 index로
bcrypt.hash(user.password, salt, function(err, hash) {
// Store hash in your password DB.
if(err) return next(err)
user.password = hash
next() // hash값 저장했으면 index로
});
});
} else {
next()
}
})
.pre 구문을 사용하는겁니다.
.pre('save') 부분을 보면
save 하기전에 실행하라. 라는 구문이됩니다.
이 save는 index 에서 회원가입을 할때,
user.save 구문의 save를 뜻합니다.
따라서, DB에 저장하기 전에 이 구문먼저 실행해서 암호화를 진행하라.
라는 의미입니다.
bcrypt 모듈을 사용했는데,
어떻게 사용되는지 잘 모를겁니다.
어떻게 구동되는지 가볍게 알아봅시다.
bcrypt 모듈은 임의의 값인 salt를 생성합니다.
saltRound의 수만큼 암호화 하는데,
이게 2의 배수가 saltRound의 수.
즉, saltRound가 10이라면 2^10 = 1024 번 암호화합니다.
저장된 암호는
$2b$[cost]$[22 character salt][31 character hash]
이런 구조를 띄게되는데,
cost가 saltRound,
22 character salt와
31 character hash가
암호화된 비밀번호와 salt입니다.
따라서, saltRound가 커지면 커질수록 무차별 대입공격에서 더욱 안전해지죠.
무차별 대입 공격이란?
이전글 :: Brute-Force Attack 무차별 대입 공격 참고
현재 많은 암호화 알고리즘,
SHA-2, PBKDF2 와 같이 사용되는 가장 강력한 알고리즘 중 하나로 여겨지는 보안 방식이며,
이 암호화 알고리즘을 수정하는 것은 용납되지 않는다고 합니다.
오히려 수정하면 더 암호화가 약해진다고 하니...
그대로 사용하는 편이 좋겠죠.
이제 이 코드를 쓴 이후,
다시 Postman을 통해 저장하면,
비밀번호가 암호화 되어 있을겁니다.
참고로, User모델을 설정할때,
userid 와 usermail 에 unique 설정을 해주었기 때문에,
동일한 값을 주면 false가 뜨게 되니 이 부분 확인 하고,
만약, 하나가 존재하는 상태에서 더이상 추가되지 않는다면
컬렉션을 삭제했을때 다시 잘 작동합니다.
(이건 왜 그런지 잘 모르겠음...)
이렇게 회원가입, DB저장, 비밀번호 암호화까지 완료했습니다.