
테스트 자동화를 넘어: Playwright, CI/CD, AI를 활용한 확장 가능한 릴리스 신뢰 플랫폼 구축기
요약
Playwright와 CI/CD, AI를 결합하여 수동 회귀 테스트를 자동화하고 릴리스 신뢰도를 높이는 엔지니어링 플랫폼 구축 사례를 소개합니다. 단순 자동화를 넘어 테스트 커버리지를 분석하고 확장 가능한 아키텍처를 설계하는 과정을 다룹니다.
핵심 포인트
- Playwright를 활용한 결정론적 E2E 자동화 구현
- CI/CD 통합을 통한 릴리스 신뢰도 확보
- AI 기반 커버리지 분석으로 미테스트 영역 식별
- 재사용 가능한 자동화 아키텍처 설계의 중요성
Playwright, CI/CD, AI를 활용하여 확장 가능한 릴리스 신뢰 플랫폼을 구축한 방법
결정론적 자동화 (Deterministic automation), 재사용 가능한 아키텍처 (Reusable architecture), 그리고 AI 지원 커버리지 분석 (AI-assisted coverage analysis)을 통해 수동 회귀 테스트 (Manual regression) 프로세스에서 릴리스 신뢰도를 높이는 엔지니어링 플랫폼으로 진화하기까지.
서론 (Introduction)
소프트웨어가 성공할수록 소프트웨어 엔지니어링은 더 어려워집니다.
초기에는 릴리스를 배포하는 것이 간단합니다. 기능이 적고, 사용자 기반이 작으며, 통합 지점 (Integration points)이 제한적이기 때문입니다. 이때는 수동 검증 (Manual verification)이 실용적입니다.
하지만 그것은 오래가지 않습니다.
새로운 모듈이 배포되고, 워크플로우 (Workflows)가 서로 연결되며, 고립된 것처럼 보이는 변경 사항이 애플리케이션의 전혀 관련 없는 부분을 망가뜨리기 시작합니다.
문제는 더 이상 다음과 같지 않습니다:
"기능을 작성하라."
이제 문제는 다음과 같습니다:
- 기존 워크플로우를 망가뜨리지 않고 오늘 배포할 수 있는가?
- 핵심 사용자 여정 (Critical user journeys)이 여전히 작동한다는 것을 알고 변경 사항을 병합(Merge)할 수 있는가?
- 프로덕션(Production)에 도달하기 전에 무엇이 테스트되지 않고 있는지 파악할 수 있는가?
저희의 경우, 제품은 수많은 상호 연결된 워크플로우를 가진 대규모 엔터프라이즈 애플리케이션으로 성장했으며, 매달 한두 번씩 릴리스를 진행했습니다. 모든 릴리스는 배포 전 전체 수동 회귀 테스트 (Manual regression) 사이클을 완료하는 것에 의존했습니다.
회귀 시나리오 (Regression scenarios)는 스프레드시트에 존재했으며, 비즈니스 핵심 워크플로우 전반에 걸쳐 수동으로 실행되었습니다. 릴리스 범위에 따라 이는 거의 근무일 전체를 소모했으며, 이후 누군가가 프로덕션 배포를 승인할 수 있을 만큼 안심할 수 있을 때까지 추가적인 검증이 뒤따랐습니다.
어떤 릴리스는 단 한 가지 이유로 지연되었습니다:
회귀 테스트 (Regression)가 아직 끝나지 않았다.
소프트웨어는 준비되었습니다.
파이프라인 (Pipeline)도 준비되었습니다.
신뢰 (Confidence)가 준비되지 않았을 뿐입니다.
원래 목표는 간단해 보였습니다:
Playwright를 사용하여 수동 회귀 테스트를 자동화하라.
하지만 간단하게 유지되지 않았습니다.
병렬 실행 (Parallel execution)은 공유 상태 버그 (Shared-state bugs)를 드러냈습니다.
인증 (Authentication)은 훨씬 더 복잡해졌습니다.
불안정한 테스트 (Flaky tests)는 신뢰를 빠르게 무너뜨렸습니다.
수백 개의 시나리오를 유지 관리하는 것은 다른 프로덕션 소프트웨어 시스템을 유지 관리하는 것과 동일한 엔지니어링 규율 (Engineering discipline)을 요구했습니다.
결국 한 가지 깨달음이 프로젝트의 방향을 바꾸어 놓았습니다.
테스트를 통과한다는 것은 단 하나의 질문에만 답할 뿐입니다.
우리가 이미 알고 있는 시나리오들이 여전히 작동하는가?
이는 똑같이 중요한 또 다른 질문에는 답을 주지 못합니다.
우리가 전혀 테스트하지 않고 있는 중요한 사용자 여정 (User journeys)은 무엇인가?
그 깨달음이 노력을 완전히 변화시켰습니다.
단순히 Playwright 스크립트 모음을 구축하는 대신, 우리는 다음과 같은 요소들을 결합한 내부 엔지니어링 플랫폼을 구축했습니다:
- 결정론적 엔드 투 엔드 (End-to-end) 자동화
- CI/CD 통합
- 재사용 가능한 자동화 아키텍처 (Architecture)
- AI 지원 커버리지 분석 (AI-assisted coverage analysis)
AI 지원 커버리지 분석 에이전트는 기존의 Playwright 자동화를 검토하여, 누락된 비즈니스 워크플로 (Workflows)를 식별하고, 커버리지 공백을 강조하며, 이러한 사각지대가 프로덕션 장애 (Production incidents)로 이어지기 전에 자동화할 가치가 있는 다음 시나리오들을 추천합니다.
이것은 Playwright 튜토리얼이 아닙니다.
수동 회귀 테스트 (Manual regression)를 릴리스의 병목 현상에서 엔지니어들이 진정으로 신뢰할 수 있는 시스템으로 변모시키는 과정 뒤에 숨겨진 엔지니어링 결정, 실패, 트레이드오프 (Trade-offs), 그리고 아키텍처적 교훈에 관한 이야기입니다.
수동 회귀 테스트의 숨겨진 비용
우리는 매달 1~2회의 릴리스를 진행했습니다.
개발은 일반적으로 제시간에 완료되었습니다.
하지만 릴리스 준비가 병목 현상이 되었습니다.
처음에는 관리 가능한 체크리스트처럼 보였던 것이 점차 다음과 같은 항목들을 검증하기 위해 하루 전체를 소요하는 작업으로 진화했습니다:
- 워크플로 관리 (Workflow management)
- 문서 처리 (Document processing)
- 검색 (Search)
- 승인 흐름 (Approval flows)
- 인증 (Authentication)
- 관리 도구 (Administrative tooling)
...이 모든 것들이 아주 작은 기능만 변경되었을 때조차 매 릴리스 전에 수동으로 실행되었습니다.
이것은 규율 (Discipline)의 문제가 아니었습니다.
수동 검증은 단순히 확장성 (Scale)을 가질 수 없습니다.
매 릴리스마다 동일한 질문들이 제기되었습니다:
- 모든 핵심 워크플로우 (Workflow)를 테스트했는가?
- 이번 변경 사항이 다른 모듈을 조용히 망가뜨리지는 않았는가?
- 새로운 기능을 놓치면서 익숙한 경로만 반복해서 테스트하고 있지는 않은가?
- 편법을 쓰지 않고도 회귀 테스트 (Regression testing)를 충분히 빠르게 완료할 수 있는가?
너무나 자주, 마지막 질문에 대한 답변은 **아니오 (No)**였습니다.
릴리스가 지연되었습니다.
그 경험은 자동화에 대한 저의 관점을 근본적으로 바꾸어 놓았습니다.
자동화는 단순히 수동 작업의 노력을 줄이는 것이 아닙니다.
릴리스의 불확실성을 제거하는 것입니다.
엔지니어링 플랫폼이 답해야 할 진짜 질문은 다음과 같습니다:
우리는 오늘 이 변경 사항을 자신 있게 배포할 수 있는가?
이것이 이후에 이어지는 모든 것들의 설계 원칙이 되었습니다.
플랫폼 소유하기 (Owning the Platform)
팀에는 전담 QA(Quality Assurance)나 테스트 자동화 엔지니어가 없었습니다.
비즈니스에 핵심적인 기능을 설계하고 전달하는 것과 병행하여, 저는 이를 지원하는 자동화 플랫폼을 구축함으로써 릴리스 품질을 개선하는 책임을 맡았습니다.
그 차이는 중요했습니다.
프레임워크는 애플리케이션을 외부에서 테스트하는 사람의 관점에서 개발되지 않았습니다.
다음 사항들에 대한 이해를 바탕으로 설계되었습니다:
- 비즈니스 워크플로우 (Business workflows)
- 시스템 아키텍처 (System architecture)
- 운영상의 제약 사항 (Operational constraints)
- 최종 사용자에게 가장 중요한 유형의 장애 (Failures)
프레임워크를 구축하고, 이를 CI/CD 파이프라인에 통합하며, 실행 신뢰성을 개선하고, 애플리케이션과 함께 발전시켜 나가는 과정은 일반적인 기능 개발을 계속하는 와중에 이루어졌습니다.
그 경험은 테스트 자동화를 바라보는 저의 시각을 완전히 바꾸어 놓았습니다.
그것은 더 이상 Playwright 스크립트의 집합이 아니었습니다.
다음과 같은 것들을 통해 릴리스 신뢰도를 높이는 것을 목적으로 하는 엔지니어링 플랫폼이 되었습니다:
- 신뢰할 수 있는 실행 (Reliable execution)
- 재사용 가능한 아키텍처 (Reusable architecture)
- 결정론적 동작 (Deterministic behavior)
- 빠른 개발자 피드백 (Fast developer feedback)
이후의 모든 아키텍처 결정은 그 목표에 따라 추진되었습니다.
첫 번째 자동화 프레임워크 구축하기
왜 Playwright인가?
Playwright의 네이티브 TypeScript 지원, 자동 대기 (automatic waiting), 내장 어설션 (built-in assertions), 신뢰할 수 있는 병렬 실행 (parallel execution), 그리고 뛰어난 디버깅 기능은 유지보수 가능한 엔드 투 엔드 (end-to-end) 테스트 플랫폼을 구축하기 위한 강력한 토대가 되었습니다.
하나의 아키텍처 제약 사항이 거의 모든 설계 결정에 영향을 미쳤습니다.
해당 애플리케이션은 테스트 데이터를 준비하거나 시나리오를 부트스트래핑 (bootstrapping)하기 위한 전용 테스트 API를 제공하지 않았습니다.
따라서 자동화는 실제 사용자와 정확히 동일한 방식으로 시스템과 상호작용해야 했습니다.
이는 인증 (authentication), 피스처 (fixtures), 상태 격리 (state isolation), 그리고 테스트 데이터 관리가 단순한 편의 기능이 아님을 의미했습니다.
이들은 프레임워크 자체의 핵심 책임이 되었습니다.
이러한 제약 사항을 중심으로 설계함으로써, 자동화된 테스트가 특권이 있는 내부 API 대신 실제 사용자 여정 (user journeys)을 검증하도록 보장하였고, 결과적으로 생성된 피드백의 신뢰성을 현저히 높일 수 있었습니다.
확장을 고려한 설계 (Designing for Scale)
Playwright 테스트를 작성하는 것은 쉽습니다.
하지만 수백 개의 테스트를 유지보수하는 것은 쉽지 않습니다.
프레임워크는 초기 단계부터 책임을 다음과 같은 명확한 계층으로 분리했습니다:
- 설정 (Configuration)
- 테스트 스위트 (Test suites)
- 페이지 오브젝트 (Page Objects)
- 유틸리티 (Utilities)
- 피스처 (Fixtures)
- 테스트 데이터 (Test data)
- 리포팅 (Reporting)
이러한 분리를 통해 UI 변경이 발생하더라도 수십 개의 파일이 아닌 보통 하나의 파일만 영향을 받게 되었습니다.
테스트 스위트가 확장됨에 따라, 유지보수 난이도가 기하급수적으로 높아지는 대신 예측 가능한 수준을 유지할 수 있었습니다.
페이지 중심이 아닌 비즈니스 워크플로우 중심
테스트를 화면이나 UI 컴포넌트 단위로 구성하는 대신, 스위트를 완전한 비즈니스 기능 (business capabilities) 단위로 구성했습니다.
이 접근 방식은 다음과 같은 몇 가지 장점을 제공했습니다:
- 더 나은 소유권 (ownership)
- 타겟팅된 회귀 테스트 (regression) 실행
- 더 쉬운 실패 격리 (failure isolation)
- 신규 엔지니어의 더 쉬운 온보딩 (onboarding)
각 스위트는 고립된 버튼 클릭이나 페이지 상호작용이 아닌, 엔드 투 엔드 (end-to-end) 비즈니스 워크플로우를 검증했습니다.
이 프레임워크는 사용자가 실제로 제품과 상호작용하는 방식을 반영했습니다.
재사용 가능한 페이지 오브젝트 (Reusable Page Objects)
테스트는 가공되지 않은 셀렉터 (raw selectors) 대신 비즈니스 수준의 추상화 (abstractions)를 통해 상호작용했습니다.
스위트 (suite) 전반에 걸쳐 버튼과 필드를 반복적으로 찾는 대신, 구현 세부 사항은 재사용 가능한 페이지 오브젝트 (Page Objects) 내부에 캡슐화되었습니다.
이러한 분리를 통해 테스트 케이스는 비즈니스 의도 (business intent)에 집중할 수 있었습니다.
UI가 변경되었을 때, 업데이트는 수백 개의 테스트에 걸쳐 광범위한 수정을 요구하는 대신 대개 단 한 곳으로 국한되었습니다.
그 결과 코드는 더 깔끔해졌고, 가독성이 향상되었으며, 유지보수 비용은 극적으로 낮아졌습니다.
공유 픽스처 (Shared Fixtures)
다음 내용을 포함한 공통 설정 (setup)이 프레임워크 전반에 걸쳐 중앙 집중화되었습니다:
- 인증된 브라우저 컨텍스트 (Authenticated browser contexts)
- 테스트 데이터 준비 (Test data preparation)
- 브라우저 설정 (Browser configuration)
- 공유 픽스처 (Shared fixtures)
이는 보일러플레이트 (boilerplate)를 줄이는 것을 넘어, 모든 테스트가 예측 가능한 환경에서 시작됨을 보장했습니다.
자동화 스위트가 성장함에 따라, 이러한 일관성은 실행 신뢰성 (execution reliability)에 기여하는 가장 큰 요소 중 하나가 되었습니다.
프레임워크 아키텍처로서의 인증 (Authentication as Framework Architecture)
인증 (Authentication)은 개별 테스트마다 수행해야 하는 작업으로 취급되지 않았습니다.
대신, 인증은 프레임워크 자체의 일부가 되었습니다.
인증된 컨텍스트 (Authenticated contexts)는 적절한 곳에서 재사용되었으며, 이를 통해 반복적인 설정을 줄이는 동시에 실행 속도를 향상시켰습니다.
더 중요한 점은, 비즈니스 검증 (business validation)으로부터 인증을 분리함으로써 테스트가 병렬로 실행되기 시작할 때 발생하는 불안정성을 줄였다는 것입니다.
인증은 테스트 로직이 아닌 인프라 (infrastructure)가 되었습니다.
조사를 위해 구축된 리포팅 (Reporting Built for Investigation)
테스트가 실패했다는 사실을 아는 것만으로는 충분하지 않습니다.
엔지니어는 왜 실패했는지 알아야 합니다.
프레임워크는 모든 실행에 대해 다음과 같은 풍부한 디버깅 아티팩트 (debugging artifacts)를 생성했습니다:
- HTML 리포트 (HTML reports)
- Playwright 트레이스 (Playwright traces)
- 실행 로그 (Execution logs)
- 실패 스크린샷 (Failure screenshots)
엔지니어들은 실패를 로컬에서 재현하는 대신, CI 파이프라인 (CI pipeline)에서 직접 근본 원인 (root causes)을 식별할 수 있는 경우가 많았습니다.
조사(Investigation) 속도가 현저히 빨라졌으며, 팀은 문제를 재현하는 데 시간을 쓰기보다 문제를 해결하는 데 더 많은 시간을 할애할 수 있게 되었습니다.
초기 결과는 고무적이었습니다.
수동 회귀 테스트 (Manual regression) 노력이 크게 감소했습니다.
테스트 실행이 반복 가능해졌습니다.
릴리스는 훨씬 더 신뢰할 수 있는 검증 프로세스를 확보했습니다.
하지만 자동화 스위트 (automation suite)가 계속 성장하고 병렬 실행 (parallel execution)이 증가함에 따라, 완전히 다른 차원의 문제들이 나타나기 시작했습니다.
흥미롭게도, 그 문제들은 애플리케이션 내에 있지 않았습니다.
그것들은 자동화 프레임워크 (automation framework) 자체 내부에 있었습니다.
확장이 모든 것을 망쳤을 때
병렬 실행이 모든 것을 바꾸어 놓았습니다.
테스트를 동시에 실행함으로써 실행 시간을 극적으로 단축했지만, 그 즉시 아무도 의심하지 않았던 가정들이 드러났습니다.
순차 실행 (sequential execution)에서는 항상 통과하던 테스트들이 갑자기 간헐적으로 실패하기 시작했습니다.
동일한 스위트.
동일한 코드.
다른 결과.
그러한 불일치는 애플리케이션이 아닌 자동화 프레임워크에 아키텍처적 개선이 필요하다는 첫 번째 신호였습니다.
신뢰를 빠르게 무너뜨리는 불안정한 테스트 (Flaky Tests)
애플리케이션의 변경 없이도 테스트가 통과했다가, 실패했다가, 다시 통과하는 상황이 한 번 발생하면, 모든 실패는 다음과 같은 동일한 질문을 던지게 됩니다.
애플리케이션이 고장 난 것인가, 아니면 테스트가 고장 난 것인가?
엔지니어들이 이에 대해 확신을 가지고 답할 수 없다면, 자동화는 빠르게 신뢰를 잃습니다.
신뢰성 (Reliability)은 단순히 있으면 좋은 기능이 아닙니다.
그것은 개발자들이 자동화된 회귀 테스트를 충분히 신뢰하여 이를 바탕으로 릴리스 결정을 내릴 수 있는지를 결정하는 토대입니다.
근본 원인: 공유 상태 (Shared State)
가장 큰 문제는 공유 상태 (shared state)인 것으로 밝혀졌습니다.
병렬 워커 (Parallel workers)들이 의도치 않게 공통 리소스와 상호작용하고 있었습니다.
테스트들은 다음과 같이 가정하고 있었습니다:
- 이전 실행에서 생성된 데이터가 여전히 존재한다.
- 스위트가 실행되는 동안 애플리케이션 상태가 변하지 않고 유지된다.
두 가정 모두 병렬 실행 환경에서는 깨졌습니다.
재시도 (retries)를 추가하는 대신, 프레임워크는 각 시나리오가 독립적으로 실행될 수 있도록 테스트 실행 간의 강력한 격리 (isolation)를 도입했습니다.
재시도 (retries)가 아닌 격리 (isolation)가 장기적인 해결책이 되었습니다.
부하 상황에서의 인증 (Authentication Under Load)
인증 (Authentication) 또한 또 다른 확장성 과제가 되었습니다.
초기에는 인증된 세션 (authenticated sessions)을 여러 워커 (workers) 간에 재사용했습니다.
동시성 (concurrency)이 증가하기 전까지는 그것이 효과적이었습니다.
여러 워커가 인증 컨텍스트 (authentication contexts)를 공유하면서, 실행 중에 완전히 무작위로 나타나는 예측 불가능한 세션 동작이 발생했습니다.
해결책은 로그인 단계를 더 추가하는 것이 아니었습니다.
인증을 비즈니스 로직 (business logic)이 아닌 프레임워크 아키텍처 (framework architecture)로 취급하는 것이었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
