한 줄 정의

핵심 메시지

성능 테스트는 “그냥 부하 한 번 쏴보는” 일이 아닙니다. 어떤 정량적 질문에 답하려는지부터 정해야 합니다. 테스트 종류마다 답이 달라지는 질문이 따로 있고, 통계적 해석 없이 평균만 보면 자바 가상 머신의 비정규 분포 앞에서 무너집니다.

쉽게 말하면

‘성능 테스트’라는 한마디로 모든 것을 뭉뚱그리는 것은 의사가 “검사 한번 해봅시다”라고만 말하는 것과 같습니다.

혈액 검사인지, MRI인지, 운동 부하 검사인지에 따라 알 수 있는 것이 다릅니다. 시스템도 마찬가지입니다. 지연 테스트로 한 거래의 시간을 잴 수도 있고, 부하 테스트로 “이 정도 트래픽 견딜 수 있나”를 이진적으로 답할 수도 있습니다.

가장 흔한 실수는 답하려는 질문 없이 일단 테스트부터 돌리는 것 입니다. 그리고 그 결과를 평균으로 요약합니다. 그러나 자바 애플리케이션에서 평균은 사용자가 겪는 진짜 경험을 거의 보여주지 못합니다.

왜 중요한가?

성능 테스트가 잘못된 이유로 수행되거나 부실하게 진행되는 경우가 많습니다. 대개는 성능 분석의 본질을 이해하지 못했거나, “아무것도 하지 않는 것보다는 무언가 하는 것이 낫다” 는 믿음에서 출발합니다.

이러한 믿음은 위험한 반쪽짜리 진실입니다. 잘못된 테스트는 잘못된 결론을 만들고, 그 결론을 근거로 한 최적화는 시간과 비용을 낭비할 뿐 아니라 실제 문제를 가립니다.

좋은 테스트의 조건

좋은 테스트는 정량적입니다. 실험 결과로 다루고 통계 분석에 적용할 수 있는 수치적 답변 을 도출하는 질문을 합니다.

테스트 종류를 결정하기 전에, 답하고자 하는 정량적 질문을 먼저 이해해야 합니다. 이 과정은 복잡할 필요가 없습니다. 테스트가 답하고자 하는 질문들을 간단히 적는 것만으로 충분합니다. 그리고 그 테스트가 애플리케이션에 왜 중요한지, 그 이유를 애플리케이션 소유자나 주요 고객과 확인하는 것이 일반적입니다.

핵심 내용

2.1 성능 테스트 종류

각 테스트 유형은 일반적으로 독립적인 목표를 가지며, 답하려는 질문이 다릅니다.

테스트 유형답하려는 질문
지연 테스트시작부터 끝까지의 트랜잭션 시간은 얼마나 걸립니까?
처리량 테스트현재 시스템 용량으로 몇 개의 동시 거래를 처리할 수 있습니까?
스트레스 테스트시스템의 한계점은 무엇인가요?
부하 테스트시스템이 특정 부하를 처리할 수 있나요?
내구성 테스트시스템을 장시간 실행했을 때 어떤 성능 이상이 발견될 수 있나요?
용량 계획 테스트추가 리소스를 투입했을 때 시스템이 예상대로 확장되나요?
성능 저하 테스트시스템이 부분적으로 장애가 발생했을 때 어떻게 처리되나요?

2.1.1 지연 테스트

지연은 가장 일반적인 성능 테스트 유형 중 하나입니다. 관리자나 사용자가 관심을 갖는 중요한 시스템 관측 지표이기 때문입니다. “우리 고객들이 트랜잭션이나 페이지 로드를 위해 얼마나 기다리고 있는가?” 와 같은 지표입니다.

질문의 단순함 때문에 지연 테스트에만 너무 집중하게 되는 경우가 발생할 수 있고, 이는 양날의 검이 될 수 있습니다. 다른 성능 테스트 유형에 대한 정량적 질문을 식별하는 필요성을 간과하게 만들 수 있습니다.

지연 조정의 목표

지연 조정 작업의 목표는 보통 사용자 경험을 직접적으로 개선 하거나 서비스 수준 계약(SLA)을 충족 시키는 것입니다.

가장 두드러지는 점은 단순한 평균값이 애플리케이션 요청에 얼마나 잘 반응하는지 측정하는 데 크게 유용하지 않다는 것입니다.

2.1.2 처리량 테스트

처리량은 성능 테스트에서 두 번째로 일반적인 측정 지표일 것입니다. 처리량 테스트는 지연 테스트와 어느 정도 이중적인 관계가 있습니다.

지연 테스트를 수행할 때는 지연 결과 분포를 생성하는 동안 동시 트랜잭션 수를 명확히 명시하고 제어 하는 것이 중요합니다. 마찬가지로 처리량 테스트를 수행할 때는 지연이 과도하게 증가하지 않도록 주의 해야 합니다.

지연과 처리량의 짝

시스템에서 관측된 지연 시간은 알려지거나 제어된 처리량 수준에서 명시 되어야 하며, 그 반대도 마찬가지입니다.

최대 처리량을 결정하는 방법은 지연 분포가 갑자기 변화하는 순간을 관찰하는 것입니다. 이는 사실상 시스템의 한계점, 즉 변곡점 입니다.

2.1.3 스트레스 테스트

스트레스 테스트는 시스템의 여유 용량이 얼마나 있는지 를 확인하기 위한 방법입니다. 시스템을 일정한 상태의 트랜잭션 상태에 두고, 동시 트랜잭션 수를 천천히 증가시켜 시스템 성능이 저하되기 시작하는 지점을 찾는 방식입니다.

관측 가능 항목들이 저하되기 시작하기 직전의 값이 스트레스 테스트에서 달성된 최대 처리량을 결정합니다.

2.1.4 부하 테스트

부하 테스트는 처리량 테스트(또는 스트레스 테스트)와는 다릅니다. 보통 “시스템이 예상 부하를 처리할 수 있는가?” 라는 이진적 테스트 로 정의됩니다.

예상되는 비즈니스 이벤트 전에 수행되며, 새 고객의 온보딩, 시장 진입, 광고 캠페인, 소셜 미디어 이벤트, 바이럴 콘텐츠 같은 이벤트 전에 실시됩니다.

2.1.5 내구성 테스트 (소크 테스트)

일부 문제들은 매우 오랜 시간 동안 또는 며칠 단위로 측정되곤 합니다. 여기에는 느린 메모리 누수, 캐시 오염, 메모리 단편화 등이 포함됩니다.

내구성 테스트(또는 소크 테스트)는 평균 사용률로 실행되며, 시스템의 실제 부하를 반영합니다. 테스트 중에 자원 수준을 면밀히 모니터링하여 자원의 소모나 고장을 감지합니다.

이러한 유형의 테스트는 저지연 시스템에서 더 흔히 수행 되는데, 전체 가비지 컬렉션 주기에 의한 스톱-더-월드(STW) 이벤트로 발생하는 지연 시간을 견디지 못할 가능성이 높기 때문입니다.

내구성 테스트는 실행하는 데 많은 시간이 걸리고 비용이 많이 듭니다. 장기간 동안 현실적인 데이터나 사용 패턴으로 테스트하는 것 자체도 내재된 어려움이 있습니다. 이러한 상황 때문에 많은 팀이 결국 “프로덕션 환경에서 테스트” 를 하게 됩니다.

이러한 유형의 테스트는 짧은 시간 내에 많은 코드 변경이 배포될 수 있는 마이크로서비스 또는 아키텍처에서는 항상 적용되지 않을 수도 있습니다.

2.1.6 용량 계획 테스트

용량 계획 테스트는 스트레스 테스트와 많은 유사점을 가집니다. 그러나 스트레스 테스트는 현재 시스템이 견딜 수 있는 한계 를 찾는 것이 목적이라면, 용량 계획 테스트는 더 미래지향적 입니다. 업그레이드된 시스템이 어떤 부하를 처리할 수 있는지 찾는 것을 목표로 합니다.

이런 이유로 용량 계획 테스트는 특정 이벤트나 위협에 대응하기보다, 장기적인 계획 활동의 일환 으로 수행되는 경우가 많습니다.

2.1.7 성능 저하 테스트 (부분 실패)

예전에는 엄격한 장애 조치와 복구 테스트가 대부분 높은 규제와 면밀한 검사가 이루어지는 환경(은행, 금융기관)에서만 수행되었습니다. 그러나 애플리케이션이 클라우드로 이동하면서 클러스터형 배포(쿠버네티스 기반)가 더 일반화되었고, 더 많은 개발자들이 클러스터형 애플리케이션의 실패 모드에 대해 인지해야 한다는 결과를 낳았습니다.

이 테스트의 기본 접근 방식은 시스템이 정상적인 프로덕션 볼륨과 동등한 시뮬레이션된 부하에서 실행되는 동안, 특정 구성 요소나 전체 하위 시스템이 갑자기 용량을 상실 할 때 시스템이 어떻게 동작하는지를 관찰하는 것입니다. 애플리케이션 서버 클러스터가 갑자기 멤버를 잃거나 네트워크 대역폭이 갑자기 떨어지는 경우가 그 예시입니다.

카오스 몽키 (Chaos Monkey)

부분 실패 테스트의 특히 흥미로운 하위 유형으로, 넷플릭스가 자사 인프라의 강건성을 검증하기 위해 수행한 프로젝트에서 유래했습니다.

카오스 몽키의 기본 아이디어는 진정으로 회복력 있는 아키텍처에서는 단일 구성 요소의 실패가 연쇄적인 실패를 유발하거나 시스템 전체에 유의미한 영향을 미쳐서는 안 된다 는 것입니다. 운영자가 프로덕션 환경에서 무작위로 라이브 프로세스를 종료함으로써 이 가능성을 직접 확인하게 합니다.

실패를 대비하여 설계된 시스템

잘 설계된 클라우드 시스템은 “실패를 대비하여 설계된 것” 이라고 말합니다. 충분히 큰 규모의 배포에서는 어느 시점이든 시스템의 일부가 고장 나 있다는 것을 의미합니다. 완벽하게 작동하는 시스템은 작은 시스템 규모의 산물일 뿐, 일반적인 상황은 아닙니다.

이 가정 차이가 코드 스타일을 바꿉니다. 정적 인프라에 대한 대규모 선투자를 기반으로 한 시스템(최소한으로 실패하도록 설계된 시스템) 을 위한 코드와, 클라우드를 위한 코드 는 매우 다르게 보입니다.

2.2 모범 사례 개론

성능 조정 작업에서 집중할 지점을 결정할 때 다음 세 가지 규칙을 고려하는 것이 좋습니다.

  1. 중요한 요소가 무엇인지 파악하고, 이것을 측정하는 방법을 찾으세요.
  2. 최적화하기 쉬운 것보다 중요한 것 을 최적화하세요.
  3. 가장 큰 영향을 미치는 요소부터 최적화를 시작하세요.

두 번째 규칙과 관련해, 쉽게 측정할 수 있는 수치에 지나치게 집착하지 않도록 주의해야 합니다. 모든 관찰 가능한 수치가 비즈니스에 중요한 것은 아니므로, 단순히 측정하기 쉬운 것 대신 올바른 지표를 선택해야 합니다.

세 번째 규칙은 단순히 최적화하는 것 자체에 의미를 두고 사소한 부분을 최적화하는 함정 에 빠지지 않도록 유의해야 합니다.

2.2.1 탑다운 성능

많은 엔지니어가 처음에 간과하는 자바 성능의 특징이 있습니다. 자바 애플리케이션의 대규모 벤치마킹이 보통 코드의 작은 부분에서 정확한 수치를 얻는 것보다 훨씬 쉽다 는 점입니다.

탑다운 성능

애플리케이션 전체의 성능 동작을 분석하는 접근 방식을 일반적으로 ‘탑다운 성능’ 이라고 부릅니다.

이 점은 워낙 오해가 많아 책 본문에서는 마이크로벤치마킹 에 대해서는 다루지 않습니다. 마이크로벤치마킹이 대부분의 애플리케이션에서 갖는 유용성을 적절히 반영해, 부록에서 별도로 다룹니다.

탑다운 성능을 최대한 활용하려면 테스트 팀이 테스트 환경을 마련하고, 측정 또는 최적화할 항목을 명확히 이해하며, 이러한 성능 분석 작업이 전체 소프트웨어 개발 수명 주기에서 어떤 역할을 하는지 파악해야 합니다.

2.2.2 테스트 환경 생성

테스트 환경 설정은 대부분의 성능 테스트 팀이 가장 먼저 해야 할 작업 중 하나입니다. 이 환경은 가능한 한 프로덕션 환경과 모든 면에서 동일하게 구성 되어야 합니다.

환경 차이의 위험

성능 테스트 환경이 실제 프로덕션 배포 환경과 크게 다를 경우, 테스트 결과가 실제 환경에서 활용할 만한 예측 가능하거나 유용한 정보를 제공하지 못할 수도 있습니다.

여기에는 애플리케이션 서버(동일한 CPU 개수, 운영 체제, 자바 런타임 버전 등)뿐만 아니라 웹 서버, 데이터베이스, 메시지 큐 등이 포함됩니다. 프로덕션 환경과 동일한 부하를 처리할 수 없는 서드파티 네트워크 서비스 같은 요소는 현실적인 성능 테스트를 위해 목(Mock) 서비스로 대체 해야 합니다.

전통적인, 즉 클라우드 기반이 아닌 환경에서는 이론적으로 프로덕션과 유사한 성능 테스트 환경을 구성하는 것이 비교적 간단합니다. 그러나 관리자는 추가 인프라 비용에 대한 부담을 느낄 수 있는데, 이는 오히려 잘못된 경제적 판단일 수 있습니다. 많은 조직이 중단으로 발생하는 비용을 정확히 반영하지 않아, 발생하는 위험을 간과하는 경우가 많기 때문입니다.

클라우드 기술의 발전으로 상황은 달라졌습니다. 온디맨드(on-demand) 인프라, 자동 확장(autoscaling) 기능, 서버를 ‘애완동물’이 아닌 ‘가축’처럼 관리하는 불변 인프라 접근 방식이 널리 사용되면서, 프로덕션과 유사한 성능 테스트 환경을 보다 손쉽게 구성할 수 있게 되었습니다.

하지만 여전히 몇 가지 중요한 고려 사항이 남아 있습니다.

  • 테스트 환경에서 변경 사항을 먼저 적용한 후 프로덕션에 반영할 수 있는 프로세스를 갖출 것
  • 테스트 환경에서 의도치 않은 프로덕션 의존성을 제거할 것
  • 테스트 환경에서 실제 인증 또는 권한 시스템을 적용하고, 더미 구성 요소를 사용하지 않을 것

2.2.3 성능 요구 사항 식별

시스템의 전체 성능은 애플리케이션 코드만으로 결정되지 않습니다. 컨테이너, 운영 체제, 하드웨어 등 다양한 요소가 성능에 영향 을 미칩니다.

따라서 성능을 평가할 때 사용하는 지표를 특정 요소에만 한정해서는 안 됩니다. 시스템을 전체적으로 바라보며, 고객과 경영진에게 중요한 관찰 가능한 지표들을 고려해야 합니다. 이러한 지표들은 보통 성능 비기능 요구 사항(NFRs) 이라고 하며, 최적화하려는 주요 대상입니다.

다음 성능 목표는 누구나 쉽게 이해할 수 있습니다.

  • 95% 기준 트랜잭션 시간을 100밀리초 줄이기
  • 현재 하드웨어로 처리량을 5배 높이기
  • 평균 응답 시간을 30% 개선하기

하지만 일부 목표는 더 복잡할 수 있습니다.

  • 고객 1명을 서비스하는 데 드는 리소스 비용을 50% 줄이기
  • 애플리케이션 클러스터가 절반으로 줄어들더라도 목표 성능의 25% 이내로 유지하기
  • 지연 시간을 10밀리초 단축해 고객 ‘이탈(drop-off)’ 비율을 25% 낮추기

킥오프 회의에서 합의

이처럼 구체적으로 무엇을 측정할지, 어떤 목표를 달성할지를 이해 관계자들과 미리 논의하는 것이 중요합니다. 이상적으로는 이러한 논의가 성능 개선 작업의 첫 번째 킥오프 회의 에서 이루어져야 합니다.

2.2.4 소프트웨어 개발 수명 주기와 성능 테스트

일부 회사나 팀은 성능 테스트를 가끔 수행하는 일회성 활동으로 여기는 경우가 많습니다. 하지만 더 발전된 팀들은 성능 테스트, 특히 성능 저하 테스트(performance regression testing) 를 소프트웨어 개발 수명 주기(SDLC)의 중요한 부분으로 인식하고 지속적으로 수행합니다.

이를 위해 개발자와 인프라 팀이 협력하여, 성능 테스트 환경에서 언제 어떤 버전의 코드가 적용되는지를 체계적으로 관리해야 합니다. 또한 이러한 성능 테스트를 위해 전용 테스트 환경 이 반드시 마련되어야 합니다.

2.2.5 자바 특유의 문제들

성능 분석에 관한 과학적 지식은 대부분 현대 소프트웨어 시스템에 적용될 수 있습니다. 하지만 자바 가상 머신의 특성상 성능 엔지니어가 추가적으로 고려해야 할 몇 가지 복잡한 요소가 있습니다. 이러한 요소들은 자바 가상 머신의 동적 자가 관리 기능 에서 비롯되며, 메모리 영역의 동적 튜닝과 JIT 컴파일이 대표적입니다.

현재 자바 가상 머신은 실행 중인 메서드를 분석해 최적화된 기계어 코드로 변환하기 위한 JIT 컴파일 후보를 자동으로 선정합니다. 만약 특정 메서드가 JIT 컴파일되지 않는다면, 그 이유는 다음 두 가지 중 하나일 가능성이 큽니다.

  • 메서드가 컴파일될 만큼 자주 실행되지 않는다.
  • 메서드가 너무 크거나 복잡하여 컴파일을 위한 분석이 불가능하다.

참고로 두 번째 조건은 첫 번째 조건에 비해 훨씬 드물게 발생합니다.

2.3 성능 안티 패턴의 원인

안티 패턴(antipattern) 은 여러 소프트웨어 프로젝트나 팀에서 공통적으로 나타나는 바람직하지 않은 행동이나 방식입니다. 이렇게 자주 발생하는 것을 보면, 이러한 원하지 않는 행동을 유발하는 근본적인 원인이 있을 것으로 추측됩니다.

이러한 안티 패턴은 때로는 팀 내부의 제약, 잘못된 관리 방식, 혹은 개발자 특유의 인간적인 성향 때문에 발생하기도 합니다. 이러한 문제들을 분류하고 정의함으로써 프로젝트에서 이를 논의하고 제거하는 데 도움이 되는 패턴 언어 를 개발할 수 있습니다.

많은 사람이 새로운 클라이언트가 서비스를 시작하거나 새로운 기능이 출시될 때 예상치 못한 오류가 발생하는 경험을 해봤을 것입니다. 운이 좋다면 사용자 수용 테스트(UAT) 단계에서 발견하지만, 대부분은 실제 서비스 중에 문제가 드러납니다. 이런 상황에서는 팀이 급히 원인을 찾아 문제를 해결해야 하며, 이는 성능 테스트가 제대로 이루어지지 않았거나, 개인적인 판단에 의존한 누군가가 떠났기 때문 입니다.

2.3.1 지루함

대부분의 개발자는 일을 하면서 지루함을 느껴본 적이 있을 것입니다. 일부 개발자는 그 지루함이 오래가지 않더라도 새로운 도전이나 역할을 찾기 위해 움직이곤 합니다.

지루함을 느끼는 개발자는 프로젝트에 여러 가지 부정적인 영향을 미칠 수 있습니다. 예를 들어, 불필요하게 복잡한 코드 를 도입할 수 있습니다. Collections.sort()처럼 간단한 기능으로 충분한 경우에도 직접 정렬 알고리즘을 작성하는 식입니다. 또 자신의 지루함을 해소하기 위해 프로젝트와 맞지 않거나 잘 모르는 기술을 사용하여 새로운 컴포넌트를 만들려고 할 수도 있습니다.

2.3.2 이력 부풀리기

때로는 개발자가 특정 기술을 과도하게 사용하는 이유가 지루함이 아닌, 이력서나 CV에 해당 기술 경험을 추가하여 경력 시장에서 자신을 더 유리한 위치에 두기 위한 것 일 수도 있습니다.

이러한 경우, 개발자는 곧 구직 시장에 나설 준비를 하면서 잠재적인 연봉과 시장성을 높이기 위해 적극적으로 새로운 기술을 시도하는 것입니다.

개발자의 지루함이나 이력서 관리를 위해 불필요한 기술이 추가되면 그 결과는 광범위하고 오래 지속될 수 있습니다. 특히, 원래 개발자가 떠난 후에도 해당 기술의 도입으로 팀이 여러 해 동안 부담을 안게 될 가능성이 있습니다.

2.3.3 사회적 압박

기술적 결정이 가장 나쁠 때는 결정을 내리는 과정에서 우려 사항이 충분히 표현되거나 논의되지 않았을 때입니다. 주니어 개발자가 시니어 앞에서 실수를 드러내고 싶지 않아 의견을 내지 않거나, 특정 주제에 대한 무지를 드러낼까 두려워 침묵하는 경우가 있습니다.

특히 경쟁이 치열한 팀에서는 개발 속도를 중시하는 분위기 속에서 중요한 결정을 서둘러 내리는 일이 발생할 수 있으며, 이 때문에 충분한 고려 없이 진행된 선택이 문제로 이어질 가능성이 큽니다.

2.3.4 이해 부족

개발자가 문제를 해결하기 위해 새로운 도구를 도입하려는 시도는 종종 기존 도구의 기능을 충분히 이해하지 못한 곳 에서 비롯됩니다. 새로운 기술이 특정 작업에서 뛰어난 성능을 발휘할 수 있지만, 도입 시 기술적 복잡성이 추가될 수 있으며 기존 도구가 해결할 수 있는지 신중하게 따져봐야 합니다.

예를 들어, 하이버네이트(Hibernate)는 객체와 데이터베이스 간의 변환을 단순화하는 솔루션으로 보일 수 있습니다. 그러나 이를 충분히 이해하지 못한 상태에서는 오히려 시스템의 복잡성을 더하거나 장애를 유발할 위험이 있습니다. 익숙한 JDBC 호출을 사용하는 것 이 이러한 위험을 줄이는 방법이 될 수 있습니다.

2.3.5 문제에 대한 오해 또는 문제 자체의 부재

기술을 도입하려는 이유가 명확하게 검토되지 않으며, 문제 자체를 제대로 파악하지 않은 상태에서 해결책이 제시되는 경우가 있습니다. 성능 값을 측정하지 않으면 특정 솔루션이 성공적인지 판단하기 어려우며, 성능 측정을 통해 문제를 보다 정확히 이해할 수 있습니다.

기술적 문제에 대한 논의가 팀 내에서 자유롭게 이루어지도록 독려하고, 불확실한 점이 있다면 실제 데이터를 수집하거나 프로토타입을 만들어 검증하는 과정을 거쳐야 합니다. 새로운 기술이 매력적으로 보이더라도, 프로토타입 결과가 기대에 미치지 못한다면 팀은 보다 신중한 결정을 내릴 수 있습니다.

2.4 자바 가상 머신 성능을 위한 통계

성능 분석이 진정한 실험이라면, 결국 데이터 결과의 분포를 다루어야 합니다. 통계학자와 과학자들은 실제 데이터가 명확하고 깨끗한 신호로 나타나지 않는다는 사실을 잘 알고 있습니다.

W. 에드워즈 데밍

우리는 신을 믿습니다. 하지만 다른 모든 사람은 데이터를 사용해야 합니다.

모든 측정에는 어느 정도의 오차가 포함되므로, 각 실행에서 발생할 수 있는 오차를 최소화하기 위해 반복 측정이 필요합니다. 자바 성능 평가의 표준은 2007년에 발표된 「통계적으로 엄격한 자바 성능 평가」 논문에서 제시되었습니다. 이에 따르면, 동적인 소프트웨어 시스템(자바 가상 머신)에서 통계적으로 유의미한 결과를 얻으려면 최소 30번의 실행 이 필요합니다.

2.4.1 오차 유형

엔지니어가 성능 분석에서 마주할 수 있는 주요 오차는 두 가지가 있습니다.

  • 무작위 오차: 측정 과정에서 발생하는 오차로, 서로 관련 없는 다양한 요인이 결과에 영향을 미치는 경우를 말합니다.
  • 체계적 오차: 특정 요인이 지속적으로 결과 측정에 영향을 미치는 경우를 말합니다.

이 두 가지 오차 유형과 관련된 용어가 있습니다.

용어대응 오차의미
정확도(accuracy)체계적 오차높을수록 체계적 오차가 적음
정밀도(precision)무작위 오차높을수록 무작위 오차가 적음

다트 판 비유로 정리하면 네 가지 경우가 나옵니다.

경우정확도정밀도설명
1높음높음측정값이 타겟 중심에 모여 있음
2낮음높음한쪽으로 치우쳐 모여 있음 (조준 오류)
3높음낮음넓게 퍼져 있지만 평균은 중심에 가까움
4낮음낮음뚜렷한 패턴 없이 흩어져 있음
무작위 오차

무작위 오차는 환경의 예측할 수 없거나 알 수 없는 변화로 발생합니다. 소프트웨어의 경우, 측정 장치가 신뢰할 수 있다고 가정 하기 때문에 무작위 오차의 원인은 운영 환경에서만 발생한다고 봅니다.

무작위 오차는 일반적으로 정규(가우스) 분포 를 따르는 것으로 간주됩니다. 이 정규 분포는 오차가 관측값에 미치는 양의 영향과 음의 영향이 동일한 확률로 발생하는 경우를 모델링하는 데 적합합니다. 그러나 자바 가상 머신 측정에서는 이보다 더 복잡한 요인이 작용합니다.

체계적 오차

체계적 오차의 예로, JSON을 송수신하는 백엔드 자바 웹 서비스 그룹에 대해 성능 테스트를 수행하는 경우를 생각해 봅시다. 아파치 제이미터(Apache JMeter) 부하 생성 도구에서 생성한 그래프에는 두 가지 체계적 효과가 나타날 수 있습니다.

선형 패턴 (서버 자원 소진)

최상단 선(이상치 서비스)에서 관찰되는 선형 패턴은 서버의 제한된 리소스가 서서히 소진되는 현상을 보여줍니다. 이런 유형의 패턴은 주로 메모리 누수(memory leak) 나 요청 처리 과정에서 스레드가 사용한 리소스가 적절히 해제되지 않는 경우와 관련이 있으며, 조사할 잠재적 문제로 간주됩니다.

단정 금지

영향을 받은 리소스 유형을 확인하려면 추가 분석이 필요합니다. 단순히 메모리 누수라고 단정할 수는 없습니다.

일관된 응답 시간 (네트워크 지연)

두 번째로 주목해야 할 효과는 대부분의 다른 서비스가 약 180ms 수준에서 일관된 응답 시간을 보인다는 점입니다. 서비스마다 요청 처리량이 크게 다름에도 불구하고 동일한 패턴이 나타난 것은 의문을 자아냅니다.

그 이유는 테스트 대상 서비스가 런던에 위치해 있지만, 이번 부하 테스트가 인도 뭄바이에서 실행되었기 때문입니다. 관찰된 응답 시간에는 뭄바이와 런던 간 왕복 네트워크 지연 시간 이 포함되며, 이는 최소 120~150ms에 달합니다.

이처럼 네트워크 지연이 개별 서비스의 실제 응답 시간 차이를 가렸습니다. 즉, 서비스는 실제로 120ms보다 훨씬 짧은 시간 내에 응답하고 있었던 것입니다. 이는 애플리케이션 자체에는 문제가 없으며, 테스트 환경이 실제 프로덕션 환경을 정확히 재현하지 못해 발생한 오류 라는 점을 보여주는 사례입니다. 다행히 테스트를 런던에서 다시 실행한 결과, 예상대로 이 현상은 완전히 사라졌습니다.

2.4.2 비정규 통계

정규 분포(normal distribution)에 기반한 통계는 복잡한 수학적 지식 없이도 쉽게 이해할 수 있습니다. 이 때문에 고등학교나 학부 수준에서 가르치는 통계는 주로 정규 분포 데이터를 분석하는 데 중점을 둡니다.

학생들은 평균과 표준 편차(standard deviation) (또는 분산) 계산 방법을 배우고, 때로는 왜도(skew)첨도(kurtosis) 같은 고차 모멘트(higher moment)도 다룹니다. 그러나 이러한 기법에는 한계가 있습니다. 분포에서 조금만 벗어난 이상치가 포함되더라도 결과가 쉽게 왜곡될 수 있기 때문입니다.

이상치의 중요성

자바 성능 분석에서 이상치는 느린 거래나 불만을 가진 고객 을 의미합니다. 따라서 이러한 데이터를 특별히 주의 깊게 살펴봐야 하며, 이상치의 중요성을 희석시키는 기법은 피해야 합니다.

자바 가상 머신의 트랜잭션 시간 분포는 정규 분포가 아닙니다. 보통은 왼쪽으로 치우치고 오른쪽으로 긴 꼬리를 가진 형태 를 보입니다. 이미 JIT 컴파일이 완료된 모든 관련 코드가 포함된 핫 패스(hot path) 가 존재하고, 가비지 컬렉터가 실행되지 않은 상태를 반영합니다. 이는 최상의 시나리오(또한 자주 발생하는 시나리오)를 나타내며, 단순한 무작위 효과 때문에 ‘조금 더 빠른(a bit faster)’ 호출이 발생하는 것은 아닙니다.

비정규 분포의 함정

비정규 분포에서는 많은 ‘기본 규칙’이 적용되지 않습니다. 특히 표준 편차, 분포, 기타 고차 모멘트들은 거의 의미를 가지지 않게 됩니다.

다른 관점에서 보면, 고객들이 대거 불만을 제기하지 않는 이상, 평균 응답 시간을 개선하는 것이 실질적인 성능 목표가 되기는 어렵습니다. 현실적으로는 몇몇 불만족스러운 고객 사례를 해결하기 위해 지연 시간 최적화 작업이 이루어지는 경우가 더 많습니다. 이는 대다수 고객이 만족스러운 서비스를 받고 있음에도, 이상치 이벤트가 더 큰 관심을 끈다 는 것을 암시합니다.

수정된 백분위수 체계

자바 가상 머신이 생성하는 비정규의 롱테일(long-tail) 분포 를 다룰 때 유용한 기법 중 하나는 수정된 백분위수 체계를 사용하는 것입니다. 데이터 분포는 단순히 하나의 숫자로 요약하기 어려우며, 전체적인 데이터 포인트 집합(데이터의 모양)을 고려해야 합니다.

평균처럼 분포 전체를 단일 결과로 표현하기보다는, 일정한 간격을 두고 샘플을 추출하는 방법을 활용할 수 있습니다. 정규 분포 데이터에서는 보통 일정한 간격으로 샘플을 채취합니다. 그러나 이 방식을 약간 변형하면 자바 가상 머신 통계에 더 효과적으로 적용할 수 있습니다.

이 수정된 방법에서는 평균을 기준으로 90번째 백분위수까지 이동한 후, 그 이후부터는 로그 스케일을 적용 하여 샘플을 추출합니다.

50.0%   level was 23 ns
90.0%   level was 30 ns
99.0%   level was 43 ns
99.9%   level was 164 ns
99.99%  level was 248 ns
99.999% level was 3,458 ns
99.9999% level was 17,463 ns

샘플 데이터를 보면, getter 메서드의 평균 실행 시간은 23ns였지만, 1,000번 중 1번의 요청은 평균보다 10배 더 오래 걸렸으며, 100,000번 중 1번의 요청은 평균보다 100배 더 오래 걸렸습니다.

HdrHistogram

이러한 롱테일 분포는 고동적 범위(high dynamic range, HDR) 분포 라고도 불립니다. 관측값의 동적 범위는 일반적으로 가장 큰 기록값을 가장 작은 값(0이 아닌 경우)으로 나눈 값으로 정의됩니다.

로그 스케일의 백분위수는 롱테일 분포를 이해하는 데 유용한 도구입니다. 더 정교한 분석이 필요할 경우, 고동적 범위를 가진 데이터셋을 다룰 수 있는 공개 라이브러리를 사용할 수 있습니다. 대표적인 예로 HdrHistogram 이 있습니다.

히스토그램의 동작 방식

히스토그램은 고유한 범위 집합(버킷(Bucket))을 사용해 데이터를 요약하며, 각 버킷에 얼마나 자주 데이터가 포함되는지를 보여주는 방식입니다.

메이븐 의존성:

<dependency>
    <groupId>org.hdrhistogram</groupId>
    <artifactId>HdrHistogram</artifactId>
    <version>2.1.12</version>
</dependency>

간단한 사용 예제로, 숫자가 포함된 파일을 입력으로 받아 연속된 값 간 차이에 대한 HdrHistogram을 계산하는 코드입니다.

public class BenchmarkWithHdrHistogram {
    private static final long NORMALIZER = 1_000_000;
 
    private static final Histogram HISTOGRAM
            = new Histogram(TimeUnit.MINUTES.toMicros(1), 2);
 
    public static void main(String[] args) throws Exception {
        final List<String> values = Files.readAllLines(Paths.get(args[0]));
        double last = 0;
        for (final String tVal : values) {
            double parsed = Double.parseDouble(tVal);
            double gcInterval = parsed - last;
            last = parsed;
            HISTOGRAM.recordValue((long)(gcInterval * NORMALIZER));
        }
        HISTOGRAM.outputPercentileDistribution(System.out, 1000.0);
    }
}

출력은 연속적인 가비지 컬렉션 사이의 시간을 보여줍니다. 가비지 컬렉션은 일정한 간격으로 발생하지 않으며, 얼마나 자주 실행되는지를 파악하는 것이 중요할 수 있습니다.

조정된 누락 (Coordinated Omission)

HdrHistogram은 조정된 누락(coordinated omission) 을 감지하고 이해하는 데도 큰 도움이 됩니다. 이는 측정 시스템이 측정 대상 시스템과 의도치 않게 동기화되어 이상치를 잘못 측정하거나, 일부 요청이 누락되어 전송되지 않는 현상 을 설명하는 용어입니다.

2.5 통계 해석

실제 데이터와 관측된 결과는 별개로 존재하는 것이 아니며, 애플리케이션을 측정하여 얻은 결과를 정확하게 해석하는 것이 가장 어려운 작업입니다.

제럴드 와인버그

어떤 문제든 결국 사람의 문제입니다.

해석 과정에서는 다양한 문제가 발생할 수 있습니다. 그 중에서도 체계적 오류와 함께 자주 발생하는 대표적인 문제로 거짓 상관관계(spurious correlation) 가 있습니다.

2.5.1 거짓 상관관계

통계에서 가장 유명한 격언은 ‘상관관계는 인과관계를 의미하지 않는다(correlation does not imply causation)’ 입니다. 두 변수가 유사한 패턴을 보인다고 해서 실제로 서로 연결되어 있다고 단정할 수는 없습니다.

이 개념은 성능 엔지니어가 반드시 이해해야 할 중요한 원칙입니다. 위키피디아에서는 상호 연관된 두 이벤트 A와 B 사이의 가능한 관계를 네 가지로 분류합니다.

  • A가 B를 유발한다 (직접적 인과관계, direct causation)
  • B가 A를 유발한다 (역방향 인과관계, reverse causation)
  • A와 B는 C라는 공통 요인으로 동시에 유발된다 (공통 인과관계, common causation)
  • A와 B 사이에는 아무런 연관이 없으며, 상관관계는 단순히 우연이다

첫 번째와 두 번째는 비교적 이해하기 쉽습니다. 세 번째 공통 인과관계는 두 변수가 실제로 연결되어 있지만, 이를 잘못 해석하여 직접적 인과관계를 추론하는 상황을 의미합니다.

예를 들어, 의료 분야에서 유아기에 모유 수유를 한 아기들이 성장 후 더 높은 IQ 점수를 보인다는 연구 결과가 있습니다. 이는 여러 연구에서 확인된 사실이지만, 원인은 모유 수유 자체가 아니라 다른 요인일 수도 있습니다. 모유 수유 비율이 높은 계층은 대체로 사회경제적 수준이 높은 경우가 많으며, 이에 아이의 정신적 발달을 위해 더 많은 시간을 투자할 가능성이 큽니다.

완전히 허구적인 상관관계

네 번째 경우의 극단적인 예로, 전혀 관련 없는 측정값 사이에서도 상관관계를 찾아낼 수 있는 상황이 있습니다. 미국의 1인당 닭고기 소비량과 미국 원유 총 수입량 사이에서 높은 상관관계가 나타나는 사례가 있습니다(2000~2009년 데이터).

또 다른 예로, 비디오 게임 아케이드에서 발생한 수익과 컴퓨터 과학 박사 학위 수여 건수 사이에도 상관관계가 나타납니다. 이는 “스트레스를 받은 박사 과정 학생들이 몇 시간 동안 비디오 게임을 하며 휴식을 취했다”는 식의 사회학적 연구로 이어질 수도 있지만, 실제로는 공통 요인조차 존재하지 않는 경우가 많습니다.

자바 가상 머신 분석에서의 주의

자바 가상 머신과 성능 분석에서는 단순한 상관관계를 근거로 측정값 간의 인과관계를 추정하지 않도록 특히 주의해야 합니다. 특정 연결이 ‘그럴듯해 보인다’ 는 이유만으로 속단해서는 안 됩니다.

리처드 파인먼

첫 번째 원칙은 스스로를 속이지 않는 것입니다. 가장 속이기 쉬운 사람은 바로 자신입니다.

실제 자바 애플리케이션의 메모리 할당 속도 예시를 보면, 비교적 성능이 잘 나오는 애플리케이션에서도 할당 속도가 초당 350MB에서 700MB 사이로 안정적이다가, 자바 가상 머신이 시작된 지 약 5시간 후부터 하락세가 시작되고, 9~10시간 사이에 최저점을 기록한 후 다시 상승하기 시작하는 패턴이 나타납니다.

이러한 추세는 매우 일반적인 현상입니다. 할당 속도는 애플리케이션이 실제로 처리하는 작업량을 반영하며, 이는 시간대에 따라 크게 변동할 수 있습니다. 그러나 실제 관측값을 해석할 때는 다양한 변수들이 영향을 미치므로 상황이 복잡해질 수 있습니다.

2.5.2 모자와 코끼리

이 문제는 흔히 모자와 코끼리 문제 라고 불리며, 앙투안 드 생텍쥐페리(Antoine de Saint-Exupéry)의 『어린왕자』에 등장하는 한 장면에서 유래합니다. 책에서 화자는 여섯 살 때 코끼리를 삼킨 보아뱀 그림을 그렸지만, 어른들의 눈에는 단순히 모양이 흐릿한 모자로만 보였습니다.

이 비유는 독자에게 상상력을 발휘하고, 단순한 표면적 설명에 만족하지 말고 더 깊이 생각할 것을 강조하는 메시지를 담고 있습니다.

소프트웨어에서 이 문제를 적용한 예시는 HTTP 요청-응답 시간의 복잡한 히스토그램에서 찾을 수 있습니다. 처음에는 여러 봉우리를 가진 복잡한 그래프로 보이지만, 『어린왕자』의 화자처럼 조금 더 깊이 분석해 보면 이 복잡한 그림이 실제로는 몇 가지 단순한 요소로 구성되어 있음을 알 수 있습니다.

응답 유형별 분해

응답 히스토그램을 해석하는 핵심은 ‘웹 애플리케이션 응답’ 이라는 범주가 매우 포괄적이며, 다양한 응답을 포함한다는 점을 이해하는 것입니다. 여기에는 다음이 포함됩니다.

  • 성공적인 요청 (일명 2xx 응답)
  • 클라이언트 오류 (4xx, 특히 잘 알려진 404 오류)
  • 서버 오류 (5xx, 특히 500 내부 서버 오류)

각 유형의 응답 시간은 서로 다른 분포 특성을 가집니다.

클라이언트가 존재하지 않는 URL에 요청을 보낸 경우(404 오류), 웹 서버는 즉시 응답할 수 있습니다. 따라서 클라이언트 오류 응답만을 따로 분석한 히스토그램은 매우 짧은 시간에 좁게 분포된 형태를 보입니다.

반면, 서버 오류는 많은 처리 시간이 소요된 후 발생하는 경우가 많습니다. 백엔드 리소스의 과부하나 시간 초과 때문입니다. 따라서 서버 오류 응답에 대한 히스토그램은 긴 처리 시간 영역에 분포가 몰린 형태를 보입니다.

성공적인 요청의 응답 시간은 긴 꼬리 분포를 나타내겠지만, 실제로는 여러 개의 국소 최댓값을 가진 다봉 분포(multimodal distribution) 형태일 수도 있습니다. 서로 다른 두 가지 응답 시간을 갖는 애플리케이션에서 공통된 실행 경로가 존재할 가능성을 보여주는 예시입니다.

모자의 재구성

이러한 다양한 유형의 응답을 하나의 그래프로 합치면 원래의 복잡한 모자 형태가 다시 만들어집니다. 이는 개별 히스토그램을 합쳐서 다시 원래의 모자 형태를 재구성한 것입니다.

데이터 세분화의 중요성

일반적인 관측값을 더 의미 있는 하위 집단으로 분해하는 개념은 매우 유용합니다. 결과에서 결론을 도출하기 전에, 데이터와 도메인을 충분히 이해해야 합니다. 따라서 데이터를 더 작은 집합으로 세분화하는 것이 중요합니다.

예를 들어, 성공적인 요청이라도 읽기 요청과 업데이트 또는 업로드 요청은 서로 매우 다른 분포를 가질 수 있습니다.

페이팔과 데이터사우루스 더즌

페이팔(PayPal)의 엔지니어링 팀은 통계와 분석 기법을 광범위하게 활용하며, 우수한 자료를 담은 블로그를 운영하고 있습니다. 특히 마흐무드 하셰미(Mahmoud Hashemi)의 「Statistics for Software」 글은 그들의 방법론을 잘 소개하며, 모자와 코끼리 문제의 변형 사례도 포함하고 있습니다.

또한 ‘데이터사우루스 더즌(Datasaurus Dozen)’ 도 주목할 만한 개념입니다. 이는 기본 통계(평균, 분산, 상관계수)는 같지만 시각적으로는 전혀 다른 형태를 띠는 데이터셋 모음입니다. 통계 요약만 보고 데이터를 판단하는 것의 위험을 시각적으로 보여주는 사례입니다.

2.6 인지적 편향과 성능 테스트

사람은 빠르고 정확한 판단을 결정하는 데 어려움을 겪을 수 있습니다. 과거 경험이나 유사한 상황에서 얻은 지식을 활용할 수 있는 경우에도 마찬가지입니다.

인지 편향(cognitive bias) 은 인간의 뇌가 잘못된 결론을 내리게 유도하는 심리적 효과입니다. 특히, 인지 편향을 겪는 사람은 대개 이를 인식하지 못하며, 오히려 자신이 합리적인 판단을 한다고 믿기 때문에 문제가 됩니다.

성능 분석에서 관찰되는 많은 안티 패턴은 대부분 하나 이상의 인지 편향에서 비롯됩니다. 이러한 편향은 무의식적인 가정을 기반으로 발생하는 경우가 많습니다.

책임 전가 (Blame Donkey)

예를 들어, 책임 전가(Blame Donkey) 안티 패턴에서는 특정 컴포넌트가 최근 여러 차례 장애를 일으킨 경우, 팀은 새로운 성능 문제의 원인도 해당 컴포넌트일 것이라고 쉽게 단정하는 경향이 있습니다. 그리고 분석된 데이터가 이 가설을 뒷받침하는 것처럼 보이면, 해당 데이터를 더욱 신뢰할 만한 정보로 받아들이게 됩니다.

이 안티 패턴은 확증 편향(confirmation bias)최신성 편향(recency bias) 의 특성이 결합된 사례입니다. 확증 편향은 자신의 기존 믿음이나 가설을 뒷받침하는 정보만을 찾으려는 경향을 의미하며, 최신성 편향은 최근에 일어난 일이 계속 발생할 것이라고 가정하는 경향을 의미합니다.

자바 단일 컴포넌트의 동작 차이

자바의 단일 컴포넌트는 런타임 최적화 방식에 따라 애플리케이션마다 다르게 동작할 수 있습니다. 따라서 기존 편향을 없애기 위해서는 애플리케이션 전체를 분석하는 것이 중요합니다.

편향의 상호작용

편향은 서로 보완적이거나 대조적일 수 있습니다. 예를 들어, 일부 개발자는 문제가 소프트웨어가 아니라 실행 환경인 인프라에 있다고 단정하는 경향이 있습니다. 이는 ‘내 환경에서는 문제 없음’ 안티 패턴에서 흔히 볼 수 있으며, “사용자 수용 테스트(UAT)에서는 정상 작동했으니 프로덕션 장비에 문제가 있을 것이다”와 같은 형태로 나타납니다.

반대로, 시스템의 모든 문제가 소프트웨어에서 비롯된다고 가정하는 편향도 존재합니다. 이는 개발자가 주로 소프트웨어 영역만을 다루며, 직접적인 영향을 미칠 수 있는 부분이 한정적이기 때문입니다.

레토 아트레이드 공작 1세

함정을 아는 것, 그것이 회피의 첫 걸음입니다.

2.6.1 환원주의적 사고 편향

환원주의적 사고 편향(reductionist thinking cognitive bias) 은 시스템을 충분히 작은 부분으로 나누면 구성 요소를 이해함으로써 전체 시스템을 파악할 수 있다는 분석적 접근에 기반합니다. 각 부분을 이해하면 잘못된 가정을 줄일 수 있다고 생각하는 것입니다.

하지만 복잡한 시스템에서는 이 관점이 성립되지 않습니다. 복잡한 소프트웨어나 물리적 시스템에서는 예측하기 어려운 창의적 행동이 자주 나타납니다. 이는 전체 시스템이 단순히 개별 구성 요소의 합 이상이라는 것을 의미합니다.

2.6.2 확증 편향

확증 편향은 성능 테스트나 애플리케이션을 객관적으로 분석하는 과정에서 큰 문제를 일으킬 수 있습니다. 이 편향은 보통 의도하지 않게 발생하며, 부적절한 테스트 집합이 선택되거나 통계적으로 타당한 분석이 이루어지지 않을 때 나타납니다. 특히, 팀 내에서 특정 의견을 증명하려는 동기나 감정적인 요인이 종종 작용하면서 더욱 극복하기 어려운 편향 중 하나입니다.

반짝이는 것에 한눈팔기 (Distracted by Shiny)

반짝이는 것에 한눈팔기(Distracted by Shiny) 안티 패턴을 예로 들어 보겠습니다. 팀원이 최신 NoSQL 데이터베이스를 도입하려고 한다고 가정해 봅시다. 이들은 전체 스키마를 평가하기엔 너무 복잡하다는 이유로, 실제 데이터와 유사하지 않은 데이터로 테스트를 진행합니다.

테스트 환경에서 NoSQL 데이터베이스가 로컬 머신에서 뛰어난 액세스 속도를 제공하는 결과를 빠르게 확인할 수 있습니다. 개발자는 이미 이 결과를 예상하고 있었으며, 이를 확인하자마자 전체 구현에 착수합니다. 하지만 여기에는 여러 가지 안티 패턴이 적용되었고, 그 결과 새로운 라이브러리 스택에 검증되지 않은 가정들이 추가되었습니다.

2.6.3 혼란 속의 행동 편향

혼란 속의 행동 편향(action bias) 은 시스템이 예상대로 작동하지 않거나 장애가 발생했을 때, 팀이 압박을 받는 상황에서 자주 나타납니다. 일반적인 원인은 다음과 같습니다.

  • 시스템이 실행되는 인프라의 변형(영향이 있을지 몰랐거나, 알림 없이 변경된 경우)
  • 시스템이 의존하는 라이브러리의 변경
  • 바쁜 날에만 발생하는 이상한 버그나 경쟁 조건

적절한 관측 도구가 있는 잘 유지된 애플리케이션에서는 문제의 원인을 파악할 수 있는 명확한 신호가 나타나야 합니다. 그러나 많은 애플리케이션은 실패 시나리오에 대한 테스트가 부족하거나, 진단을 위한 도구가 충분하지 않습니다.

이러한 상황에서 숙련된 엔지니어조차 장애를 해결해야 한다는 압박을 느끼기 쉽고, 단순한 움직임을 문제 해결 속도와 혼동하여 전쟁의 안개 속으로 빠져들 수 있습니다.

이때 문제 해결 접근 방식을 체계적으로 관리하지 않으면, 이 장에서 논의된 여러 인간적인 요소가 영향을 미칠 수 있습니다.

예를 들어, 책임 전가 안티 패턴은 전체적인 조사를 생략하고 특정 경로로 조사를 진행하도록 유도하여, 문제의 큰 그림을 놓칠 수 있습니다. 또한, 문제의 원인을 먼저 확인하지 않은 채 시스템을 개별 구성 요소로 나누어 코드의 세부적인 부분부터 검토하고자 하는 유혹에 빠질 수도 있습니다.

2.6.4 위험 편향

사람은 본래 위험을 회피하려는 성향이 강하며, 변화에 저항하는 경향이 있습니다. 이는 대부분 변화가 상황을 악화시킬 수 있다는 경험에서 비롯되며, 이를 피하려는 시도로 이어집니다. 하지만 계산된 작은 위험을 감수하면 제품을 발전시킬 수 있을 때에도, 이러한 성향은 오히려 큰 장애가 될 수 있습니다. 특히 성능 프로필에 영향을 줄 수 있는 변경을 주저하는 팀에서 이러한 위험 회피 성향이 두드러지게 나타납니다.

강력한 단위 테스트와 프로덕션 저하 테스트를 통해 이런 위험 편향을 상당 부분 줄일 수 있습니다. 성능 저하 테스트는 시스템의 비기능적 요구 사항(NFR) 을 반영하는 중요한 요소가 될 수 있으며, 저하 테스트에 이러한 요구 사항이 반영되도록 보장할 수 있습니다.

그러나 팀이 이 테스트들을 충분히 신뢰하지 못하면, 변화는 더욱 어려워지고 위험 요소를 효과적으로 통제할 수 없습니다. 이러한 편향은 애플리케이션 문제(서비스 중단 포함)에서 교훈을 얻지 못하고, 적절한 대응 방안을 마련하지 못하는 형태로 자주 드러납니다.

2.7 요약

성능 결과를 평가할 때는 데이터를 적절히 처리하고 비과학적이거나 주관적인 사고에 빠지지 않는 것이 중요합니다. 특히 적절하지 않은 상황에서 정규 분포 모델에 의존하는 통계적 오류를 피하는 것이 필요합니다.

이 장에서는 다양한 성능 테스트 유형, 테스트 모범 사례, 성능 분석과 관련된 인간적 문제들을 다루었습니다.

비교 / 트레이드오프

성능 테스트 종류별 비교
테스트목적부하 패턴답하는 질문 형태
지연트랜잭션 시간 측정일정 동시성”얼마나 걸리는가?”
처리량처리 가능량 측정일정 동시성”초당 몇 건?”
스트레스한계점 찾기점진 증가”어디서 무너지는가?”
부하예상 부하 검증예상 부하 고정”버틸 수 있는가? (Y/N)“
내구성장시간 문제 찾기평균 부하 장시간”느리게 새고 있는가?”
용량 계획미래 확장 검증증설 후 부하”확장이 비례하는가?”
성능 저하부분 실패 시 동작정상 부하 + 장애 주입”무너지면 어떻게 되는가?”
정확도 vs 정밀도
항목정확도(accuracy)정밀도(precision)
대응 오차체계적 오차무작위 오차
의미평균이 참값에 가까움측정값들이 서로 모여 있음
개선 방법측정 환경 보정측정 횟수 증가
자바 사례네트워크 지연 보정30회 이상 반복 실행
정규 분포 vs JVM 분포
항목정규 분포JVM 트랜잭션 분포
모양종 모양 대칭왼쪽 치우침 + 롱테일
핵심 통계평균, 표준편차백분위수, HDR 히스토그램
이상치 처리노이즈로 평균화신호로 별도 분석
적합한 도구평균/분산 계산HdrHistogram, percentile sampling
인지 편향과 안티 패턴 매핑
인지 편향대표 안티 패턴발현 형태
확증 편향 (confirmation bias)반짝이는 것에 한눈팔기도입하고 싶은 기술의 장점만 측정
최신성 편향 (recency bias)책임 전가 (Blame Donkey)최근 장애 컴포넌트를 또 의심
환원주의적 사고 편향코드 세부부터 보기큰 그림 없이 함수 단위로 파고듦
행동 편향 (action bias)전쟁의 안개진단 없이 일단 무엇이든 시도
위험 편향 (risk bias)변경 회피NFR을 알면서도 손대지 않음
응답 히스토그램 분해 (모자와 코끼리)
응답 유형분포 특성원인
2xx 성공다봉 분포 (multimodal)읽기/쓰기 등 실행 경로별 차이
4xx 클라이언트 오류좁고 짧은 분포서버가 즉시 거절 가능
5xx 서버 오류긴 처리 시간 영역백엔드 타임아웃·과부하 후 실패

내 생각

  • “테스트 종류부터 정하라”는 원칙이 실무에서 가장 어렵습니다. 보통 “성능 테스트 좀 해줘”라는 요청으로 시작해서, JMeter 스크립트를 던지고 평균 응답 시간 그래프를 그리는 것으로 끝납니다. 그 결과가 사업적 결정에 어떤 답을 주는지 가 빠져 있으면, 일주일을 들여도 의미가 없습니다.

  • 지연과 처리량을 짝지어 명시하는 규칙은 SLO 문서를 쓸 때 반드시 적용해야 합니다. “p99 200ms”만 적은 SLO는 무의미합니다. “초당 5,000 RPS에서 p99 200ms”라야 측정·검증·알람이 가능합니다. 부하 수준 없이 지연만 적은 알람은 한밤중에 트래픽 낮은 시간대를 보고 “정상”이라고 잘못 보고합니다.

  • 카오스 몽키가 클라우드 보편화와 함께 더 이상 넷플릭스만의 사치가 아니게 됐습니다. 쿠버네티스 환경에서 노드 교체·파드 재시작·네트워크 분할은 매일 일어나는 정상 동작이고, 운영자가 의도적으로 일으키지 않아도 일어납니다. 따라서 “실패를 가정한 코드”가 기본값이 되어야 합니다. 헬스 체크·재시도·서킷 브레이커는 안전망이 아니라 기본 기능입니다.

  • 안티 패턴 절에서 “지루함”과 “이력 부풀리기”를 첫 번째 원인으로 꼽은 것이 흥미롭습니다. 기술 선택의 실패를 기술 문제로 보지 않고 인간 문제 로 본 시각입니다. 마이크로서비스 도입, NoSQL 도입, 새 프레임워크 도입의 상당수가 “팀에 그게 필요한가”보다 “내 이력서에 그게 필요한가”로 결정된다는 점은 부정하기 어렵습니다.

  • HdrHistogram과 백분위수 샘플링은 실무 모니터링 도구(Prometheus, Datadog, New Relic) 모두 채택하고 있는 방식입니다. 평균만 보는 대시보드는 위험합니다. p50, p95, p99, p99.9를 같은 차트에 겹쳐 보면 “평균은 좋은데 일부 사용자가 비명을 지르는 상태”가 한눈에 보입니다.

  • 조정된 누락(coordinated omission)은 부하 테스트 도구의 함정으로 잘 알려져 있습니다. JMeter 같은 도구가 응답을 기다리느라 다음 요청을 지연시키면, 실제 시스템에서 일어나야 할 큐잉이 측정에서 빠집니다. wrk2나 gatling이 이를 보정하는 이유입니다. 부하 도구를 선택할 때 반드시 확인해야 할 항목입니다.

  • 거짓 상관관계 사례는 부서 회의에서 자주 인용해야 할 무기입니다. “배포 이후 지연이 늘었다”는 관찰이 곧 “이번 배포 때문이다”가 되는 일이 흔합니다. 같은 시간대에 트래픽 패턴이 바뀌었거나, 의존 서비스의 가비지 컬렉션이 정렬되었거나, 단순히 CPU 스로틀링이 시작되었을 수도 있습니다. 상관관계를 본 순간 공통 요인(C)을 먼저 찾는 습관 이 필요합니다.

  • 모자와 코끼리는 SRE가 매일 마주하는 문제입니다. 평균 응답 시간 그래프가 이상해 보일 때, 거의 항상 답은 “응답 종류별로 쪼개봐라”입니다. 2xx와 5xx, 읽기와 쓰기, 인증 경로와 비인증 경로를 같은 버킷에 넣고 평균을 내는 대시보드는 사실상 거짓말 입니다. 라벨 카디널리티가 무서워서 합쳐버리는 순간 코끼리가 모자로 둔갑합니다.

  • 책임 전가 안티 패턴은 사후 분석(postmortem) 문화로만 막을 수 있습니다. 같은 컴포넌트가 세 번 의심받으면, 네 번째 사건에서는 그 컴포넌트를 일부러 가장 마지막에 조사한다는 규칙을 두는 식의 메타 규칙이 필요합니다. 데이터가 가설을 따라가는 것을 막아야 합니다.

  • 환원주의적 사고 편향에 대한 반박은 분산 시스템 트레이싱 도구의 존재 이유와 정확히 일치합니다. 함수 하나의 지연을 아무리 줄여도 호출 사슬 전체의 꼬리 가 줄지 않으면 사용자 경험은 그대로입니다. OpenTelemetry로 엔드투엔드 스팬을 보고 가장 굵은 막대부터 줄여야 합니다.

  • 위험 편향에 대한 처방으로 책이 제시한 “강한 단위 테스트 + 성능 저하 테스트”는 실무에서는 CI 파이프라인의 성능 회귀 게이트로 구현됩니다. p99 응답 시간이 마지막 메인 빌드 대비 X% 이상 늘면 머지를 막는 규칙 같은 것입니다. 이게 없으면 누구도 위험을 감수하지 않으려 합니다.

더 알아볼 것

  • HdrHistogram 라이브러리로 GC 로그 분석 실습
  • wrk2 또는 gatling으로 coordinated omission 보정 부하 테스트
  • 카오스 엔지니어링 도구(Chaos Mesh, Litmus) 쿠버네티스 환경 적용
  • SLO 문서에 부하 수준과 지연 백분위수를 짝지어 정의하기
  • 부하 테스트 환경에서 네트워크 지연 시뮬레이션(tc, toxiproxy) 설정

관련 개념

출처

  • 자바 최적화 2판, Ch02 성능 테스트 방법론