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

테스트 용이성 확보 협상 — 훅·테스트 모드·안정 식별자 요구하기

테스트 용이성을 부탁이 아니라 요구사항으로 다루는 법을 다룬다. 개발팀에 요구할 네 묶음(안정 식별자, 테스트 모드와 훅, 상태 초기화 API, 관측 가능성)을 비용과 함께 정리하고, 설계 단계에 끼어드는 시점과 질문 형식, 거절당했을 때의 대안과 비용 비교, 테스트 훅이 프로덕션에 남지 않게 하는 가드, 그리고 테스트 용이성을 DoD와 수용 기준에 넣는 문구까지 만든다.

  • #테스트용이성
  • #관측성
  • #제어성
  • #DoD
  • #수용기준
  • #협상

개념

테스트 용이성은 요구사항이지 부탁이 아니다

자동화가 깨지는 원인의 상당수는 자동화 코드 밖에 있다. 클래스명이 바뀌는 버튼, 현재 시각에 따라 결과가 달라지는 화면, 초기화할 방법이 없는 상태, 외부 결제사가 멈추면 같이 멈추는 흐름. 이런 조건에서 자동화를 안정화하려는 노력은 대부분 헛수고 다 — 구조로 못 막는 문제를 기법으로 덮는 중이기 때문이다.

전환점은 말의 틀을 바꾸는 데서 온다. "셀렉터 좀 안 바뀌게 해 주실 수 있나요" 는 부탁 이고, "이 화면은 자동 검증이 불가능한 상태이며 그 비용은 회귀 1회당 수동 2시간입니다" 는 요구사항 제기 다. 레슨 1이 쓴 gTAA 어휘로 말하면 테스트 용이성은 SUT 가 적응 계층에 제공해야 하는 인터페이스 품질 이고, ISTQB 실러버스가 자동화 인프라와 함께 테스트 용이성을 다루는 이유도 그것이 TAS 의 전제 조건이기 때문이다.

요구 항목은 두 축으로 정리하면 빠진 것이 보인다.

  • 관측성(observability) — 시스템의 상태를 밖에서 읽을 수 있는가. 안정 식별자, 구조화 로그, 상관 ID 전파, 상태 조회·버전 엔드포인트.
  • 제어성(controllability) — 시스템을 원하는 상태로 만들 수 있는가. 시드 데이터 주입, 상태 초기화, 시간 고정, 외부 연동 스텁, 기능 플래그 강제.

자동화가 어려운 화면은 거의 항상 둘 중 하나가 없다. 어느 쪽이 없는지 먼저 말하면 대화가 요구 목록으로 바로 넘어간다.

요구 목록 1 — 안정 식별자 (가장 싸고 가장 효과가 크다)

<!-- ✗ 자동화가 깨지는 구조: 스타일이 식별자를 겸한다 -->
<button class="btn btn-primary mt-2">주문 취소</button>
 
<!-- ○ 테스트 전용 식별자. 리팩터링·디자인 변경과 분리된다 -->
<button class="btn btn-primary mt-2" data-testid="order-cancel">주문 취소</button>

요구할 것은 속성 하나가 아니라 규칙 이다. (1) 속성 이름을 하나로 통일하고 (data-testid 가 Playwright 기본값이며 testIdAttribute 로 바꿀 수 있다), (2) 값의 작명 규칙을 정하고(영역-대상-동작 처럼), (3) 그 속성을 지우는 변경은 리뷰에서 막는다. 셋 중 셋째가 빠지면 반년 뒤 절반이 사라져 있다.

개발 비용은 거의 없다. 그래서 이것부터 요구한다 — 첫 요구가 거절당하면 다음 요구는 훨씬 어려워진다. 다만 접근성 속성이 이미 잘 붙은 제품이라면 역할·이름 기반 로케이터 (getByRole)가 먼저이고, 그것으로 안 되는 지점에만 테스트 id 를 요구하는 쪽이 설득력이 높다(모듈 13 레슨 3).

요구 목록 2 — 테스트 모드와 훅

요구없을 때 자동화가 치르는 비용
시간 고정·이동(쿠폰 만료·정산 마감 등)해당 시나리오를 아예 자동화 못 하거나 날짜마다 깨진다
외부 연동 스텁(PG·문자·배송)외부 장애가 곧 테스트 실패. 결제 테스트 비용 발생
기능 플래그 강제 지정플래그 상태에 따라 간헐 실패
OTP·캡차 우회(테스트 환경 한정)로그인 자동화가 불가능

여기서 반드시 함께 설계할 것이 프로덕션 유출 방지 다. 테스트 훅은 그 자체로 취약점이 될 수 있으므로, 요구할 때 가드를 함께 제안 해야 개발팀이 받아들인다.

// 서버 측 예시 — 테스트 전용 라우트는 세 겹으로 막는다
export function mountTestHooks(app: Express) {
  if (process.env.NODE_ENV === 'production' || process.env.TEST_HOOKS !== 'on') {
    return;                                   // 1) 환경에서 아예 마운트되지 않는다
  }
  app.use('/test', requireSharedSecret());    // 2) 전용 토큰 없이는 호출 불가
  app.post('/test/clock', setFrozenTime);     // 3) 감사 로그에 호출을 남긴다
  app.post('/test/seed/orders', seedOrders);
}

제안의 요지는 "테스트를 위해 구멍을 내 달라" 가 아니라 "환경 변수로 꺼지고 토큰으로 잠기며 호출이 로그에 남는 전용 경로를 하나 두자" 다. 이 문장을 준비해 가면 보안 담당 자가 반대하기 어렵다. 반대로 가드 없이 요구하면 한 번 거절당하고 그 뒤로는 주제 자체가 금기가 된다.

요구 목록 3 — 상태 초기화와 시드 API

레슨 2에서 전제 조건은 API 로 만든다고 정했다. 그 API 가 없으면 UI 경유 시딩으로 떨어지고, 테스트 하나당 수십 초가 더 붙는다.

POST /test/seed/orders      { "status": "paid", "sku": "SKU-1001" }  → { "orderNo": "..." }
DELETE /test/orders/{no}                                             → 204
POST /test/reset            { "scope": "tenant", "tenantId": "qa-7" } → 204

요구를 통과시키는 핵심은 범위를 작게 쪼개 들어가는 것 이다. "테스트용 API 를 만들어 주세요" 는 막연해서 백로그 맨 뒤로 간다. "회귀 자동화 12건이 막혀 있고, 그 12건은 결제완료 주문 1건 생성 하나만 있으면 풀립니다" 는 스토리 하나로 들어간다.

초기화의 대안도 함께 제시한다 — 전체 리셋이 위험하다면 테넌트·계정 단위 격리 로 좁히고, 그것도 어렵다면 테스트가 만든 데이터만 역추적해 지우는 쪽으로 간다. 선택지를 두 개 이상 들고 가면 거절이 아니라 선택이 된다.

요구 목록 4 — 관측 가능성

레슨 3의 상관 ID 구조는 서버가 그 헤더를 로그에 남겨 줄 때만 작동한다. 그래서 이것은 자동화 쪽 요구가 아니라 공통 로깅 규약 요구다.

  • 들어온 x-correlation-id 를 받아 그대로 로그 필드에 기록하고, 하위 호출에 전파한다
  • 요청 로그에 상태 코드·소요 시간·사용자 식별자를 구조화 필드 로 남긴다
  • 배포 버전·커밋 해시를 알려주는 엔드포인트를 둔다 — 실패가 어느 빌드 에서 났는지 리포트에 적을 수 있다
  • 비동기 작업(배치·큐)의 완료를 조회할 방법을 둔다 — 없으면 테스트가 대기로 때운다

마지막 항목이 특히 값지다. 큐 처리 완료를 폴링할 상태 조회가 없으면 자동화는 waitForTimeout 으로 때우게 되고, 그 지점이 플래키의 주요 생산지가 된다. 즉 관측성 결핍은 플래키로 환전된다.

언제 요구하는가 — 설계 단계에 끼어든다

구현이 끝난 뒤에 하는 요구는 재작업 요청이 되어 거의 거절된다. 끼어들 지점은 셋이다.

시점할 일형식
리파인먼트테스트 용이성 질문 3개를 던진다"이 상태는 어떻게 만들 수 있나요?"
설계 리뷰식별자·훅·로그 요구를 설계 항목으로 올린다리뷰 코멘트
PR 리뷰합의한 항목이 실제로 들어왔는지 본다체크리스트

리파인먼트에서 쓰는 질문 세 개는 외워 둔다 — "이 화면의 성공·실패 상태를 어떻게 만들죠?", "이게 잘 동작했는지 UI 말고 무엇으로 확인하죠?", "외부 연동은 테스트 환경에서 무엇으로 대체되죠?" 이 질문은 요구처럼 들리지 않으면서 요구와 같은 일을 한다. 그리고 세 질문에 답이 없다는 사실 자체가 설계가 덜 됐다는 신호 라서, 개발팀도 답하면서 얻는 게 있다. 요구를 개발팀의 이익으로 번역하는 것이 협상의 기본이다 — 테스트 모드와 시드 API 는 개발자의 로컬 재현 시간도 줄인다.

거절당했을 때 — 대안과 비용을 같이 내민다

거절은 정상이다. 준비해야 할 것은 설득이 아니라 대안과 그 비용 이다.

거절된 요구차선비용
안정 식별자역할·텍스트 기반 로케이터문구 변경마다 수정. 다국어면 더 심함
시드 APIUI 경유 시딩테스트당 20~60초 추가, 실패 지점 증가
시간 고정상대 시간으로 케이스 재설계, 일부 포기만료·마감 시나리오 커버리지 손실
외부 연동 스텁샌드박스 계정 사용, 야간 실행외부 장애가 실패로 들어옴
상태 초기화실행마다 신규 계정 생성데이터 누적, 정리 잡 필요

이 표를 들고 가는 목적은 압박이 아니다. 비용을 누가 지불할지 결정하게 만드는 것 이다. 차선으로 가기로 했다면 그 비용(추가 실행 시간, 커버리지 공백, 플래키 증가)을 기록에 남긴다 — 리스크 등록부나 자동화 백로그에 한 줄로. 6개월 뒤 "자동화가 왜 느리냐" 는 질문이 올 때 이 기록이 답이 된다. 반대로 기록하지 않으면 그 비용은 QA 의 역량 문제로 해석된다.

그리고 우리가 PR 을 보내는 선택지 를 늘 가지고 있는다. 안정 식별자 추가는 대개 QA 가 직접 보낼 수 있는 변경이다. "해 주세요" 보다 "이렇게 올려 뒀으니 봐 주세요" 가 통과율이 훨씬 높고, 한 번 통과하면 그 뒤로는 관행이 된다.

DoD·수용 기준에 넣어 영구화한다

한 번의 협상으로 얻은 것은 다음 스프린트에 사라진다. 제도화 지점은 두 곳이다.

완료 정의(모듈 48 레슨 2)에 넣는 문구 예시 — 팀 전체에 한 번 적용된다.

  • 새로 추가·변경된 상호작용 요소에 합의된 테스트 식별자가 있다
  • 새 기능의 주요 상태를 만들 방법(시드 또는 훅)이 문서화돼 있다
  • 서버 로그에 상관 ID 가 전파되고, 신규 엔드포인트도 이를 따른다

수용 기준 에 넣는 문구 예시 — 스토리 하나에 붙는다.

  • (주어진) 쿠폰 만료 시나리오를 검증하려면 (그때) 테스트 환경에서 기준 시각을 지정할 수 있어야 한다
  • 주문 상태 조회 응답에 상태 전이 시각이 포함되어, UI 없이도 결과를 확인할 수 있다

문구를 쓸 때의 원칙은 검증 가능하게 쓰는 것 이다. "테스트하기 쉽게 만든다" 는 판정할 수 없어서 DoD 에 들어가도 아무 일도 일어나지 않는다.

실무에서 왜 필요한가

첫째, 자동화의 상한은 테스트 용이성이 정한다. 아무리 좋은 프레임워크도 상태를 만들 수 없는 시나리오는 자동화하지 못한다. 자동화 커버리지가 60%에서 멈추는 팀의 원인은 대개 기술이 아니라 제어성 결핍이다.

둘째, 협상 능력이 자동화 엔지니어와 자동화 리드를 가른다. 코드로 풀 수 없는 문제를 알아보고, 그것을 개발팀의 언어와 비용으로 번역해 설계 단계에 밀어 넣는 일은 직접 구현보다 레버리지가 크다. 식별자 규칙을 DoD 에 한 줄 넣는 것이 셀렉터 수정 300건을 없앤다.

셋째, 거절을 기록으로 남기는 습관이 QA 의 신뢰를 지킨다. 테스트 용이성을 요구했고 거절됐으며 그 대가가 무엇인지 적혀 있다면, 나중에 발생한 공백은 결정의 결과다. 적어 두지 않으면 같은 공백이 "자동화가 부실하다" 로 돌아온다.

실습 과제

과제 1 — 테스트 용이성 결핍 지도 (30분)

담당 제품에서 자동화하지 못한(또는 자주 깨지는) 시나리오 5개 를 적고, 각각이 관측성 결핍인지 제어성 결핍인지 분류한다. 그다음 결핍을 해소할 구체적 요구 한 줄 과 그 요구가 없을 때의 월 비용(수동 실행 시간 또는 실패 트리아지 시간)을 붙인다. 결과물은 5행 표다. 모호한 요구("테스트하기 쉽게")는 다시 쓴다.

과제 2 — 리파인먼트 질문 리허설 (25분)

다음 스프린트의 스토리 2개를 골라, 본문의 질문 세 개를 실제로 적용해 답을 받아 본다(회의가 없으면 담당 개발자에게 메시지로). 받은 답을 그대로 스토리의 수용 기준 문구로 바꿔 쓴다. 보고할 것은 질문 전후로 스토리가 어떻게 달라졌는지 다. 답이 "모르겠다" 로 돌아온 질문이 가장 값진 결과다 — 설계 공백을 찾은 것이다.

과제 3 — 가드가 포함된 테스트 훅 제안서 (35분)

가장 아쉬운 훅 하나(시간 고정 / 시드 / 상태 초기화 중 하나)를 골라 한 페이지로 쓴다. 담을 것은 다섯 — (1) 요구하는 엔드포인트·동작, (2) 프로덕션 유출 방지 가드 세 겹, (3) 그것이 풀어주는 테스트 케이스 수, (4) 없을 때의 차선과 비용, (5) 개발팀이 얻는 이익(로컬 재현·디버깅). 그다음 DoD 한 줄과 수용 기준 한 줄 로 제도화 문구를 붙인다. 이 문서가 레슨 5의 제안서에 그대로 들어간다.

자가 체크리스트

  • 테스트 용이성을 관측성·제어성 두 축으로 나눠 결핍을 진단할 수 있다
  • 안정 식별자를 요구할 때 속성·작명 규칙·리뷰 보호 셋을 함께 요구한다
  • getByRole 기반 접근을 먼저 시도하고, 그것으로 안 되는 지점에만 테스트 id 를 요구한다
  • 테스트 훅을 요구할 때 프로덕션 유출 방지 가드를 함께 제안할 수 있다
  • 관측성 결핍이 플래키로 환전되는 경로(대기로 때우기)를 설명할 수 있다
  • 리파인먼트에서 던질 테스트 용이성 질문 세 개를 외워 말할 수 있다
  • 거절당했을 때의 차선과 그 비용을 표로 제시할 수 있다
  • 테스트 용이성 항목을 검증 가능한 문장으로 DoD·수용 기준에 쓸 수 있다

흔한 실수

구현이 끝난 뒤에 요구한다. 이미 만들어진 화면에 식별자를 넣어 달라는 요청은 재작업 요청이다. 같은 요구를 리파인먼트에서 하면 거의 통과한다. 시점이 내용보다 중요하다.

가장 비싼 요구부터 꺼낸다. 첫 대화에서 "테스트용 API 서버를 만들어 주세요" 를 꺼내면 거절되고, 그 뒤 식별자 요구마저 어려워진다. 싸고 효과 큰 것부터 순서를 지킨다.

가드 없이 테스트 훅을 요구한다. 보안 검토에서 한 번 막히면 주제가 금기가 된다. 환경 가드·인증·감사 로그 세 겹을 먼저 설계해 가져간다.

자동화의 편의로 말한다. "자동화가 편해집니다" 는 개발팀의 우선순위가 아니다. 같은 요구를 릴리스 리스크·수동 회귀 시간·로컬 재현 시간 으로 번역한다.

합의를 구두로 끝낸다. 식별자 규칙을 회의에서 합의하고 어디에도 적지 않으면 다음 스프린트에 사라진다. DoD·PR 템플릿·린트 규칙 중 최소 하나 에 심는다.

거절을 기록하지 않는다. 거절 자체는 문제가 아니지만, 기록이 없으면 그 결과로 생긴 커버리지 공백이 QA 의 실력 문제로 해석된다. 한 줄이라도 남긴다.

참고 자료

  • ISTQB, Certified Tester Test Automation Engineering (CT-TAE) Syllabus v2.0, 2024 — 자동화를 위한 SUT 테스트 용이성과 자동화 인프라 요구. 장·절 번호는 판본마다 달라 원문 확인 이 필요하다
  • ISTQB Glossary — testability, observability, controllability 항목의 공식 정의. 요구를 문서에 쓸 때 이 용어를 그대로 쓴다
  • Playwright 공식 문서 — Locators(getByRole·getByTestId), testIdAttribute 설정, Clock(시간 제어 API) 과 Network mocking. 지원 범위는 버전마다 달라 원문 확인 이 필요하다
  • 모듈 48 레슨 2 — 완료 정의. 테스트 용이성 항목을 팀 규칙으로 영구화하는 자리
  • 모듈 13 레슨 3 — 견고한 로케이터. 식별자 요구 전에 먼저 시도할 접근
  • 레슨 2(저장소 구조), 레슨 3(상관 ID 와 리포팅), 레슨 5(이 요구들을 제안서로 묶기)

낯선 말 풀이

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

SUT — 시험 대상 시스템
지금 시험하고 있는 바로 그 시스템. 시험하는 도구 쪽과 구별해 부를 때 쓴다.
ISTQB — 국제 소프트웨어 테스팅 자격 위원회
테스터 자격 시험을 만들고 용어를 정리하는 국제 단체.
ID — 식별자
무엇 하나를 다른 것과 구별하기 위해 붙인 이름이나 번호.
PG — 결제 대행
쇼핑몰과 카드사 사이에서 결제를 중개해 주는 서비스.
API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.
UI — 사용자 인터페이스
사람이 직접 보고 누르는 부분 — 화면, 버튼, 입력창.
테넌트 — 입주자
한 시스템을 여러 고객사가 나눠 쓸 때, 그 고객사 하나하나를 가리키는 말. 건물의 입주자에 해당한다.
PR — 풀 리퀘스트
"내가 고친 코드를 본체에 합쳐 달라"고 올리는 요청. 합치기 전에 사람들이 보고 의견을 단다.
QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.
DoD — 완료의 정의
모든 작업에 똑같이 적용되는 "여기까지 해야 끝" 목록. 코드 검토·문서 갱신 같은 것들이다.
CT-TAE — 전문 자격 · 테스트 자동화
자동화 설계를 다루는 과목. 상급 자격의 같은 과목을 이렇게도 표기한다.

용어집 전체 보기