“어쩔 땐 되고, 어쩔 땐 안 돼요!” 잡기 힘든 유령 버그, ‘경쟁 상태(Race Condition)’

“분명히 방금 버그를 봤는데, 다시 테스트하니까 정상적으로 동작해요.”

“개발자에게 설명하기도 어렵고, 재현도 잘 안 되는 이상한 버그를 경험한 적 있으신가요?”

이런 ‘유령 버그’의 가장 흔한 원인 중 하나가 바로 ‘경쟁 상태(Race Condition)’입니다.

이는 일반적인 기능 테스트로는 거의 발견하기 힘든, 매우 까다로운 유형의 결함입니다.

이번 글에서는 이 까다로운 ‘경쟁 상태’가 무엇인지, 그리고 QA가 어떻게 접근해야 하는지 알아보겠습니다.

Q. ‘경쟁 상태(Race Condition)’, 정확히 무엇인가요?

‘경쟁 상태’는 두 개 이상의 작업(스레드, 프로세스)이 하나의 공유된 자원(예: 데이터, 파일)에 ‘동시에’ 접근하여 변경하려고 할 때, 그 실행 순서에 따라 결과가 비정상적으로 달라지는 현상을 말합니다.

즉, 어떤 작업이 간발의 차이로 먼저 도착하여 자원을 차지하느냐에 따라, 결과가 성공하거나 실패하는 ‘경쟁’ 상황 때문에 발생하는 문제입니다.

이것이 바로 ‘동시성 문제’의 가장 대표적인 유형입니다.

Q. 이해하기 쉬운 ‘경쟁 상태’ 예시가 있을까요?

은행 계좌에 돈을 입금하는 상황을 생각해 보면 쉽게 이해할 수 있습니다.

  • 상황:
    • 내 은행 계좌에 현재 잔액이 10,000원 있습니다.
    • 아빠와 엄마가 ‘거의 동시에’ 내 계좌에 5,000원씩을 입금하려고 합니다.
  • 우리가 기대하는 정상적인 결과:
    • 10,000원 + 5,000원 + 5,000원 = 최종 잔액은 20,000원이 되어야 합니다.
  • ‘경쟁 상태’가 발생했을 때의 문제:
    1. 아빠의 스마트폰 앱이 현재 잔액 ‘10,000원’을 읽어옵니다.
    2. 바로 그 찰나의 순간, 엄마의 스마트폰 앱도 현재 잔액 ‘10,000원’을 읽어옵니다. (아직 아빠의 입금이 서버에 반영되기 전입니다.)
    3. 아빠의 앱은 자신이 읽어온 10,000원에 5,000원을 더해, ‘15,000원’으로 잔액을 업데이트해달라고 서버에 요청합니다.
    4. 엄마의 앱 역시 자신이 읽어온 10,000원에 5,000원을 더해, ‘15,000원’으로 잔액을 업데이트해달라고 서버에 요청합니다.
    5. 최종 결과: 10,000원이 입금되었지만, 최종 잔액은 기대했던 20,000원이 아닌 15,000원이 되는 심각한 데이터 유실 버그가 발생합니다.

Q. QA는 이 잡기 힘든 ‘경쟁 상태’를 어떻게 테스트할 수 있나요?

‘경쟁 상태’는 일반적인 기능 테스트처럼 순서대로 차근차근 테스트해서는 발견하기 매우 어렵습니다.

의도적으로 ‘동시’ 요청을 만들어내는 전략이 필요합니다.

  • 1. 수동으로 동시에 요청하기:
    • 가장 원시적이지만, 간단한 시도를 해볼 수 있는 방법입니다.
    • 두 개의 브라우저 창이나 다른 스마트폰 기기를 준비합니다.
    • 그리고 ‘결제하기’나 ‘예약하기’처럼 데이터 변경이 일어나는 버튼을 거의 동시에 눌러보는 것입니다.
    • 성공 확률은 낮지만, 간단한 시나리오에서는 의외의 결과를 발견하기도 합니다.
  • 2. 개발자 도구 활용하기:
    • 크롬 브라우저의 개발자 도구(F12)를 열어, 네트워크 탭을 확인합니다.
    • 특정 API 요청을 찾은 뒤, 마우스 우클릭 -> ‘Copy as fetch’를 선택합니다.
    • 콘솔 탭에 이 코드를 여러 번 붙여넣고, 거의 동시에 실행하여 여러 요청을 보내는 것을 흉내 낼 수 있습니다.
  • 3. 성능 테스트 도구 활용하기 (가장 효과적):
    • JMeter, k6와 같은 성능 테스트 도구를 사용하는 것이 가장 확실한 방법입니다.
    • 이 도구들은 수백, 수천 개의 가상 사용자를 만들어 ‘완벽하게 동시에’ 특정 요청을 보내도록 정밀하게 설정할 수 있습니다.
    • “100명의 사용자가 동시에 ‘선착순 쿠폰 발급’ 버튼을 누른다” 와 같은 시나리오를 통해, ‘경쟁 상태’를 높은 확률로 재현하고 문제점을 찾아낼 수 있습니다.

결론: 동시성을 의심하는 습관

‘경쟁 상태’는 여러 사용자가 동시에 사용하는 현대적인 웹/앱 환경에서는 항상 존재하는 잠재적 위험입니다.

이를 테스트하는 것은 QA에게 분명 큰 도전이지만, 서비스의 데이터 정합성과 신뢰성을 지키는 매우 중요한 활동입니다.

단순한 기능의 흐름뿐만 아니라, ‘여러 사용자가 동시에 이 기능을 사용하면 어떨까?’ 라고 의심하고, 성능 테스트 도구 등을 활용하여 ‘동시성’을 검증하는 습관은 QA의 기술적 역량을 한 단계 끌어올려 줄 것입니다.

댓글 남기기