실제 운영 환경에서 대량 사용자 기반 로그인 부하 테스트를 수행하고, 시스템의 병목 지점을 명확히 식별한다.
주요 분석 대상:
| 항목 | 내용 |
|---|---|
| 사용자 수 | 약 20,000명 |
| 로그인 방식 | loginId + password |
| 인증 방식 | Spring Security + AuthenticationManager + BCrypt |
| 서버 스펙 | 2 vCPU / 4GB RAM |
| JVM Heap | 1GB 제한 |
k6 기반 signupScript.js를 사용하여 테스트 사용자 생성
특징:
login_id: testuser_r2_xxxxxPassword!1234)SELECT * FROM user LIMIT 10 OFFSET 19990;
결론: 사용자 데이터 준비 완료, 로그인 테스트 기반 충족
| 항목 | 값 |
|---|---|
| TPS | 약 25 req/s |
| 평균 응답 시간 | ~786ms |
| p95 | 1.04s |
| 실패율 | 0% |
| CPU 사용률 | ~90% |
| 항목 | 값 |
|---|---|
| TPS | ~26 req/s |
| 평균 응답 시간 | 3.7 ~ 3.8s |
| p95 | 4.1s |
| 로그인 성공률 | 100% |
| 실패율 | 0% |
이 테스트는 순수 CPU-bound workload
cpu 가 주요 병목으로 생각했기때문에 사실상 I/O 는 확인을 안해도 되었지만, 그럼에도 한번 확인을 해 보았습니다.
EXPLAIN SELECT * FROM user WHERE login_id = 'testuser_r2_00001';
로그인 성능 병목은 DB가 아님
AuthenticationManager
→ DaoAuthenticationProvider
→ UserDetailsService.loadUserByUsername(loginId)
→ PasswordEncoder.matches(raw, bcryptHash)
AuthUserDetailsService@Service 구현SecurityConfiguration에 자동 주입loadUserByUsername()는 자동 호출BCrypt는 단순한 해시가 아니다.
"공격자를 느리게 만들기 위해" 서버도 일부러 느리게 만드는 알고리즘이다.
핵심 개념:
로그인 시 일어나는 일:
즉:
로그인 1회 = 수만 ~ 수십만 번의 암호 연산
👉 동시 로그인 = CPU 직격
| 자원 | 상태 | 이유 |
|---|---|---|
| CPU | 🔥 100% | bcrypt 연산 |
| Memory | 여유 | 연산 위주, 메모리 사용 적음 |
| Disk | 거의 0 | DB 1회 조회뿐 |
| Network | 미미 | 요청/응답 작음 |
| GC | 안정 | heap 압박 없음 |
👉 전형적인 CPU-bound 시그니처
DelegatingPasswordEncoder 기본 전략즉:
느려서 문제가 아니라
느리게 설계된 게 의도
예: SHA-256 같은 빠른 해시
✅ 장점:
❌ 치명적 단점:
👉 로그인은 빨라지지만, 보안은 붕괴
| 구분 | 빠른 해시 (SHA) | 느린 해시 (BCrypt) |
|---|---|---|
| 속도 | 매우 빠름 | 느림 |
| CPU 사용 | 적음 | 많음 |
| 로그인 성능 | 좋음 | 제한적 |
| 무차별 대입 | ❌ 취약 | ✅ 강함 |
| GPU 공격 | ❌ 취약 | ⭕ 방어 |
| 실무 사용 | ❌ 금지 | ✅ 표준 |
| 연산 | 특징 |
|---|---|
| BCrypt | CPU 반복 연산 |
| Argon2 | CPU + 메모리 |
| SCrypt | CPU + 메모리 |
| PBKDF2 | 반복 해시 |
그중 BCrypt가:
테스트에서 관측된 모든 지표는 BCrypt 병목 패턴과 100% 일치
따라서:
👉 CPU 코어 수 + BCrypt cost = 로그인 처리량 상한
dev / qa → cost ↓
prod → 유지
❗ 이번 결과는 장애가 아니라
보안이 의도대로 작동하고 있다는 증거
| 항목 | 상태 |
|---|---|
| 아키텍처 | ✅ 정석 |
| 병목 원인 | ✅ 명확 (BCrypt + CPU) |
| DB/네트워크/JVM | ✅ 정상 |
| 개선 방향 | 선택의 문제 |
로그인은 느려도 된다.
대신, 안전해야 한다.
❌ 잘못된 접근:
✅ 올바른 접근:
1. 병목 지점 정확히 식별
2. 병목이 의도된 것인지 판단
3. 비즈니스 요구사항과 보안 균형 고려
4. 실질적 개선 방안 도출
CPU-bound (이번 사례):
I/O-bound:
보안 기술은 종종 의도적으로 느리게 설계된다.
이유:
교훈:
모든 '느림'이 최적화 대상은 아니다.
때로는 '느림'이 정답이다.
이번 부하 테스트를 통해 다음을 배울 수 있었다.
핵심 메시지:
좋은 아키텍처는 빠른 것이 아니라
목적에 맞게 설계된 것이다.
다음에는 실제 프로덕션 환경에서의 로그인 최적화 전략과 OAuth 전환 과정을 다뤄볼 예정이다.