문제와 판단 기준
오류를 수정하고 배포했습니다. 모니터링 화면도 조용해졌습니다. 그런데 몇 주 뒤 비슷한 문의가 들어옵니다. 이전 이슈에는 스택 트레이스와 수정 PR만 남아 있습니다. 어떤 조건에서 사용자가 실패했는지, 다음 변경에서 무엇을 다시 확인해야 하는지는 찾기 어렵습니다.
운영 오류를 회귀 테스트로 바꾸려면, 오류 기록에서 재현에 필요한 조건을 추려 내고 기대 동작을 정한 뒤, 수정 전에는 그 기대를 위반하고 수정 후에는 만족하는지 확인해야 합니다. 그 테스트가 다음 변경의 검증에 포함되도록 담당자와 실행 조건을 정하고 배포 후 실제 사용자 영향까지 다시 확인해야 연결이 끝납니다.
이 글에서 제안하는 흐름은 다음과 같습니다.
사용자 영향 → 릴리스·환경 확인 → 최소 재현 조건 → 기대 동작 → 수정 → 회귀 검증 → 배포 후 확인
모든 로그를 테스트로 옮길 필요는 없습니다. 반복될 수 있는 실패를 다음 변경에서 알아챌 수 있게 만드는 것이 목표입니다. 아래의 기록 모델과 예시는 이를 위한 설계 제안이며 특정 제품의 자동화 기능이나 실제 고객의 테스트 결과를 설명하지 않습니다.
오류를 발견했다는 사실과 원인을 밝혔다는 사실을 나눈다
Sentry는 유사한 이벤트를 fingerprint에 따라 이슈로 묶습니다. 기본 그룹화에는 스택 트레이스, 예외 유형, 메시지 같은 정보가 사용됩니다. 따라서 이슈는 조사 대상을 정리하는 단위로 유용합니다. 다만 그룹화 결과를 곧바로 확정된 원인이나 테스트케이스 하나로 해석해서는 안 됩니다. 앞부분은 제품의 동작이고 뒷부분은 조사할 때 지켜야 할 판단 경계입니다. 1 2
예를 들어 저장 API의 401 응답과 화면의 예외가 함께 관찰됐다고 합시다. 확인된 사실은 인증 실패 응답과 화면 예외가 있었다는 것입니다. 세션 만료 때문인지, 잘못된 인증 설정 때문인지, 화면이 실패 응답을 성공 응답처럼 처리했는지는 따로 확인해야 합니다. 401만 보고 원인을 ‘세션 만료’로 확정하면 다른 실패를 같은 수정으로 덮을 수 있습니다.
기록에서는 다음 세 상태를 구분하는 편이 좋습니다.
| 상태 | 지금 말할 수 있는 것 | 다음에 필요한 것 |
|---|---|---|
| 관찰됨 | 어느 사용자 행동에서 어떤 결과가 관찰됐다 | 대상 버전·환경, 관련 증거, 영향 범위 |
| 재현·설명됨 | 통제한 조건에서 같은 실패가 발생하고 원인 가설을 검토했다 | 대안 가설, 제거해도 되는 조건, 기대 동작 |
| 회귀 자산으로 검증됨 | 같은 기대 조건을 수정 전후에 적용해 차이를 확인했다 | 반복 실행 조건, 담당자, 배포 후 확인 |
원인 규명에 앞서 사용자 관점의 실패 조건을 테스트로 표현할 수도 있습니다. 이때는 ‘증상은 재현됨, 원인은 조사 중’이라고 남기면 됩니다. 테스트가 존재한다는 사실이 원인 분석까지 끝났다는 뜻은 아닙니다.
실행 결과의 신뢰를 지키는 유지관리 — 변경에 강한 식별자로 실행하고 실패 원인을 분류해 보완한 뒤 다시 검증합니다.
로그를 복사하기 전에 어느 버전의 어떤 실패인지 고정한다
오류 메시지보다 먼저 적을 것은 사용자가 끝내지 못한 일입니다. ‘TypeError 발생’보다는 ‘작성한 문서를 저장하려 했지만 완료되지 않았고 입력 내용도 사라졌다’가 다음 검증으로 옮기기 쉽습니다. 입력 소실, 잘못된 성공 안내, 중복 처리처럼 서로 다른 영향이 있다면 각각 구분합니다.
그다음 발생 시각과 시간대, 서비스·릴리스 식별자, 운영 또는 검증 환경, 관련 설정과 기능 플래그를 묶습니다. Sentry에서 release는 환경에 배포된 코드의 버전이며 JavaScript SDK에는 release와 environment 설정이 있습니다. 팀의 배포 기록과 실제 이벤트의 값이 맞는지 확인해야 하며 이름만 보고 운영 환경이라고 추정하지 않는 편이 안전합니다. 3 4
프런트엔드와 API가 따로 배포된다면 버전도 따로 남깁니다. 재현에 영향을 주는 브라우저·런타임·설정은 실제 값을 기록하되, 모든 정보를 무조건 모으지는 않습니다. 예를 들어 특정 역할에서만 실패했다면 그 역할과 권한 조건이 중요하고 모든 브라우저에서 같은 응답 처리 오류가 난다면 세부 기기 정보는 최소 재현에서 제외할 후보입니다.
OpenTelemetry의 컨텍스트 전파는 Trace ID와 Span ID 등을 이용해 서비스 사이 요청과 관측 신호를 연결하는 기반입니다. 로그와 트레이스의 연결도 계측·전파가 맞게 구성돼 있어야 활용이 가능합니다. 이 연결은 조사 경로를 좁히는 증거이지, 특정 코드가 버그의 원인이라는 결론을 대신하지는 않습니다. 5
운영 증거는 접근이 제한된 원래 보관 위치를 참조하고 테스트 저장소에는 그 증거에서 도출한 조건을 남기는 방식을 권합니다. 증거가 만료되기 전에 공개 가능한 기술 사실을 요약하고 누가 언제 어떤 자료를 확인했는지도 기록합니다. 인증정보를 포함한 원문을 보존하자는 뜻은 아닙니다.
고객 데이터를 가져오지 않고 실패 조건을 보존한다
테스트용 fixture는 실행 전에 준비하는 데이터와 상태입니다. 운영에서 가져와야 할 것은 고객의 이름이나 실제 문서가 아니라 실패를 일으킨 데이터의 구조와 조건입니다.
원문 대신 합성 데이터를 만들되, 실패에 영향을 준 속성은 보존합니다. 빈 값, 문자열 길이, 문자 인코딩, 필드의 누락과 null의 차이, 레코드 사이의 관계, 역할과 소유권, 만료 전후의 상태가 그런 후보입니다. 숫자를 아무 값으로 바꾸거나 관계를 끊어 버리면 개인정보는 제거했어도 재현 조건은 사라질 수 있습니다.
Sentry는 SDK에서 전송 전에 민감 데이터를 제거하는 방법과 서버 측에서 저장되지 않도록 처리하는 방법을 구분해 안내합니다. 이미 수집된 데이터가 있다는 이유로 테스트 저장소에 다시 복사할 수 있는 것은 아닙니다. 오류 이벤트뿐 아니라 사용하는 트레이스·로그·첨부물의 수집 경로도 각각 점검해야 합니다. 하나의 제거 설정이 모든 경로를 보호한다고 가정하지 않습니다. 6
이메일 일부를 가리거나 식별자를 다른 값으로 바꿨다는 사실만으로 재식별 가능성이 없어졌다고 단정하지도 않습니다. 공개·공유용 예제는 처음부터 합성 데이터로 만들고, 테스트 실행 계정과 비밀정보는 저장소 밖에서 관리합니다. OpenTelemetry의 baggage에도 인증정보나 개인정보를 넣지 않도록 주의해야 합니다. 서비스 경계를 넘어 전달될 수 있기 때문입니다. 5
Playwright Test의 기본 page와 context fixture는 테스트별로 격리됩니다. 그러나 이 브라우저 격리가 애플리케이션의 공유 데이터베이스까지 초기화해 주는 것은 아닙니다. 테스트별 데이터 영역을 만들고 실행 후 정리하는 절차는 따로 설계해야 합니다. worker 범위로 공유하는 fixture도 테스트별 자원과 구분해야 합니다. 7 8
기다리는 시간을 늘리지 말고 실패를 만드는 조건을 통제한다
재현에 성공했다면 우연을 덜어 냅니다. ‘가끔 느린 네트워크에서 실패한다’는 설명을 ‘요청에 이 응답이 돌아오고, 처리 순서가 이렇게 될 때 실패한다’로 바꾸는 작업입니다. 다음 표는 그때 적용할 설계 원칙을 보여 줍니다.
| 달라질 수 있는 조건 | 테스트에서 통제할 것 | 그 통과만으로 증명되지 않는 것 |
|---|---|---|
| 네트워크 | 응답 상태·본문, 연결 실패, 필요한 응답 순서 | 실제 외부 서버의 정상 동작이나 가용성 |
| 시간 | 기준 시각·시간대, 만료 경계, 타이머 진행 | 브라우저 밖 API·DB의 시계가 함께 바뀌었다는 사실 |
| 외부 의존성 | 문서화된 응답 계약에 맞춘 mock, 허가된 검증 환경 | 사업자의 실제 장애가 해결됐다는 사실 |
| 권한·데이터 | 합성 계정의 역할·소유권, 사전 데이터, 기능 플래그 | 다른 역할·다른 데이터 상태에서도 안전하다는 사실 |
| 비동기 처리 | 요청 발생, 응답 처리, 완료 상태 같은 명시적 조건 | 단순히 일정 시간이 지났다는 이유의 완료 판정 |
Playwright의 네트워크 mock으로 HTTP 응답을 통제합니다. 그러나 서버가 보낼 수 없는 응답을 편의상 만들면 테스트는 실제 연동과 멀어집니다. 오류 상태와 응답 구조를 현재 계약에 맞추고, mock으로 확인하는 것은 그 조건에 대한 우리 코드의 반응임을 분명히 합니다. 9
시간도 구분해야 합니다. page.clock.setFixedTime()은 Date.now()와 new Date()가 보는 시각을 고정하는 용도입니다. 타이머를 직접 진행시켜야 할 때는 install() 등을 사용하는 Clock 기능을 검토합니다. 이는 브라우저 측 제어이므로 서버가 판정하는 세션 만료는 서버의 테스트용 시계나 세션 상태를 별도로 준비해야 합니다. 10
화면 검증에서는 ‘3초 대기 후 확인’보다 해당 상태가 나타나는지를 기다리는 assertion을 사용합니다. Playwright의 비동기 assertion은 조건을 다시 확인하다가 만족하거나 제한 시간에 도달하면 종료됩니다. 다만 오류 안내가 나타났다는 이유만으로 모든 처리가 끝났다고 가정하지 말고, 해당 화면의 완료 상태까지 정의합니다. 그다음 입력 보존과 성공 안내의 부재를 확인합니다. 기능 테스트의 대기 제한은 서비스 성능 목표와도 별개입니다. 11
설계 예시: 세션 만료 후 저장하면 작성 내용이 사라진다
다음은 가상의 문서 작성 서비스입니다. 실제 IXC 고객 사례가 아니며, 제시한 테스트를 실행한 결과도 아닙니다. 버전과 식별자, 데이터, 담당 역할은 모두 설명용입니다.
가정한 상황은 이렇습니다. 사용자가 로그인한 상태에서 문서를 작성하다가 세션이 만료됐습니다. 저장 요청은 401로 거절됐는데, 화면은 입력을 비우고 응답에서 문서 식별자를 읽으려다 예외를 냅니다. 여기서 보호할 요구는 ‘어떤 경우든 저장 성공’이 아닙니다. 인증에 실패한 요청은 저장하지 않고, 화면은 실패를 안내하면서 현재 입력을 승인된 범위에서 보존하는 것입니다.
아래 기록은 원문 로그를 옮기는 양식이 아니라, 운영 이슈에서 테스트로 넘길 내용을 채운 예시입니다.
| 기록 항목 | 채워 넣은 내용 — 설계 예시 |
|---|---|
| 연결 식별자 | 운영 이슈 OBS-DEMO-044 → 요구 REQ-DRAFT-401 → 수정 FIX-DEMO-044 → 테스트 REG-044-UI |
| 사용자 영향 | 문서 저장 실패 후 현재 화면의 입력 내용 소실. 다시 작성해야 하는 영향 |
| 대상 릴리스·환경 | 프런트엔드 web-demo-r1, API api-demo-r1, production이라는 가정. 재현은 격리된 검증 환경에서 수행 |
| 오류 근거 | 같은 저장 시도에 대한 401 응답, 화면 예외, 입력 소실 관찰 기록을 연결한다는 가정. 실제 이벤트·trace 식별자는 예시에 넣지 않음 |
| 가설과 미확정 사항 | 화면이 실패 응답을 성공 응답처럼 처리하며 입력을 먼저 초기화했을 가능성. 운영의 401 발생 원인이 세션 만료였는지는 별도 조사 |
| 최소 재현 조건 | 문서 입력 화면이 열린 상태, 합성 입력 존재, 저장 요청에 계약상 401 응답 반환. UI 재현에는 실제 고객 계정이나 만료 토큰 불필요 |
| 합성 fixture | 제목 검증용 문서, 본문 합성 입력 A, 검증 전용 역할 editor. 고객 원문·쿠키·인증 헤더는 포함하지 않음 |
| 기대 결과 | 재로그인 필요 안내, 요청 처리 종료 상태, 현재 화면의 입력 유지, 저장 성공 표시 없음. API는 거절된 요청으로 문서를 생성하지 않아야 함 |
| 수정 후보 | 성공 여부를 확인한 뒤에만 성공 처리를 수행하고 401 경로에서는 입력 초기화 없이 복구 가능한 안내를 표시. 실제 수정은 원인 확인 후 결정 |
| 회귀 자산 | UI 응답 처리 REG-044-UI; 실제 인증·저장 경계 REG-044-API; 인증이 유효한 정상 저장 REG-044-OK |
| 수정 전후 비교 | UI 결함 재현은 이전 빌드에서 입력 보존 기대를 위반하고 수정 빌드에서 만족해야 함. 아직 실행하지 않았으므로 결과는 모두 NOT_RUN |
| 담당·차단 기준 | 프런트엔드 담당이 UI 수정·테스트, API 담당이 인증·저장 경계 검증, QA 리드가 기대 결과 검토. 검증이 안정화된 뒤 관련 변경의 필수 검사로 편입 |
| 배포 후 확인 | 수정 릴리스가 실제 적용된 환경에서 저장 실패·입력 소실 신호를 구분해 확인. 관측 수집 상태와 해당 경로의 실제 사용 여부도 함께 확인 |
| 유지·폐기 조건 | 인증 처리·응답 계약·입력 보존 정책이 바뀌면 재검토. 기능 폐기 또는 동등한 검증으로 대체할 때 근거와 승인자를 남기고 제거 |
브라우저 테스트와 서버 테스트를 따로 만든다
REG-044-UI에서는 합성 입력을 채운 뒤, 저장 요청에 대해 계약에 맞는 401 응답을 반환하도록 네트워크를 통제합니다. 요청이 실제로 발생했는지, 실패 처리의 완료 상태에 도달했는지 확인한 다음 재로그인 안내와 입력 보존을 검증합니다. 저장 완료 표시가 없는지도 확인합니다. 오류만 더 이상 발생하지 않는다는 assertion으로 끝내지 않습니다. 사용자에게 보이는 동작을 검증하라는 원칙은 Playwright의 공식 권고와도 맞닿아 있습니다. 8 9 11
이 테스트는 서버의 인증을 증명하지 못합니다. 401을 테스트가 직접 만들어 줬기 때문입니다. REG-044-API에서는 격리된 환경에 테스트용 세션을 만들고, 서버의 정책에 따라 만료 상태를 구성한 뒤 실제 검증 대상 API를 호출합니다. 거절 응답과 저장되지 않은 데이터 상태를 함께 확인합니다. 이 계층에서는 검증하려는 인증·저장 로직 자체를 mock으로 대체하지 않습니다.
정상 저장을 깨뜨리지 않았는지 확인하는 REG-044-OK도 남깁니다. 모든 요청을 실패 처리하도록 바꾸면 401 테스트만 통과시키는 잘못된 수정이 가능하기 때문입니다. 이 정상 경로와 API 검증은 수정 전부터 통과할 수 있습니다. 수정 전 실패를 요구하는 대상은 실제 결함을 드러내는 재현 테스트이지, 관련된 모든 테스트가 아닙니다.
입력 보존의 범위도 합의해야 합니다. 이 예시에서는 현재 화면의 메모리 상태를 유지한다고 가정합니다. 로그아웃 후 영구 저장하거나 브라우저 저장소에 민감한 내용을 자동 보관하자는 제안이 아닙니다. 다른 계정으로 전환되는 경우의 입력 처리도 별도의 보안·제품 정책으로 정해야 합니다.
제품 맥락을 지키며 실행 규모를 조정 — 고정 리드가 기준과 이력을 이어가고 구간별 스쿼드의 실행 결과를 출시 판단의 근거로 모읍니다.
수정 전 실패와 수정 후 성공을 같은 조건에서 확인한다
이슈를 닫기 전에 ‘새 테스트를 추가했다’보다 강한 증거가 필요합니다. 같은 기대 결과와 fixture를 사용해 결함이 있던 빌드와 수정 빌드를 비교합니다. 요구사항을 바꿔 통과시킨 것인지, 결함을 고쳐 통과한 것인지 구분할 수 있어야 합니다.
먼저 이전 빌드의 실패가 정말 그 결함 때문인지 확인합니다. 로그인 준비 실패, 잘못된 선택자, 실행 환경 오류로 실패한 것은 재현 증거가 아닙니다. 그다음 수정 빌드에서 기대 결과를 만족하는지 확인하고 정상 경로와 영향이 있는 인접 조건을 함께 검증합니다. 실행 기록에는 앱 빌드, 테스트 버전, fixture 버전, 설정, 실행 식별자, 실패 이유를 연결합니다.
이전 빌드를 실행할 수 없거나 당시 상태를 복원할 수 없다면 ‘수정 전 실패 확인’ 칸을 통과로 채우지 않습니다. ‘수정 후 확인만 수행’, 복원하지 못한 조건, 남은 불확실성을 기록하고 다른 증거로 보완합니다. 필요하다면 안전한 로컬 실험에서 결함의 핵심 조건을 되살릴 수 있지만 이를 실제 운영의 과거 상태를 완전히 재현한 결과와 혼동해서는 안 됩니다.
테스트 계층은 실패의 핵심을 어디에서 가장 직접적으로 확인할 수 있는지로 고릅니다. 순수한 조건 분기라면 단위 테스트, 인증과 저장처럼 경계가 중요하면 API·통합 테스트, 화면 입력 소실처럼 사용자 동작이 핵심이면 브라우저 테스트가 필요합니다. 같은 조건을 모든 계층에 복제하기보다 각 테스트가 증명하는 요구를 나눕니다. Google SRE도 테스트 종류별 역할과 시스템 수준 검증을 구분해 다룹니다. 12
생성한 테스트를 이번 변경의 어느 범위에 포함할지는 다음 판단입니다. 그 기록에는 기존 변경별 회귀 테스트 범위 결정 워크북을 활용하십시오. 이 글은 운영 실패를 테스트로 만드는 과정을, 워크북은 실행할 테스트와 제외할 근거를 정리하는 과정을 다룹니다.
테스트를 배포 기준에 넣되, 실패를 숨기지 않는다
영향이 크고, 통제한 조건에서 안정적으로 결함을 드러내며, 실패 시 조치할 담당자가 정해진 테스트라면 관련 변경의 배포 차단 기준에 포함합니다. 이때 검사 이름과 실행 조건을 정하고 해당 경로의 변경인데 검사가 실행되지 않은 경우를 성공으로 처리하지 않도록 합니다. 긴급 우회가 필요하다면 승인자·사유·보완 검증·해제 시점을 기록합니다.
반대로 원인이 불분명한 간헐 실패를 필수 검사로 급히 넣으면, 배포를 막는 데 지친 팀이 검사를 꺼 버릴 수 있습니다. 실행을 별도 구간으로 격리하더라도 담당자와 재검토 기한을 남기고, 그동안 어떤 위험을 다른 방법으로 확인할지 정합니다. ‘재실행하면 통과했다’는 사실과 ‘테스트가 안정적이다’는 판정은 다릅니다.
기존 자동화 실패 분석·격리·복귀 운영 양식은 이처럼 만들어진 테스트가 불안정해졌을 때 검토할 후속 자료입니다. 운영 이슈에서 테스트를 새로 정의하는 일과, 테스트 자체의 실패를 분석하는 일을 같은 기록으로 뒤섞지 않는 편이 좋습니다.
테스트를 영구히 쌓기만 하지도 않습니다. 보호하던 요구가 사라졌거나 동등한 검증으로 대체됐다면 제거해도 됩니다. 다만 수정이 오래됐다는 이유만으로 지우지 말고, 어떤 실패 조건이 어디에서 계속 검증되는지 확인합니다. 서비스 담당자는 요구 변경을, 테스트 담당자는 실행 품질을, 배포 담당자는 차단과 예외 정책을 맡도록 책임을 나눌 수 있습니다. 작은 팀에서는 한 사람이 겸해도 결정은 구분해 남깁니다.
회귀 테스트가 맞는 답이 아닌 경우도 남겨 둔다
재현 정보가 부족하다면 이름뿐인 테스트를 만드는 대신 다음 발생에서 필요한 증거를 수집하는 작업을 잡습니다. 수집 항목과 개인정보 경계, 담당자, 재검토 시점이 그 작업의 완료 조건입니다. 관측하지 못했다는 사실을 ‘재발하지 않는다’는 결론으로 바꾸지는 않습니다.
외부 사업자 장애라면 우리 시스템의 타임아웃 처리, 재시도 정책, 실패 안내 등을 통제된 응답으로 검증합니다. 그러나 그것만으로 사업자의 가용성을 보장할 수는 없습니다. 계약에 맞는 연동 확인, 의존성 관측, 우회·복구 절차가 별도로 남습니다.
메모리 누수, 자원 포화, 배포 설정, 장시간 누적 상태처럼 비기능·환경 조건이 핵심이라면 기능 회귀 테스트만으로 충분하지 않을 수 있습니다. 소유하거나 허가받은 환경에서 진행하는 성능·지속 부하 검증, 설정 검사, 용량·구조 개선과 런북을 조합해야 합니다. Google SRE의 사후 분석 원칙 역시 문서 작성 자체보다 재발 가능성이나 영향을 낮추는 후속 조치를 강조합니다. 12 13
배포 뒤에도 이슈의 상태값 하나로 검증을 끝내지 않습니다. 수정 릴리스가 해당 환경과 사용자에게 적용됐는지, 문제가 있던 경로가 실제로 사용됐는지, 오류 수집이나 샘플링 조건이 바뀌지 않았는지 확인합니다. 사용량이 없거나 수집이 끊긴 구간의 오류 0건은 개선 증거가 되기 어렵습니다. 관찰 기간과 재개방 조건은 사용 빈도와 실패 영향에 맞춰 정하고 근거가 부족하면 확인 대기 상태를 유지합니다.
이 흐름을 시작하는 데 새 플랫폼이 반드시 필요한 것은 아닙니다. 기존 이슈 관리 도구, 테스트 저장소와 배포 기록으로도 연결할 수 있습니다. 회귀 자산을 설계하고 CI에 연결할 여력이 부족하다면 IXC 테스트 자동화 서비스의 지원 범위를 확인하십시오.
운영 이슈가 회귀 자산이 됐다고 말할 수 있는 시점은, 다음 담당자가 기록을 보고 같은 실패 조건을 만들고 같은 기대 결과를 확인할 수 있을 때입니다. 오류를 닫는 데서 멈추지 말고, 무엇을 다시 확인할지와 누가 그 확인을 이어 갈지를 남겨야 합니다.



