요즘 Springboot Security의 로직을 공부하면서 node js의 Spring이라고 불리는 nest js에 대해 같이 공부하고 있다.
그러면서 유저 인증 관련 로직을 구현하면서 bcryptjs라는 라이브러리를 알게되었다.
bcryptjs는 bcrypt 라이브러리를 js에서 사용할 수 있도록 구현되서 nest js에선 무조건 bcryptjs를 사용해야 하는 줄 알았으나 최근에 nest js에서도 bcrypt 라이브러리를 사용할 수 있다는 것을 발견했다.
그렇다면 bcrypt 라이브러리가 있는데 왜 bcryptjs가 생겨났는 지, 두 라이브러리를 비교해보기로 했다.
bcrypt
bcryptjs
두 라이브러리의 사용 빈도를 비교해보면 bcryptjs가 bcrypt에 비해 약 1.43배 많이 사용되었다.
두 라이브러리를 통해 10개의 임의 비밀번호를 암호화하고 기존 비밀번호와 비교하는 로직의 처리 시간을 비교해보고자 한다.
구현하는 로직은 다음과 같다.
// bcrypt library
import bcrypt from 'bcrypt';
const password = [ 'MyPassword123!@#',
'YourPassword123!@#',
'MyPassword456!@#',
'YourPassword**123!@#',
'MyPass123!@#!@#',
'Password123!@@@@#',
'mypassword!!@#',
'MyPassword!@#',
'MyPassword123123',
'MyPas(!)@#*!@)' ]
async function doTest (password, i) {
const saltRounds = 8;
var hashAsync;
var hashSync;
console.log(`State : ${i}, Password : ${password} `)
// - Async 기반의 암호화 로직
console.time('bcrypt_Async Generate Hash');
hashAsync = await bcrypt.hash(password, saltRounds);
console.timeEnd('bcrypt_Async Generate Hash');
// - Async 기반의 암호 비교 로직
console.time('bcrypt_Async Compare Hash');
await bcrypt.compare(password, hashAsync)
console.timeEnd('bcrypt_Async Compare Hash');
// - Sync 기반의 암호화 로직
console.time('bcrypt_Sync Generate Hash');
hashSync = bcrypt.hashSync(password, saltRounds);
console.timeEnd('bcrypt_Sync Generate Hash')
// - Sync 기반의 암호 비교 로직
console.time('bcrypt_Sync Compare Hash');
bcrypt.compareSync(password, hashSync);
console.timeEnd('bcrypt_Sync Compare Hash');
console.log();
}
for (var i = 0; i < 10; i++) {
await doTest(password[i], i);
}
// bcryptjs library
import bcryptjs from 'bcryptjs';
const password = [ 'MyPassword123!@#',
'YourPassword123!@#',
'MyPassword456!@#',
'YourPassword**123!@#',
'MyPass123!@#!@#',
'Password123!@@@@#',
'mypassword!!@#',
'MyPassword!@#',
'MyPassword123123',
'MyPas(!)@#*!@)' ]
async function doTest (password, i) {
const saltRounds = 8;
var hashAsync;
var hashSync;
console.log(`State : ${i}, Password : ${password} `)
// - Async 기반의 암호화 로직
console.time('bcryptjs_Async Generate Hash');
hashAsync = await bcryptjs.hash(password, saltRounds);
console.timeEnd('bcryptjs_Async Generate Hash');
// - Async 기반의 암호 비교 로직
console.time('bcryptjs_Async Compare Hash');
await bcryptjs.compare(password, hashAsync)
console.timeEnd('bcryptjs_Async Compare Hash');
// - Sync 기반의 암호화 로직
console.time('bcryptjs_Sync Generate Hash');
hashSync = bcryptjs.hashSync(password, saltRounds);
console.timeEnd('bcryptjs_Sync Generate Hash')
// - Sync 기반의 암호 비교 로직
console.time('bcryptjs_Sync Compare Hash');
bcryptjs.compareSync(password, hashSync);
console.timeEnd('bcryptjs_Sync Compare Hash');
console.log();
}
for (var i = 0; i < 10; i++) {
await doTest(password[i], i);
}
bcrypt
bcryptjs
평균 수행 시간 비교
평균적으로 4개의 로직 모두에서 bcryptjs 라이브러리가 bcrypt 라이브러리에 비해 약 1.5배의 시간이 더 소요되는 것을 알 수 있다.
bcrypt github 위키에 보면 제작자 kelektiv가 남긴 코멘트를 볼 수 있다.
위 코멘트를 해석하면 다음과 같다.
'bcryptjs 라이브러리는 순수 자바스크립트로 구현된 라이브러리로 c++로 구현된 bcrypt 라이브러리를 코드의 변화없이 대체할 수 있다.
다만 c++로 구현되어 스레드 풀 방식을 사용하는 기존의 bcrypt 라이브러리에 비해 단일 스레드로 구성된 js로 구현된 bcryptjs 라이브러리가 약 30%의 성능 차이를 보인다.
그러나 브라우저에서는 bcrypt 라이브러리가 동작하지 않기 때문에 웹브라우저 환경에서는 bcryptjs 라이브러리가 유일한 선택지다.'
bcrypt 라이브러리가 bcryptjs 라이브러리에 비해 약 30% 정도 성능적으로 우수하지만 웹브라우저 환경에서 사용할 수 있다는 장점으로 인해 bcryptjs 라이브러리가 더욱 많이 사용된다.