본문으로 건너뛰기
고급약 45분

테스트 환경·데이터 운영 — 환경 버전 관리, 초기화, 데이터 수명

테스트 환경 전체를 운영 대상으로 다룬다. 구성·스키마·시드 데이터·의존 서비스 버전을 하나의 환경 좌표로 묶는 방법, 초기화 전략(매 실행·주기·더티)의 비용 비교, 테스트 데이터의 수명과 소유자·정리 책임, 고아 데이터 탐지, 그리고 실행 전에 환경이 멀쩡한지 확인하는 프리플라이트 체크를 설계한다.

  • #TestOps
  • #테스트환경
  • #테스트데이터
  • #환경버전
  • #프리플라이트
  • #데이터수명

개념

이 레슨의 경계 — 테스트 하나가 아니라 환경 전체

m13 레슨 10은 테스트 하나가 필요한 데이터를 스스로 만들고 치우는 방법을 다뤘다. 픽스처가 API 로 전제 데이터를 생성하고, 식별 접두사로 멱등성을 보장하고, 끝나면 정리한다. 그 레슨은 테스트 한 건의 자급자족 을 책임진다.

이 레슨은 그 테스트들이 올라타는 바닥을 책임진다. 픽스처가 완벽해도 바닥이 흔들리면 전부 무너진다 — 스키마가 어제와 다르거나, 의존 서비스가 내려가 있거나, 공용 코드 테이블의 시드가 바뀌었거나, 앞 실행이 남긴 쓰레기가 쌓여 있거나. 픽스처는 자기가 만든 데이터만 알고, 환경 전체의 상태는 아무도 모르는 영역 이 되기 쉽다. 그 영역이 TestOps 의 일이다.

구분하는 기준은 간단하다. 그 데이터를 테스트 하나가 만들고 지울 수 있으면 픽스처의 일(m13), 여러 테스트가 공유하거나 테스트 밖에서 결정되면 환경 운영의 일(m59)이다. 사용자 계정 하나는 픽스처, 통화 코드 테이블은 환경. 주문 한 건은 픽스처, 결제 연동의 모의 서버 버전은 환경.

환경을 버전으로 관리한다는 것

"스테이징에서 테스트했다"는 문장은 정보를 거의 담고 있지 않다. 스테이징은 어제의 스테이징과 다르다. 환경을 버전으로 관리한다는 것은 그 다름을 기록 가능한 좌표로 바꾸는 일 이다. 좌표에는 최소한 네 축이 들어간다.

축무엇을 가리키는가기록하지 않으면 생기는 일
애플리케이션 버전배포된 제품의 커밋·이미지 태그어느 코드가 테스트됐는지 모른다
구성(configuration)기능 플래그, 환경 변수, 외부 연동 모드플래그 하나로 결과가 갈리는 이유를 못 찾는다
스키마 버전적용된 마이그레이션의 마지막 번호스키마 불일치 실패가 제품 결함으로 오분류된다
시드 데이터 세트공용 기준 데이터의 버전기준 데이터가 바뀐 날 전부 실패하고 원인을 못 짚는다

여기에 의존 서비스(모의 서버·결제 샌드박스·검색 엔진)의 버전을 더하면 사실상 완성된다. 좌표를 파일 하나로 두고, 실행 결과에 그 파일의 해시를 같이 남긴다.

# env/staging.coordinate.yml — 예시 형태
env: staging
app:
  image: registry.internal/shop-api:2026.10.02-7f3a1c9
  commit: 7f3a1c9
schema:
  migration_head: "20261001_add_order_memo"
config:
  feature_flags: flags/staging-2026-10-02.json
  payment_mode: sandbox
seed:
  dataset: seed/base-v14
  applied_at: "2026-10-02T21:10:00+09:00"
dependencies:
  payment_mock: payment-mock:1.8.0
  search: opensearch:2.x        # 정확한 패치 버전은 배포 기록 참조

이 파일의 값이 바뀌면 환경이 바뀐 것이다. 바뀐 사실을 사람이 기억하는 대신 좌표가 증언하게 만드는 것 이 핵심이고, 레슨 6에서 이 좌표를 테스트 결과와 묶는다.

환경 좌표가 있으면 평소에 못 하던 질문에 답할 수 있다.

  • "어제는 통과했는데 오늘 실패" → 두 실행의 좌표를 비교한다. 다른 축이 원인 후보다.
  • "내 로컬에서는 됩니다" → 로컬 좌표와 CI 좌표를 나란히 놓는다. 대화가 3분에 끝난다.
  • "이 버전 다시 테스트해 주세요" → 같은 좌표를 재현할 수 있는지가 답을 결정한다.

환경 대장과 점유 — 환경은 공유 자원이다

테스트 환경이 하나뿐인 조직에서는 환경 자체가 경쟁 자원이 된다. 개발자가 수동으로 확인하는 동안 자동화가 돌면 둘 다 실패하고, 둘 다 상대를 의심한다. 그래서 환경에도 재고 장부가 필요하다.

환경 대장 (최소 열)
이름 | 용도 | 현재 좌표 | 점유자 | 점유 만료 | 마지막 초기화 | 상태
----
staging-1 | 자동화 야간    | base-v14 | CI(야간)   | 06:00 | 02:10 | 가용
staging-2 | 수동 탐색      | base-v14 | 김OO      | 18:00 | 09:00 | 점유
perf-1    | 성능           | base-v13 | -         | -     | 월요일 | 점검

점유를 기계적으로 강제할 수 없는 조직이라도, 장부만 있으면 충돌 대화가 짧아진다. 자동화가 환경을 잡을 때 점유 표시를 남기고 끝나면 해제하게 만드는 것이 첫 단계다.

초기화 전략 — 매번·주기·더티

환경을 언제 깨끗하게 되돌릴지는 비용과 간섭 위험의 교환이다. 세 가지 기본형이 있고, 실무에서는 데이터 영역별로 다르게 섞어 쓴다.

전략방식유리한 점비용과 위험
매 실행 초기화실행 전 DB·스토리지를 알려진 상태로 되돌린다간섭이 0 에 가깝다. 재현성이 최고준비 시간이 고정비로 붙는다. 병렬 실행이 서로의 초기화에 걸린다
주기 초기화야간·주간 등 정해진 시점에만 되돌린다준비 비용이 분산된다. 운영과 비슷한 데이터 밀도를 유지주기 사이에 쓰레기가 쌓인다. 초기화 직전 실행이 가장 불안정하다
더티(초기화 없음)되돌리지 않고 계속 쓴다. 테스트가 자기 데이터만 책임진다준비 비용이 없다. 병렬에 가장 유리데이터가 무한히 늘어난다. 간섭 사고가 잠복한다. 고아 데이터 정리가 필수

선택의 기준은 "얼마나 깨끗해야 하는가"가 아니라 "간섭이 생겼을 때 얼마나 빨리 알아챌 수 있는가" 다. 간섭을 탐지할 수 없는 조직에서 더티 전략을 쓰면 원인 불명 플래키가 쌓인다. 반대로 탐지와 정리가 되는 조직에서는 더티가 가장 경제적이다.

실무에서 가장 잘 작동하는 혼합형은 이렇다.

데이터 영역별 초기화 정책 (혼합형 예시)
 
공용 기준 데이터(코드·카테고리·권한)   → 주기 초기화 + 좌표로 버전 관리
트랜잭션 데이터(주문·결제·로그)         → 더티 + 픽스처가 자기 것만 생성·정리
계정                                     → 더티 + 실행별 접두사로 격리, 7일 후 일괄 삭제
파일 스토리지                            → 더티 + 경로 접두사로 격리, 주간 정리 작업
캐시·검색 인덱스                        → 매 실행 초기화(비용이 작고 간섭이 치명적)

캐시를 매 실행 비우는 이유가 중요하다. 캐시는 앞 실행의 결과를 다음 실행에 보이게 만드는 가장 흔한 경로 이고, 그로 인한 실패는 테스트 코드 어디를 봐도 원인이 없다. 비용이 거의 없으므로 의심할 가치가 없게 만드는 것이 낫다.

테스트 데이터의 수명 — 세 가지를 반드시 정한다

더티 전략을 쓰든 안 쓰든, 생성된 데이터에는 세 가지가 붙어 있어야 한다.

  1. 소유자 — 어느 실행이 만들었는가. 실행 식별자를 데이터에 심는다(이름 접두사, 메모 필드, 전용 태그 컬럼 중 가능한 것).
  2. 수명 — 언제까지 살아 있어도 되는가. 테스트 종료 시점·당일·7일 같은 등급으로 나눈다. 실패 조사용으로 남겨야 하는 데이터가 있으므로 "즉시 삭제"가 늘 정답은 아니다.
  3. 정리 책임 — 누가 지우는가. 테스트 자신(픽스처 해제), 실행 종료 훅, 또는 주기적 정리 작업 중 하나로 명시한다.

세 가지가 정해지면 고아 데이터 탐지 가 가능해진다. 고아는 소유자 식별자가 이미 끝난 실행을 가리키면서 수명이 지난 데이터다. 탐지 쿼리는 보통 한 줄이면 된다.

-- 예시: 실행 접두사가 있고 생성 7일이 지난 테스트 계정
SELECT run_id, count(*) AS rows, min(created_at) AS oldest
FROM accounts
WHERE login LIKE 'qa-%'
  AND created_at < now() - interval '7 days'
GROUP BY run_id
ORDER BY oldest;

이 쿼리의 결과 추세를 주간으로 보면 정리 체계가 작동하는지 바로 보인다. 고아 건수가 우상향하면 어딘가의 정리 책임이 비어 있다는 뜻이다. 픽스처 해제가 실패하는 경로 (테스트 타임아웃, 러너 강제 종료)가 보통 원인이고, 그래서 픽스처 해제만으로는 부족하고 주기 정리가 항상 함께 있어야 한다.

데이터 운영에서 반드시 짚어야 할 제약이 하나 더 있다. 운영 데이터를 복제해 테스트에 쓸 때는 개인정보가 함께 넘어온다. 가명·마스킹 처리 없이 복제하는 관행은 법적 위험이고, 처리 방식·보존 기간·접근 권한은 조직의 개인정보 정책에 따라 결정된다. 관련 규정의 적용 범위는 법무·보안 담당과 확인하고, 기술적으로는 "복제 후 마스킹"보다 "마스킹된 생성 데이터"가 안전하다.

프리플라이트 — 실행 전 60초가 아침 두 시간을 산다

환경 문제로 생긴 대량 실패는 TestOps 의 비용 구조에서 가장 큰 항목이다. 300개가 전부 실패한 아침에 당번은 원인을 찾는 데 두 시간을 쓰고, 결과는 "모의 결제 서버가 내려가 있었음"으로 끝난다. 그 두 시간은 실행 전 60초로 막을 수 있다.

프리플라이트는 테스트가 아니라 게이트다. 실패하면 테스트를 돌리지 않고 멈추고, 결과를 "환경 미준비"라는 별도 상태로 기록한다. 테스트 실패로 기록하지 않는 것이 중요하다 — 통과율 지표를 오염시키면 추세가 거짓말을 한다.

프리플라이트 체크 목록 (실행 직전, 목표 60초 이내)
 
[좌표]
- 환경 좌표 파일을 읽어 결과 메타에 기록했다
- 애플리케이션이 보고하는 버전이 좌표의 값과 일치한다
- 스키마 마이그레이션 head 가 좌표의 값과 일치한다
 
[가용성]
- 애플리케이션 헬스 엔드포인트가 정상을 반환한다
- 의존 서비스(모의 결제·검색·메시지 큐)가 각각 정상을 반환한다
- 테스트 계정 발급 API 로 계정 하나를 만들고 지우는 왕복이 성공한다
 
[상태]
- 공용 기준 데이터의 행 수가 기대 범위 안이다 (예: 통화 코드 ≥ 20)
- 디스크·DB 커넥션 여유가 임계 이상이다
- 직전 실행의 정리 작업이 성공으로 끝났다
 
[판정]
- 좌표 불일치 → 중단, '환경 미준비' 로 기록, 당번 알림
- 의존 서비스 1개 장애 → 해당 서비스 의존 테스트만 건너뛰기로 표시
- 전부 정상 → 실행 시작

세 번째 항목의 "계정 왕복"이 실무에서 가장 값을 한다. 헬스 엔드포인트는 서버가 떠 있다는 것만 말하지만, 계정 하나를 만들고 지우는 왕복은 DB 쓰기·인증·정리 경로를 한 번에 확인한다. 가장 짧은 실제 트랜잭션을 프리플라이트에 넣는 것이 원칙이다.

의존 서비스 하나가 죽었을 때 전체를 중단하지 않고 해당 의존 테스트만 건너뛰기로 표시 하는 것도 운영 판단이다. 전체 중단은 안전하지만 정보를 0 으로 만든다. 부분 실행은 정보를 남기되, 건너뛴 범위를 결과에 명시해야 "통과했다"는 오해를 막는다.

실무에서 왜 필요한가

자동화가 신뢰를 잃는 가장 빠른 경로는 환경 문제로 생긴 대량 실패다. 테스트 결함은 한두 건이라 개별적으로 처리되지만, 환경 문제는 수백 건을 한꺼번에 빨갛게 만든다. 그런 아침을 세 번 겪으면 사람들은 빨간 배지를 "또 환경일 것"으로 읽기 시작하고, 그 순간부터 진짜 결함도 같이 묻힌다. 환경 운영은 자동화의 신뢰도를 지키는 일 이고, 신뢰도가 떨어진 자동화는 아무리 커버리지가 넓어도 결정에 쓰이지 않는다.

좌표 기록은 조사 시간에 직접 작용한다. 좌표가 없으면 "어제와 오늘이 왜 다른가"가 추측 게임이 되고, 조사는 몇 시간에서 며칠로 늘어난다. 좌표가 있으면 두 실행의 차이를 기계적으로 비교해 후보를 몇 개로 좁힐 수 있다. 이 차이는 테스트 개수와 무관하게 일정하게 돌아오는 수익이다.

데이터 수명 정책은 조용히 쌓이는 비용을 막는다. 정리 없는 테스트 데이터는 몇 달 뒤 DB 성능으로, 스토리지 비용으로, 그리고 "테스트 계정이 1만 개여서 검색 결과가 이상하다" 같은 엉뚱한 실패로 돌아온다. 처음부터 소유자·수명·정리 책임을 붙이는 비용은 작고, 나중에 소급해 정리하는 비용은 크다.

실습 과제

과제 1 — 환경 좌표 파일 쓰기 (30분)

현재 쓰는 테스트 환경 하나를 골라 본문 형태의 좌표 파일을 작성한다. 네 축(앱 버전· 구성·스키마·시드)과 의존 서비스를 모두 채운다.

  • 채울 수 없는 축이 있으면 그것이 첫 번째 개선 과제다. 왜 못 채우는지 한 줄로 적는다
  • 작성한 좌표를 어제 날짜 기준으로 한 번 더 채워, 두 좌표에서 다른 축을 찾는다
  • 그 차이가 최근 발생한 실패와 관련이 있는지 확인한다

과제 2 — 데이터 수명 정책표와 고아 탐지 (30분)

테스트가 만드는 데이터 종류를 5개 이상 나열하고, 각각에 소유자 식별 방법·수명·정리 책임을 지정한다. 표로 만든다.

그중 하나에 대해 고아 탐지 쿼리를 실제로 작성해 실행한다.

  • 고아가 몇 건 있는가, 가장 오래된 것은 언제 생성됐는가
  • 정리 책임이 "픽스처 해제"로만 되어 있는 항목이 있는가 — 있다면 러너 강제 종료 시나리오에서 어떻게 되는지 적는다

과제 3 — 프리플라이트 구현 계획 (25분)

본문의 체크 목록을 우리 환경으로 구체화해, 각 항목마다 세 가지를 적는다 — 확인 방법(엔드포인트·쿼리·명령), 목표 소요 시간, 실패 시 판정(중단/부분 건너뛰기).

마지막에 전체 목표 소요 시간을 합산한다. 60초를 넘으면 무엇을 뺄지 정한다 — 프리플라이트 자체가 느려지면 아무도 켜 두지 않는다.

자가 체크리스트

  • 어떤 데이터가 픽스처(m13-l10)의 일이고 어떤 것이 환경 운영(m59)의 일인지 기준으로 가를 수 있다
  • 환경 좌표의 네 축(앱 버전·구성·스키마·시드)을 열거하고 각각 어디서 읽어오는지 안다
  • 두 실행의 좌표를 비교해 "어제와 오늘의 차이"를 후보로 좁히는 절차를 안다
  • 환경 대장이 있고 현재 점유자와 마지막 초기화 시각이 기록된다
  • 초기화 세 전략의 비용·위험을 비교해 데이터 영역별로 다르게 적용할 수 있다
  • 캐시·검색 인덱스를 매 실행 비우는 이유를 설명할 수 있다
  • 테스트 데이터마다 소유자·수명·정리 책임 셋이 정해져 있다
  • 고아 데이터 탐지 쿼리가 있고 그 건수 추세를 주기적으로 본다
  • 운영 데이터를 복제해 쓰는 경우 마스킹·보존 기간이 정책으로 정해져 있다
  • 프리플라이트가 있고 실패 시 결과가 '환경 미준비' 라는 별도 상태로 기록된다

흔한 실수

환경 미준비를 테스트 실패로 기록한다. 가장 비용이 큰 실수다. 통과율과 플래키율이 오염되고, 레슨 5의 추세 전체가 거짓이 된다. 상태 코드를 분리하지 않으면 그 뒤의 모든 지표가 믿을 수 없다.

좌표 없이 "스테이징"이라고만 기록한다. 조사할 때 비교할 대상이 없어진다. 좌표 기록은 구현 비용이 작고(파일 하나 읽어 메타에 붙이기) 수익은 조사가 발생할 때마다 반복된다.

초기화를 전부 매번으로 한다. 안전해 보이지만 준비 시간이 고정비로 커지고 병렬이 막힌다. 영역별로 나누지 않고 일괄 적용하면 가장 비싼 선택이 된다.

더티 전략을 쓰면서 고아 탐지를 안 둔다. 더티는 정리와 탐지를 전제로만 성립한다. 탐지가 없으면 몇 달 뒤 DB 성능 문제와 설명할 수 없는 실패로 비용을 치른다.

정리를 픽스처 해제에만 맡긴다. 테스트가 타임아웃되거나 러너가 강제 종료되면 해제 코드는 실행되지 않는다. 주기 정리 작업이 항상 함께 있어야 한다.

프리플라이트를 점점 키운다. 실패할 때마다 체크를 하나씩 더하면 어느새 5분이 걸리고, 누군가 꺼 버린다. 목표 시간을 정하고, 새 체크를 넣을 때 기존 체크 하나를 빼는 규칙을 두면 유지된다.

참고 자료

  • ISTQB CTAL-TAE v2.0 실러버스 — 테스트 환경과 테스트 데이터 관리 절. 장 번호는 원문 확인
  • ISTQB Foundation Level v4.0 실러버스 — 테스트 환경 요구사항과 테스트 데이터 절
  • Jez Humble, David Farley, Continuous Delivery — 환경 관리와 데이터 마이그레이션 장
  • Docker·Testcontainers 공식 문서 — 실행 단위로 의존 서비스를 띄우고 버리는 방식. 지원 언어·수명주기 옵션은 원문 확인
  • 데이터베이스 마이그레이션 도구(Flyway·Liquibase 등) 공식 문서 — 마이그레이션 head 를 좌표로 쓰는 방법. 원문 확인
  • 개인정보 보호법·관련 고시의 가명처리 기준 — 운영 데이터 복제 시 적용 범위는 법무·보안 담당과 원문 확인
  • 이 커리큘럼 안의 관련 레슨: 모듈 13 레슨 10(API 픽스처·테스트 데이터), 모듈 14 레슨 3(Docker 재현성), 이 모듈 레슨 6(좌표와 결과 묶기)·레슨 7(환경 문제 분류)

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.
CI — 지속적 통합
코드를 올릴 때마다 자동으로 합쳐서 빌드하고 시험까지 돌리는 방식.
DB — 데이터베이스
데이터를 넣어 두고 꺼내 쓰는 저장소. 보통 표 여러 개가 서로 연결된 형태다.
ISTQB — 국제 소프트웨어 테스팅 자격 위원회
테스터 자격 시험을 만들고 용어를 정리하는 국제 단체.
CTAL-TAE — 상급 자격 · 테스트 자동화
상급 테스터 자격 중 자동화 설계를 다루는 과목.

용어집 전체 보기