brunch

실패를 설계하는 사람들

QA 엔지니어가 바라본 Fail Fast와 Fail Safe

by 제임스

(EO planet의 글을 읽고, 작성된 글입니다.)


버그와 함께 살아가기

"버그 없는 소프트웨어는 없습니다"

QA 엔지니어로 일하면서 가장 먼저 받아들여야 했던 현실이었습니다. 처음엔 모든 버그를 잡아내겠다는 야심찬 목표를 가졌지만, 곧 깨달았습니다. 우리의 역할은 버그를 완전히 없애는 것이 아니라, 버그와 함께 살아가는 방법을 설계하는 것이라는 것을요.

그래서 오늘은 실패를 대하는 두 가지 철학, "Fail Fast""Fail Safe"에 대한 이야기를 해보려 합니다. QA 엔지니어의 시선으로 바라본, 불완전한 세상에서 품질을 지키는 방법에 대해서 입니다.


첫 번째 철학: 빨리 실패하라

Fail Fast는 직역하면 '빨리 실패하라'입니다. 처음 들었을 때는 이상했습니다. 실패를 권장한다니? 하지만 이 철학의 핵심은 '실패'가 아니라 '빨리'에 있었습니다.

자동화 테스트를 작성할 때를 떠올려보겠습니다. 로그인 테스트를 실행하는데 첫 단계인 '페이지 로딩'부터 실패한다면? Fail Fast 철학은 명확합니다. 즉시 멈추고 알리는 것입니다. 로그인 버튼을 찾으려 시도하거나, 아이디를 입력하려 애쓰지 않습니다. 이미 게임은 끝났으니까요.

이런 즉각적인 중단이 왜 중요할까요?


어느 날 새벽, 배포 파이프라인에서 테스트가 실패했습니다. 하지만 시스템은 '관대하게' 설계되어 있어서 몇 번 재시도하고, 타임아웃을 늘리고, 그래도 안 되면 그냥 넘어갔습니다. 결과는 어땠을까요?

아침에 출근해보니 프로덕션 환경에 심각한 버그가 배포되어 있었습니다. 만약 첫 실패 시점에 즉시 멈췄다면, 새벽에 얼럿이 울렸겠지만 적어도 사용자는 버그를 만나지 않았을 겁니다.

Fail Fast는 잔인하지만 정직합니다. "지금 뭔가 잘못됐어요. 더 진행하면 더 큰 문제가 될 거예요." 라고 말하는 것이죠.


두 번째 철학: 안전하게 실패하라

그런데 항상 멈출 수만은 없습니다. 특히 실제 서비스가 돌아가는 프로덕션 환경에서는요.

Fail Safe는 '안전한 실패'를 추구합니다. 실패는 인정하되, 전체 시스템이 무너지지 않도록 하는 것입니다. 비행기가 엔진 하나가 고장나도 착륙할 수 있는 것처럼요.

예전에 만들었던 모니터링 시스템이 떠오릅니다. 외부 API를 호출해서 데이터를 가져오는 부분이 있었는데, 이 API가 종종 응답하지 않았습니다. Fail Fast로 접근했다면 어떻게 됐을까요? 모니터링이 중단되고, 알람이 울리고, 온콜 엔지니어가 새벽에 깨어났을 겁니다.

대신 우리는 Fail Safe를 선택했습니다. API가 응답하지 않으면 캐시된 데이터를 사용했습니다. 1시간 전 데이터라도 없는 것보단 나았으니까요. 물론 "Using cached data (1 hour old)"라는 경고는 띄웠지만, 시스템은 계속 돌아갔습니다.

이게 타협일까요? 어떻게 보면 그렇습니다. 하지만 현실적인 타협입니다. 100% 완벽한 데이터를 위해 서비스를 중단시키는 것보다, 99% 정확한 데이터로 서비스를 유지하는 것이 사용자에게는 더 나은 선택일 수 있습니다.


그래서, 언제 무엇을 선택할까요?

이 질문이 QA 엔지니어의 일상입니다. 매 순간 우리는 "선택"합니다.

즉시 멈출 것인가, 우회할 것인가?


개발 환경에서 단위 테스트를 돌릴 때는 Fail Fast입니다. 하나라도 실패하면 빌드를 막습니다. 왜 일까요? 지금 잡지 않으면 나중에 찾기 힘들어지니까요. 마치 집을 지을 때 기초공사가 잘못되면 즉시 멈추는 것처럼요.

하지만 프로덕션 환경의 헬스체크(Health Check)는 다릅니다. 데이터베이스 연결이 일시적으로 끊겼다고 전체 서비스를 내릴 수는 없습니다. 대신 "degraded" 상태(완전히 정상은 아니지만, 그래도 작동은 하고 있는 상태)로 표시하고, 가능한 기능만 제공합니다. 마치 정전이 되어도 비상등은 켜지는 것처럼요.

금융 거래 시스템을 테스트할 때는 더욱 엄격했습니다. 계산이 1원이라도 맞지 않으면 즉시 중단했습니다. 여기서는 Fail Fast가 절대적입니다. 반면 추천 시스템은 관대했습니다. 개인화 추천이 실패하면 인기 상품을 보여주면 됩니다. 완벽하진 않아도 충분히 쓸만하니까요.


이전 팀 빌딩 후 설계한 Fail-Safe Process


두 철학이 만나는 지점

가장 흥미로운 건 이 두 철학이 만나는 지점입니다. 예전에 설계했던 장애 대응 프로세스가 그랬습니다.

CS팀에서 이슈를 발견하면 자동으로 Jira 티켓이 생성됩니다(Fail Safe - 시스템은 계속 돌아갑니다). 하지만 심각도가 Critical이면 즉시 엔지니어에게 알람이 갑니다(Fail Fast - 빠른 대응). QA팀이 재현을 시도하고, 재현되면 개발팀에 할당됩니다. 각 단계마다 명확한 타임아웃이 있어서, 응답이 없으면 다음 단계로 에스컬레이션됩니다.

이 프로세스의 묘미는 'Emergency' 플로우를 별도로 둔 것입니다. 평소에는 체계적으로(Fail Safe), 긴급시에는 과감하게(Fail Fast). 마치 병원의 응급실과 일반 진료실이 다르게 운영되는 것처럼요.

특히 기억에 남는 건 "Developer checks (will change status by developer)"라는 부분입니다. 완전 자동화를 추구하지 않고, 의도적으로 사람의 판단을 끼워넣었습니다. 왜일까요? 맥락을 이해하는 건 아직 사람이 더 낫기 때문입니다. 자동화는 빠르지만, 사람은 현명합니다. - 경우에 따라 자동화를 사용해도 무관합니다만, 현 상황에 무엇이 적절한가는 판단이 필요합니다.


실패에서 배우기

Postmortem, 즉 사후 분석은 두 철학의 아름다운 결합입니다.

장애가 발생하면 일단 빠르게 복구합니다(Fail Fast). 하지만 끝이 아닙니다. 왜 발생했는지, 어떻게 막을 수 있는지 분석합니다(Fail Safe). "Not Reproduced"로 끝난 이슈도 기록합니다. 재현되지 않는 버그도 버그입니다. 언젠가 다시 나타날 수 있으니까요.

이런 문화가 자리잡으면 신기한 일이 일어납니다. 실패를 두려워하지 않게 됩니다. 실패는 학습의 기회가 되고, 시스템은 점점 강해집니다. 마치 백신을 맞아 면역력이 생기는 것처럼요.


QA 엔지니어로서의 깨달음

처음 QA를 시작했을 때는 모든 버그를 잡아야 한다고 생각했습니다. 하나라도 놓치면 실패라고 여겼습니다. 하지만 이제는 압니다. 우리의 역할은 버그를 없애는 것이 아니라, 버그가 있어도 시스템이 견딜 수 있도록 만드는 것이라는 것을요.

Fail FastFail Safe는 이를 위한 두 개의 도구입니다. 망치와 드라이버처럼, 각자의 용도가 있습니다. 중요한 건 언제 무엇을 써야 하는지 아는 것입니다.

개발 초기엔 Fail Fast로 품질의 기준을 세웁니다. 프로덕션 환경에선 Fail Safe로 서비스의 연속성을 지킵니다. 데이터 정합성엔 엄격하게, 사용자 경험엔 유연하게. 이런 균형 잡기가 QA 엔지니어의 일상입니다.


불완전함을 설계하기

완벽한 소프트웨어는 없습니다. 하지만 불완전함을 예측하고 대비할 수는 있습니다.

Fail Fast는 "잘못되면 빨리 알려주세요"라고 말합니다. Fail Safe는 "잘못되어도 최대한 버텨주세요"라고 말합니다.

모순처럼 들리지만, 사실 이 둘은 같은 목표를 향합니다. 사용자에게 더 나은 경험을 제공하는 것. 때로는 빠른 실패가, 때로는 안전한 실패가 그 답이 됩니다.


QA 엔지니어는 이 두 철학 사이에서 줄타기를 합니다. 품질과 속도, 안정성과 혁신, 자동화와 수동 검증 사이에서 균형을 잡습니다. 그리고 그 균형점에서 소프트웨어의 품질이 만들어집니다.

버그와 함께 살아가는 법을 배우는 것. 실패를 설계하는 것. 이것이 QA 엔지니어가 하는 일입니다.

그리고 오늘도 우리는 테스트를 작성하며 생각합니다. "이건 즉시 실패해야 할까, 아니면 우회해야 할까?"

이 질문에 대한 답은 없습니다. 상황마다, 시스템마다, 회사마다 다릅니다. 하지만 이 질문을 던지는 것 자체가 QA의 시작입니다.

keyword
작가의 이전글"말이 그리는 미래, 웃음이 여는 문"