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

gTAA — 계층 분리와 컴포넌트 설계

ISTQB CT-TAE v2.0 실러버스의 일반 테스트 자동화 아키텍처(gTAA)가 정의하는 네 핵심 역량(테스트 생성·정의·실행·적응)과 각 역량의 책임 범위, 계층을 섞었을 때의 변경 비용, TAS·TAA·TAF 의 구분, TAF 계층(테스트 스크립트·비즈니스 로직·코어 라이브러리)의 경계와 공통 컴포넌트 판정 기준을 다룬다.

  • #gTAA
  • #자동화아키텍처
  • #계층분리
  • #TAF
  • #ISTQB
  • #CTAL-TAE

개념

자동화가 죽는 방식은 거의 같다

버려진 자동화 저장소를 열어보면 패턴이 반복된다. 테스트 파일 하나하나가 혼자서 모든 일을 한다 — 로그인하고, 셀렉터로 요소를 찾고, API 를 직접 호출해 데이터를 만들고, 환경별 URL 을 if 로 분기한다. 처음 20개까지는 아무 문제가 없다(모듈 11 레슨 6). 문제는 그다음이다. 로그인 버튼 하나가 바뀌면 47개 파일을 고쳐야 하고, 새로 온 사람은 테스트를 읽어도 무엇을 검증하는 테스트인지 알 수 없다.

gTAA 란 무엇인가

ISTQB CT-TAE(Certified Tester Test Automation Engineering, v2.0 · 2024) 실러버스 3.1.1 은 gTAA 를 이렇게 규정한다 — 테스트 자동화와 그것이 연결되는 시스템들 사이의 소통을 추상적으로 보여주는 고수준 설계 개념. 연결 대상은 SUT(시험 대상 시스템), 프로젝트 관리, 테스트 관리, 형상 관리 네 가지다.

즉 gTAA 는 "Playwright 를 이렇게 쓰라"는 가이드가 아니다. 자동화 아키텍처(TAA)를 설계할 때 빠뜨리면 안 되는 역량의 목록 이라서, 도구를 바꿔도 설계 판단은 남는다.

인터페이스 네 개

인터페이스기술하는 것
SUT 인터페이스SUT 와 TAF 사이의 연결
프로젝트 관리 인터페이스자동화 개발의 진척
테스트 관리 인터페이스테스트 케이스 정의와 자동화된 테스트의 매핑
형상 관리 인터페이스CI/CD 파이프라인, 환경, 테스트웨어

셋째 줄이 현장에서 가장 자주 빠진다. 테스트 관리 도구의 TC-1047 과 저장소의 order-cancel.spec.ts 가 아무 연결 없이 따로 살면, "자동화 커버리지가 얼마인가"에 아무도 답할 수 없다.

네 가지 핵심 역량

실러버스는 자동화 도구·라이브러리가 제공하는 핵심 역량(core capabilities) 네 개를 든다 — 흔히 "gTAA 의 네 계층"으로 불리는 그것이다.

역량책임비고
테스트 생성(test generation)테스트 모델에 기반한 테스트 케이스의 자동 설계 지원실러버스가 선택적(optional) 역량 으로 명시
테스트 정의(test definition)테스트 케이스·테스트 스위트의 정의와 구현. 테스트 정의를 SUT·도구와 분리 한다테스트 데이터·테스트 케이스·테스트 라이브러리 컴포넌트로 다룬다
테스트 실행(test execution)테스트 실행과 테스트 로깅 지원실행 도구 + 로깅·리포팅 컴포넌트
테스트 적응(test adaptation)SUT 의 여러 컴포넌트·인터페이스에 자동 테스트를 적응 시키는 기능API·프로토콜·서비스로 연결하는 어댑터들

세 가지를 짚어둘 만하다. 테스트 생성은 선택이다 — 모델 기반 테스팅 도구(모듈 54)를 쓰지 않는 팀이라면 사람이 손으로 채우고, 그게 결함이 아니다. 정의와 실행은 다른 층이다 — 단언을 쓰는 일과 몇 개 병렬로 돌릴지 정하는 일은 다른 관심사다. 그리고 적응 계층이 가장 자주 없다 — 테스트가 page.click() 을 직접 부르면 적응 계층이 테스트 파일에 녹아 있는 것이다. 모듈 13 레슨 4의 페이지 객체가 이 계층을 제자리로 돌려놓는 가장 기본적인 장치다.

TAS · TAA · TAF — 세 단어를 구분해 써야 한다

실러버스 3.1.2~3.1.3 이 쓰는 세 용어를 섞어 쓰면 설계 논의가 꼬인다.

  • TAS(Test Automation Solution) — 실제로 구축해 운영하는 해결책 전체. SUT 의 기능·비기능·기술 요구를 이해한 데서 정의되고, 상용 또는 오픈소스 도구로 구현되며, SUT 전용 어댑터가 추가로 필요할 수 있다.
  • TAA(Test Automation Architecture) — TAS 전체의 기술 설계. 실러버스가 TAA 가 다뤄야 한다고 든 항목이 그대로 설계 체크리스트가 된다 — 도구·라이브러리 선정, 플러그인·컴포넌트 개발, 연결성·인터페이스 요구(방화벽·DB·URL·목·스텁·메시지 큐· 프로토콜), 테스트·결함 관리 도구 연결, 버전 관리 시스템과 저장소 활용.
  • TAF(Test Automation Framework) — TAS 의 토대. 테스트 하네스(= 테스트 러너), 테스트 라이브러리, 테스트 스크립트, 테스트 스위트를 포함한다.

실무 번역: "우리 자동화"라고 말할 때 사람들은 TAF 를 떠올리지만, 실패하는 자동화는 대개 TAA 에서 빠진 항목 때문에 죽는다 — 방화벽이 안 열려서, 결함 관리 연결이 없어서, 테스트 데이터 경로가 정해지지 않아서.

TAF 계층 — 세 층과 호출 방향

실러버스 3.1.3 은 TAF 계층을 세 층으로 예시하고, 호출 방향에 명확한 금지선 을 둔다.

테스트 스크립트 (Test scripts)
  SUT 의 테스트 케이스 저장소 + 스위트 주석
  비즈니스 로직 계층의 서비스를 호출한다
  ✗ 코어 라이브러리를 직접 호출하지 않는다
        │ 테스트 단계 · 사용자 흐름 · API 호출
        ▼
비즈니스 로직 (Business logic)
  SUT 에 의존하는 라이브러리가 모두 여기 산다
  코어 라이브러리를 상속하거나 그 파사드를 쓴다
        │
        ▼
코어 라이브러리 (Core libraries)
  어떤 SUT 에도 의존하지 않는 것만
  같은 개발 스택을 쓰는 어떤 프로젝트에서도 재사용

테스트 스크립트에서 코어 라이브러리로의 직접 호출을 하지 말라 는 것이 실러버스가 명시한 규칙이다.

그리고 실러버스는 계층을 많이 만들지 말라 고 권고한다 — 목적마다 계층을 하나씩 도입하면 설계가 복잡해질 수 있으니 TAF 계층 수를 낮게 유지하라는 것. 과잉 추상화에 대한 가장 강한 방어 근거다(레슨 2에서 다시 쓴다). 확장 모델도 함께 제시된다 — 여러 TAF 가 하나의 코어 위에 서고 다른 프로젝트가 그 코어를 가져다 쓴다. 즉 코어의 재사용 단위는 프로젝트가 아니라 개발 스택 이다.

계층을 섞으면 생기는 일 — 셀렉터로 보는 비용

추상적인 얘기를 숫자로 바꿔 보자. 셀렉터가 테스트 파일에 흩어진 저장소에서 무엇이 깨지는가.

// ✗ 계층 혼합 — 적응(셀렉터)·정의(검증)·실행(환경)이 한 파일에 섞였다
import { test, expect } from '@playwright/test';
 
test('주문 취소', async ({ page }) => {
  const base = process.env.ENV === 'stage'
    ? 'https://stage.shop.example'
    : 'https://dev.shop.example';                                  // 실행 관심사
  await page.goto(`${base}/login`);
  await page.fill('#login-form > div:nth-child(2) > input', 'qa@example.com');
  await page.fill('input[name="passwd"]', process.env.QA_PW!);      // 적응 관심사
  await page.click('.btn.btn-primary.submit');
  await page.goto(`${base}/orders`);
  await page.click('table tbody tr:first-child .cancel');
  await expect(page.locator('.toast-success')).toContainText('취소'); // 정의 관심사
});
// ○ 테스트 스크립트는 "무엇을 검증하는가"만 말한다
import { test, expect } from '../fixtures/shop-fixtures';
 
test('배송 전 주문은 취소할 수 있다', async ({ shopper, orderList }) => {
  const order = await shopper.givenPaidOrder({ sku: 'SKU-1001' });
  await orderList.open();
  await orderList.cancel(order.orderNo);
  await expect(orderList.toast).toContainText('취소되었습니다');
});

이 구조가 만드는 변경 비용은 여러 갈래로 퍼진다.

바뀌는 것흩어진 구조에서계층이 분리된 구조에서
로그인 버튼 클래스명로그인하는 모든 테스트 (보통 전부)LoginPage 한 곳
주문 목록 테이블 구조주문을 만지는 모든 테스트OrderListPage 한 곳
환경 추가(qa2)if 분기가 있는 모든 파일설정 파일 한 곳

더 나쁜 건 고쳐야 할 파일을 찾는 비용 이다. .btn-primary 는 전수 검색이 안 된다 — 로그인 버튼인지 저장 버튼인지 구분이 안 되니까. 결국 전부 열어 본다. 그리고 읽히지 않는다 — 첫 예제 15줄 중 검증은 마지막 한 줄이다. 두 번째 예제에는 셀렉터가 한 줄도, URL 도, 환경 분기도 없다. 그게 핵심이다 — 테스트 스크립트는 비즈니스 로직 계층의 서비스를 호출 할 뿐이다.

공통 컴포넌트의 경계 — 무엇이 어느 층에 사는가

판단 기준은 하나다 — 이 코드가 다른 제품의 자동화에서도 글자 하나 안 바꾸고 쓰일 수 있는가? 그렇다면 코어 라이브러리, 아니라면 비즈니스 로직이다.

코드어느 층이유
재시도 가능한 HTTP 클라이언트 래퍼코어어떤 SUT 든 동일
상관 ID 생성·주입, 구조화 로거, 아티팩트 경로 규칙코어SUT 무관
LoginPage, OrderListPage비즈니스 로직이 제품의 화면
givenPaidOrder() 시드 함수비즈니스 로직이 제품의 API
expectOrderCancelled() 복합 단언비즈니스 로직이 제품의 도메인
order-cancel.spec.ts테스트 스크립트케이스 그 자체

경계를 잘못 그었을 때의 증상은 역방향 의존 으로 나타난다. 코어가 OrderStatus 같은 도메인 타입을 import 하기 시작하면 그 코드는 이미 코어가 아니다. tsconfig.json 의 paths 로 @core/*·@domain/* 별칭을 잡고 린트로 방향을 고정하면 기계적으로 막을 수 있다.

// eslint.config.js — 의존 방향을 린트로 고정한다
export default [
  {
    files: ['src/core/**/*.ts'],
    rules: {
      'no-restricted-imports': ['error', {
        patterns: [
          { group: ['@domain/*', '../domain/*', '**/domain/**'], message: '코어는 도메인에 의존하면 안 된다' },
        ],
      }],
    },
  },
  {
    files: ['tests/**/*.spec.ts'],
    rules: {
      'no-restricted-imports': ['error', {
        patterns: [{ group: ['@core/*'], message: '테스트는 비즈니스 로직 계층을 통해 코어를 쓴다' }],
      }],
    },
  },
];

문서로만 적어둔 계층 규칙은 무너진다. CI 가 거절하는 규칙만 살아남는다.

gTAA 를 Playwright 저장소에 겹쳐 보기

gTAA 역량이 저장소에서 해당하는 것
테스트 생성(없음 — 선택적 역량. 케이스는 사람이 설계)
테스트 정의tests/**.spec.ts, src/domain/assertions/, src/domain/data/
테스트 실행playwright.config.ts(프로젝트·병렬·재시도·리포터), CI 워크플로
테스트 적응src/domain/pages/, src/domain/api/, src/core/http/

여기서 테스트 실행 역량의 상당 부분은 모듈 14(CI/CD)와 모듈 59(TestOps)가 맡는다. 이 모듈은 저장소 안의 구조 — 정의와 적응 — 에 집중한다.

실무에서 왜 필요한가

구조 설계는 "있으면 좋은 것"이 아니라 자동화의 수명을 결정하는 변수 다.

첫째, 변경 비용이 선형이 아니다. 셀렉터가 흩어진 저장소는 테스트 수에 비례해 변경 비용이 커진다 — 테스트 200개면 UI 리뉴얼 한 번에 200개를 본다. 계층이 분리돼 있으면 화면 수에 비례한다. 이 차이가 "유지할 사람 0.5명"과 "2명"을 가른다.

둘째, 인수인계가 가능해진다. 만든 사람이 떠난 뒤에도 돌아가는 저장소는 테스트 파일을 열었을 때 도메인 언어만 보인다. givenPaidOrder() 는 QA 신입도 읽지만, 깊은 CSS 경로가 박힌 page.fill(...) 은 만든 사람도 3개월 뒤엔 못 읽는다.

셋째, 공통 어휘가 설계 리뷰 시간을 줄인다. "이건 적응 계층에 있어야 하는 로직이 정의 계층에 올라와 있다"는 한 문장이 20분짜리 애매한 대화를 대체한다. 같은 이유로 아키텍처는 협상 도구가 된다 — 레슨 4에서 개발팀에 "셀렉터가 자꾸 깨져서요"라고 말하는 것과 "적응 계층이 DOM 구조에 결합돼 스타일 변경이 테스트 실패로 번집니다"라고 말하는 것은 다른 결과를 낳는다.

채용시장에서의 위치

자동화 아키텍처 설계 경험은 자동화 엔지니어와 SDET·자동화 리드 를 가르는 선이다. 공고의 "테스트 프레임워크 설계 및 개선 경험" 문구가 가리키는 게 이 능력이고, 면접에서 "기존 자동화가 매일 깨지는 상황에 들어갔다면 첫 2주에 무엇을 보겠습니까"라는 질문의 모범 답안은 도구가 아니라 계층 진단이다 — 셀렉터가 어디 사는지, 환경 설정이 몇 곳에 흩어져 있는지, 테스트 파일에서 도메인 언어가 보이는지. ISTQB CT-TAE v2.0 실러버스 3장이 이 레슨의 원전이며, 자격증을 따지 않더라도 네 용어를 정확히 구분해 쓰는 것만으로 설계 논의에서 신뢰를 얻는다(모듈 42 레슨 6의 자격증 지도 참고).

실습 과제

과제 1 — 우리 저장소의 계층 진단 (20분)

담당하는 자동화 저장소(없으면 공개 예제)에서 네 숫자를 센다 — (1) .spec.ts 중 CSS 셀렉터·XPath 문자열이 직접 든 파일 수 / 전체, (2) 환경별 URL·계정이 흩어진 파일 수, (3) 무작위 테스트 하나의 검증 줄 수 / 전체 줄 수 비율, (4) 코어로 분류할 코드가 도메인 타입을 import 하는 곳. 각각이 gTAA 의 어느 역량이 제자리에 없다는 신호인지 한 줄씩 붙인다. 개선안을 쓰지 말고 진단만 한다.

과제 2 — 경계 긋기 결정표 (15분)

아래 6개를 코어 라이브러리 / 비즈니스 로직 / 테스트 스크립트 중 하나로 배정하고 판단 기준 한 문장 을 함께 적는다.

  1. 응답이 429 면 지수 백오프로 3회 재시도하는 HTTP 래퍼
  2. "장바구니에 상품 3개를 담은 상태"를 만드는 함수
  3. 금액 문자열 1,200원 을 숫자로 바꾸는 파서
  4. 실행마다 유니크한 상관 ID 를 만들어 요청 헤더에 넣는 미들웨어
  5. "VIP 회원은 무료 배송" 규칙을 확인하는 복합 단언
  6. 쿠폰 코드 생성 API 를 호출하는 클라이언트

까다로운 건 3번과 6번이다. 애매할 때 어느 쪽에 두는 편이 되돌리기 쉬운지 를 적는다.

과제 3 — 의존 방향을 린트로 고정하기 (20분)

빈 디렉터리에 src/core/, src/domain/, tests/ 를 만들고 본문의 no-restricted-imports 설정을 적용한다. 그리고 일부러 규칙을 위반하는 파일 세 개 — src/core/retry.ts 가 @domain/order 를, tests/order.spec.ts 가 @core/http 를, src/core/logger.ts 가 ../domain/pages/LoginPage 를 import — 를 만들어 npx eslint . 이 정확히 3건을 잡는지 확인한다. 세 번째가 잡히지 않으면 패턴에 상대 경로가 빠진 것이다.

자가 체크리스트

  • gTAA 의 네 핵심 역량 이름을 보지 않고 말할 수 있고, 어느 것이 선택적 역량인지 안다
  • gTAA 의 인터페이스 네 개를 들고, 우리 팀에서 빠진 것을 지목할 수 있다
  • TAS · TAA · TAF 를 구분해 쓸 수 있고, TAF 가 TAS 의 토대라는 관계를 설명할 수 있다
  • TAF 세 계층의 호출 방향과 "테스트 스크립트는 코어를 직접 호출하지 않는다" 규칙을 안다
  • 셀렉터가 테스트 파일에 흩어졌을 때 커지는 비용을 변경 대상 파일 수 로 설명할 수 있다
  • 어떤 코드를 코어 라이브러리로 올릴지 "다른 제품에서도 그대로 쓰이는가" 기준으로 판정할 수 있다
  • 계층 규칙을 린트·tsconfig 로 강제하는 방법을 하나 이상 구현해 봤다
  • 실러버스가 계층 수를 낮게 유지하라고 권고하는 이유를 설명할 수 있다

흔한 실수

gTAA 를 디렉터리 구조로 착각한다. test-generation/, test-definition/ 같은 네 폴더를 만드는 저장소를 본다. gTAA 는 역량 목록이고, 디렉터리는 TAF 계층(스크립트· 비즈니스 로직·코어)에 맞추는 편이 실제로 동작한다. 역량과 폴더는 1:1 이 아니다.

계층을 과하게 쪼갠다. 실러버스 자체가 계층 수를 낮게 유지하라고 적어 뒀다. 다섯 층을 지나야 클릭 하나에 도달하는 구조는 계층 분리가 아니라 계층 중독 이다. 레슨 2에서 이 비용을 구체적으로 센다.

페이지 객체를 만들었으니 계층 분리가 끝났다고 생각한다. 페이지 객체는 적응 계층의 일부일 뿐이다. 환경 설정, API 어댑터, 테스트 데이터 생성, 공통 단언이 여전히 테스트 파일에 있으면 섞인 상태다.

코어 라이브러리에 도메인이 새어 들어간다. core/utils.ts 가 가장 흔한 범인이다. 이름이 utils 인 파일은 시간이 지나면 무엇이든 받아들인다. 코어에는 utils 라는 이름의 파일을 두지 않는 편이 안전하다 — retry.ts, clock.ts 처럼 책임이 이름에 드러나게 쓴다.

테스트 관리 인터페이스를 끝까지 비워 둔다. 자동화 TC 와 테스트 관리 도구의 케이스 id 를 잇지 않으면 "무엇이 자동화됐는가"를 아무도 모르게 된다. 최소한 테스트 제목이나 태그에 케이스 id 를 심는다.

로케이터 작성법과 구조 설계를 같은 문제로 다룬다. 좋은 로케이터를 쓰는 일(모듈 13 레슨 3)은 깨지는 빈도를, 로케이터를 어디에 둘지 정하는 일은 깨졌을 때의 비용을 줄인다. 하나로 다른 하나를 대체할 수 없다.

참고 자료

  • ISTQB, Certified Tester Test Automation Engineering (CT-TAE) Syllabus v2.0, 2024 — 3.1.1 gTAA 와 네 핵심 역량, 3.1.2 TAS·TAA, 3.1.3 TAF 계층. 이 레슨의 용어는 이 판 기준이며, 2016/2019 판은 구성이 달라 판본 확인 이 필요하다
  • ISTQB Glossary — test automation architecture, test automation framework, test automation solution, test adaptation layer 항목의 공식 정의. 실러버스 PDF 는 ISTQB 및 각국 보드(ANZTB·GTB·GASQ 등)가 배포한다
  • Playwright 공식 문서 — Page Object Models 가이드. 적응 계층을 TypeScript 로 구현하는 최소 패턴
  • 모듈 11 레슨 6(자동화 프레임워크 유형), 모듈 13 레슨 3·4(견고한 로케이터, Page Object Model) — 이 레슨의 계층 설계를 구현 기법과 이어 읽는다
  • 모듈 54(모델 기반 테스팅 실무) — gTAA 의 선택적 역량인 테스트 생성을 채우는 접근

낯선 말 풀이

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

API — 응용 프로그램 인터페이스
프로그램끼리 주고받는 창구. 사람이 화면 버튼을 누르는 대신, 프로그램이 정해진 주소로 요청을 보내 답을 받는다.
URL — 인터넷 주소
웹에서 무언가의 위치를 가리키는 주소 한 줄.
ISTQB — 국제 소프트웨어 테스팅 자격 위원회
테스터 자격 시험을 만들고 용어를 정리하는 국제 단체.
CT-TAE — 전문 자격 · 테스트 자동화
자동화 설계를 다루는 과목. 상급 자격의 같은 과목을 이렇게도 표기한다.
SUT — 시험 대상 시스템
지금 시험하고 있는 바로 그 시스템. 시험하는 도구 쪽과 구별해 부를 때 쓴다.
CI/CD — 통합·배포 자동화
합치고 시험하고 배포하는 과정을 자동으로 이어 둔 흐름 전체.
DB — 데이터베이스
데이터를 넣어 두고 꺼내 쓰는 저장소. 보통 표 여러 개가 서로 연결된 형태다.
HTTP — 하이퍼텍스트 전송 규약
웹에서 요청과 응답을 주고받는 약속. "무엇을 달라"고 보내면 "여기 있다" 또는 "그런 건 없다"가 돌아온다.
ID — 식별자
무엇 하나를 다른 것과 구별하기 위해 붙인 이름이나 번호.
CI — 지속적 통합
코드를 올릴 때마다 자동으로 합쳐서 빌드하고 시험까지 돌리는 방식.
UI — 사용자 인터페이스
사람이 직접 보고 누르는 부분 — 화면, 버튼, 입력창.
QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.

이 레슨에 나온 나머지 6개를 포함해 용어집 전체 보기