K6로 API 부하테스트 해본 후기 🚀

Twoweekhee·2025년 5월 20일

최근에 팀에서 "우리 API가 실제로 얼마나 많은 트래픽을 처리할 수 있을까?"라는 궁금증이 생겨서 K6를 사용해 부하테스트를 진행해봤습니다. 생각보다 재미있는 경험이었어서 공유해보려고 해요.

🎯 테스트 대상 선정

가장 먼저 어떤 API들을 테스트할지 정했습니다. 실제 사용자들이 자주 사용하는 API들을 중심으로 선택했어요:

  • 위치 관련 API 📍: 사용자 위치 저장 및 업데이트
  • 결제카드 API 💳: 카드 목록, 추천 카드, 개인 카드 조회
  • 가족 정보 API 👨‍👩‍👧‍👦: 가족 구성원 상세 정보

실제 사용 패턴과 비슷하게 이 API들을 랜덤하게 호출하는 방식으로 테스트를 설계했습니다.

💡 왜 K6를 선택했을까?

여러 부하테스트 툴 중에서 K6를 선택한 이유는 다음과 같습니다:

  • JavaScript 기반이라 개발팀이 쉽게 접근할 수 있었어요
  • 설치가 간단하고 가벼워서 로컬 환경에서도 충분히 테스트 가능
  • 점진적 부하 증가 기능이 우리가 원하는 테스트 시나리오와 잘 맞았음
  • 결과 시각화도 쉽게 할 수 있어서 팀과 공유하기 좋았습니다

🔧 K6 설치 및 실행

macOS 환경에서는 Homebrew를 통해 간단하게 설치할 수 있습니다:

bash# K6 설치
brew install k6

테스트 스크립트 작성

vi api_load_test.js

테스트 실행 (결과를 JSON으로 저장)

k6 run --out json=results.json api_load_test.js

🔍 테스트 시나리오 구성

현실적인 상황을 고려해서 3가지 시나리오를 준비했습니다:

  1. 인증 없는 요청 🚫
    토큰 없이 API를 호출해서 403 에러가 제대로 반환되는지 확인

  2. 잘못된 토큰 요청 ⚠️
    유효하지 않은 토큰으로 요청해서 보안이 올바르게 작동하는지 테스트

  3. 정상적인 인증 요청 ✅
    올바른 토큰으로 실제 사용자 시나리오를 모방한 테스트

📈 점진적 부하 증가 전략

갑작스러운 트래픽 폭증보다는 점진적인 증가가 현실적이라고 판단해서 다음과 같이 설계했어요:

1분: 초당 10개 요청 (워밍업) 🚶
2분: 초당 20개 요청
3분: 초당 50개 요청 🏃‍♀️
4분: 초당 60개 요청
5분: 초당 80개 요청
6~7분: 초당 100개 요청 (최대 부하) 🔥

💻 K6 스크립트 구현

기본 설정

javascript
export const options = {
  scenarios: {
    ramping_load: {
      executor: 'ramping-arrival-rate',
      startRate: 5,
      timeUnit: '1s',
      preAllocatedVUs: 50,
      maxVUs: 300,
      stages: [
        { duration: '1m', target: 10 },
        { duration: '1m', target: 20 },
        // ... 점진적 증가
      ],
    }
  },
  thresholds: {
    http_req_duration: ['p(95)<2000'], // 95%의 요청이 2초 이내
    http_req_failed: ['rate<0.01'],    // 실패율 1% 미만
  },
};

API 호출 함수 예시

javascript
function saveLocation() {
  const payload = JSON.stringify({
    userId: Math.floor(Math.random() * 1000000),
    latitude: (LAT_BASE + Math.random() * 0.1).toFixed(10),
    longitude: (LON_BASE + Math.random() * 0.1).toFixed(10),
    batteryLevel: randomIntBetween(10, 100).toString(),
  });

  const response = http.post(`${BASE_URL}/api/locations`, payload, { 
    headers: AUTH_HEADERS 
  });
  
  return check(response, {
    'Location save success': (r) => r.status === 200,
  });
}

스마트 로깅 시스템

부하가 높아질수록 로그 출력을 줄여서 성능에 미치는 영향을 최소화했습니다:

javascript
function shouldLog() {
  const currentRate = __ENV.CURRENT_RATE || 0;
  
  if (currentRate <= 10) {
    return __ITER % 10 === 0;  // 낮은 부하: 자주 로그
  } else if (currentRate <= 50) {
    return __ITER % 50 === 0;  // 중간 부하: 적당히
  } else {
    return __ITER % 100 === 0; // 높은 부하: 가끔만
  }
}

📊 결과 분석

테스트 완료 후 다음과 같은 메트릭을 확인할 수 있었습니다:

  • 총 요청 수: 실행된 전체 요청 개수
  • 성공률: 정상 처리된 요청의 비율
  • 평균 응답 시간: 전체 요청의 평균 처리 시간
  • 95 백분위 응답 시간: 대부분의 사용자가 경험하는 응답 시간
  • 최대 동시 사용자 수: 시스템이 처리할 수 있는 최대 부하

🎓 배운 점들

점진적 부하 증가의 중요성
갑작스러운 부하보다는 천천히 증가시키는 것이 실제 상황과 유사하고, 시스템의 한계점을 정확히 파악할 수 있었습니다.

로그 관리의 필요성
높은 부하 상황에서는 로그 출력 자체도 성능에 영향을 줄 수 있다는 걸 깨달았어요.

다양한 시나리오 테스트의 가치
단일 API보다는 실제 사용 패턴을 모방한 혼합 시나리오가 더 의미 있는 결과를 제공했습니다.

🚀 다음 계획

  • CI/CD 통합: 배포 파이프라인에 부하테스트를 포함시켜 자동화된 성능 검증
  • 정기적인 모니터링: 주기적인 성능 테스트로 회귀 방지
  • 복합 시나리오: 여러 API를 순차적으로 호출하는 더 복잡한 - 사용자 플로우 테스트

마무리

K6를 사용한 부하테스트는 생각보다 간단하면서도 강력한 도구였습니다. 서비스의 성능 한계를 미리 파악하고 대비할 수 있어서 매우 유용했어요.

아직 부하테스트를 도입하지 않으신 팀이 있다면 K6로 시작해보시는 걸 추천드립니다. 설치도 간단하고 학습 곡선도 완만해서 금방 익숙해지실 수 있을 거예요! 🙌

혹시 K6 사용하면서 궁금한 점이나 더 나은 방법이 있으시면 댓글로 공유해주세요!

profile
개발하는 감자, 이세상 감자 모여라~

0개의 댓글