로그인 부하테스트 & 성능 분석 총정리

박정민·2026년 1월 26일

1. 테스트 목적

📌 목표

실제 운영 환경에서 대량 사용자 기반 로그인 부하 테스트를 수행하고, 시스템의 병목 지점을 명확히 식별한다.

주요 분석 대상:

  • DB 성능
  • 인덱스 효율성
  • JVM 메모리 관리
  • Spring Security 인증 프로세스
  • BCrypt 암호화 성능

사전 조건

항목내용
사용자 수약 20,000명
로그인 방식loginId + password
인증 방식Spring Security + AuthenticationManager + BCrypt
서버 스펙2 vCPU / 4GB RAM
JVM Heap1GB 제한

2. 사용자 생성 (Signup Seed 단계)

2.1 Signup 스크립트

k6 기반 signupScript.js를 사용하여 테스트 사용자 생성

특징:

  • 실제 signup API 호출
  • 규칙 기반 사용자 생성
    • login_id: testuser_r2_xxxxx
    • 비밀번호: 고정값 (Password!1234)
  • 대량 사용자 seed 목적 (부하 테스트 아님)

2.2 결과

SELECT * FROM user LIMIT 10 OFFSET 19990;
  • 총 20,000명 이상 정상 생성
  • bcrypt 해시 정상 저장 확인

결론: 사용자 데이터 준비 완료, 로그인 테스트 기반 충족


3. 로그인 부하 테스트 (1차)

3.1 테스트 조건

  • VUS (Virtual Users): 20
  • DURATION: 1분

📈 3.2 결과 요약

항목
TPS약 25 req/s
평균 응답 시간~786ms
p951.04s
실패율0%
CPU 사용률~90%

3.3 해석

  • 로그인 성공률 100%
  • DB/네트워크 병목 없음
  • ⚠️ CPU가 주요 병목 후보로 등장

4. 로그인 부하 테스트 (2차 – 확장)

4.1 테스트 조건

  • VUS: 100
  • DURATION: 5분

📈 4.2 k6 결과

항목
TPS~26 req/s
평균 응답 시간3.7 ~ 3.8s
p954.1s
로그인 성공률100%
실패율0%

4.3 시스템 모니터링 결과 (Grafana)

CPU

  • CPU Busy: ~100% 🔥
  • User mode: ~98%
  • Pressure (PSI CPU Some): 93~99%
  • System load: 477%

Memory

  • RAM 사용량: 약 50%
  • Swap: 없음
  • GC: 문제 없음

Disk / I/O

  • Disk IO: 거의 없음
  • I/O wait: ≈ 0%

Network

  • 트래픽: 매우 낮음

명확한 결론

이 테스트는 순수 CPU-bound workload


5. DB & 인덱스 분석

cpu 가 주요 병목으로 생각했기때문에 사실상 I/O 는 확인을 안해도 되었지만, 그럼에도 한번 확인을 해 보았습니다.

5.1 로그인 쿼리 분석

EXPLAIN SELECT * FROM user WHERE login_id = 'testuser_r2_00001';

결론

로그인 성능 병목은 DB가 아님


6. Spring Security 인증 흐름 검증

6.1 실제 실행 흐름

AuthenticationManager
 → DaoAuthenticationProvider
   → UserDetailsService.loadUserByUsername(loginId)
   → PasswordEncoder.matches(raw, bcryptHash)

6.2 UserDetailsService

  • 구현체: AuthUserDetailsService
  • 단일 @Service 구현
  • SecurityConfiguration에 자동 주입

확인 사항

  • loadUserByUsername()는 자동 호출
  • 설정/구현 정상

7. 병목의 실체: BCrypt (확장 분석)

7.1 BCrypt는 왜 "느리게" 설계되었는가

BCrypt는 단순한 해시가 아니다.
"공격자를 느리게 만들기 위해" 서버도 일부러 느리게 만드는 알고리즘이다.

핵심 개념:

  • Slow Hash (느린 해시)
  • Key Stretching
  • CPU-bound 연산

7.2 BCrypt 내부 동작 개념 (요약)

로그인 시 일어나는 일:

  1. 입력된 비밀번호를 기반으로
  2. salt 추출
  3. Blowfish 기반 키 확장
  4. 2^cost 횟수만큼 반복 연산
  5. 결과 해시 비교

즉:

로그인 1회 = 수만 ~ 수십만 번의 암호 연산
👉 동시 로그인 = CPU 직격

7.3 왜 CPU만 100% 타고, 다른 자원은 멀쩡한가

자원상태이유
CPU🔥 100%bcrypt 연산
Memory여유연산 위주, 메모리 사용 적음
Disk거의 0DB 1회 조회뿐
Network미미요청/응답 작음
GC안정heap 압박 없음

👉 전형적인 CPU-bound 시그니처


7.4 BCrypt는 Spring Boot의 "사실상 표준"

  • Spring Security 공식 권장 PasswordEncoder
  • DelegatingPasswordEncoder 기본 전략

즉:

느려서 문제가 아니라
느리게 설계된 게 의도


7.5 "빠른 해시"를 쓰면 안 되는 이유

예: SHA-256 같은 빠른 해시

장점:

  • 빠름
  • CPU 적게 사용

치명적 단점:

  • GPU로 초당 수십억 번 대입 가능
  • DB 유출 시 대부분 비밀번호 수 시간 내 복구
  • 레인보우 테이블 공격 취약

👉 로그인은 빨라지지만, 보안은 붕괴


7.6 느린 해시 vs 빠른 해시 비교

구분빠른 해시 (SHA)느린 해시 (BCrypt)
속도매우 빠름느림
CPU 사용적음많음
로그인 성능좋음제한적
무차별 대입❌ 취약✅ 강함
GPU 공격❌ 취약⭕ 방어
실무 사용❌ 금지✅ 표준

7.7 BCrypt 말고 "CPU를 태우는" 연산들

연산특징
BCryptCPU 반복 연산
Argon2CPU + 메모리
SCryptCPU + 메모리
PBKDF2반복 해시

그중 BCrypt가:

  • 가장 안정적
  • 운영 경험 가장 풍부
  • 컨테이너/소형 서버에서 예측 가능

7.8 로그인 부하테스트 결과와의 정합성

테스트에서 관측된 모든 지표는 BCrypt 병목 패턴과 100% 일치

따라서:

  • 인덱스 튜닝 ❌
  • DB 증설 ❌
  • 네트워크 ❌

👉 CPU 코어 수 + BCrypt cost = 로그인 처리량 상한


💡 8. 그래서 개선 방법은?

❌ 효과 없는 것

  • DB 인덱스 추가
  • 쿼리 튜닝
  • 커넥션 풀 증가

✅ 현실적 선택지

1. BCrypt cost 환경별 분리

dev / qa → cost ↓
prod → 유지

2. CPU 코어 수 증가

  • 수평 확장 (인스턴스 추가)
  • 수직 확장 (vCPU 증가)

3. 로그인 빈도 줄이기

  • Access Token TTL 연장
  • Refresh Token 적극 사용

4. 구조적 개선

  • OAuth 위임 (Google, Kakao 등)
  • Passwordless (이메일/SMS 인증)

9. 최종 종합 결론

이번 결과는 장애가 아니라
보안이 의도대로 작동하고 있다는 증거

종합 평가

항목상태
아키텍처✅ 정석
병목 원인✅ 명확 (BCrypt + CPU)
DB/네트워크/JVM✅ 정상
개선 방향선택의 문제

💬 10. 한 줄 요약

로그인은 느려도 된다.
대신, 안전해야 한다.


느낀점

1. 성능 테스트의 올바른 접근

잘못된 접근:

  • "느리니까 무조건 최적화"
  • "인덱스부터 추가"
  • "캐시부터 도입"

올바른 접근:
1. 병목 지점 정확히 식별
2. 병목이 의도된 것인지 판단
3. 비즈니스 요구사항과 보안 균형 고려
4. 실질적 개선 방안 도출


2. CPU-bound vs I/O-bound 구별

CPU-bound (이번 사례):

  • CPU 100% 사용
  • 메모리/디스크/네트워크 여유
  • 연산 집약적 작업 (암호화, 압축, 인코딩)

I/O-bound:

  • CPU 여유
  • 디스크 I/O wait 높음
  • DB 쿼리, 파일 읽기/쓰기 대기

3. 보안과 성능의 트레이드오프

보안 기술은 종종 의도적으로 느리게 설계된다.

이유:

  • 공격자의 시도 비용 증가
  • 무차별 대입 공격 방어
  • 서버 부하 < 사용자 데이터 보호

교훈:

모든 '느림'이 최적화 대상은 아니다.
때로는 '느림'이 정답이다.


🔗 참고 자료


마치며

이번 부하 테스트를 통해 다음을 배울 수 있었다.

  1. 문제의 본질 파악: 표면적 증상이 아닌 근본 원인 분석
  2. 데이터 기반 의사결정: 추측이 아닌 모니터링 데이터 기반 판단
  3. 보안과 성능의 균형: 무조건적 최적화보다 목적에 맞는 선택

핵심 메시지:

좋은 아키텍처는 빠른 것이 아니라
목적에 맞게 설계된 것이다.

다음에는 실제 프로덕션 환경에서의 로그인 최적화 전략OAuth 전환 과정을 다뤄볼 예정이다.

profile
Backend Developer

0개의 댓글