E2E 테스트 도입기

Sangmin Park,14 min read

E2E 테스트를 도입하면서 고려했던 점과 느꼈던 점들에 대해 알아봅니다.


개요

프론트엔드 환경에서 테스팅은 필수 요소입니다. 작성한 코드가 의도된 대로 작동하는지 확인하는 유닛 테스트부터, 실제 고객의 여정 전체를 검증하는 E2E 테스트까지 다양한 테스트 방법이 있습니다. 이 중에서도 E2E 테스트는 실제 사용자의 실행 환경과 거의 동일한 환경에서 테스트를 진행하기 때문에 실제로 발생하는 에러를 잡아낼 수 있다는 점에서 가장 중요하다고 생각합니다.

프로덕트에서 고객들이 가장 많이 사용하는 기능은 파일 번역입니다. 법률 도메인의 품질 측면에서 큰 강점을 갖고 있어서 변호사를 포함한 다양한 고객들이 구독형 요금제를 이용하고 있습니다. 하지만 서버와 Translation worker 등의 여러 이슈로 번역이 실패하는 경우가 종종 발생했고, 이는 자연스럽게 고객 문의의 증가로 이어졌습니다.

문제는 이러한 오류가 발생할 때 고객 문의를 받기 이전에 미리 확인하고 예방할 수 있는 프로세스가 없다는 것이었습니다. QA 팀이 없는 것도 한몫했습니다. 고객 문의에 하나하나 대응하고 직접 재현해보며 수정하는 과정은 번거롭고 개발 생산성을 저해하는 요인이었습니다. 이러한 문제를 해결하기 위해 클라이언트에서 발생할 수 있는 오류를 사전에 발견하고 예방할 수 있도록 E2E 테스트를 도입하고 자동화하기로 결정했습니다.

Playwright vs Cypress

E2E 테스트를 위한 라이브러리로는 대표적으로 Playwright (opens in a new tab)Cypress (opens in a new tab)가 있습니다. 이 두 가지 중 어떤 라이브러리를 사용할지에 대해 고민했습니다. 제가 생각한 주요 기준은 다음과 같습니다.

Cypress의 장단점

장점

단점

Playwright의 장단점

장점

단점

최종 선택

우리 프로젝트에서는 다음과 같은 이유로 Playwright를 선택했습니다.

  1. 크로스 브라우저 테스트 필요성: 다양한 브라우저 환경의 고객들을 대상으로 하기 때문에 Safari 포함 모든 주요 브라우저에서의 테스트가 필수였음
  2. CI 최적화: GitHub Actions에서 빠른 테스트 실행을 위해 병렬 테스트 기본 지원이 중요했음
  3. 속도: 브라우저 환경이 필수인 Cypress와 비교했을 때 Headless 기반인 Playwright의 속도가 더 빠름

특히 병렬 테스트 지원 측면에서 Playwright는 playwright.config.tsworkers 설정만으로 간단하게 병렬 테스트를 구성할 수 있었습니다.

playwright.config.ts

import { defineConfig } from "@playwright/test"
 
export default defineConfig({
  // Opt out of parallel tests on CI.
  workers: process.env.CI ? 2 : undefined,
  // ...
})

테스트 시나리오 작성

E2E 테스트의 시나리오는 기획서를 기반으로 작성했습니다.

  1. 테스트별로 다른 언어쌍, 도메인 기반 실행 (법률, 특허, 일반 번역)
  2. .docx.pdf 파일 업로드, presigned URL API 호출 및 S3 업로드 로직 확인
  3. 파일 업로드 완료 시 UI 확인
  4. 번역하기 버튼 클릭 후 SSE를 통한 실시간 번역 상태 확인
  5. 번역 완료 후 완료 파일 다운로드/삭제 기능 확인

이러한 접근 방식은 기획서의 변경사항을 빠르게 파악하고 테스트 코드에 반영할 수 있다는 장점이 있습니다.

Mocking과 실제 API를 조합한 테스트 코드 작성

Mock API를 사용한다는 것은 실제 백엔드 서버와 통신하는 것이 아니라, API가 무조건 특정 응답을 줄 것이라고 가정하고 작업하는 것입니다. 모든 테스트 상황이 실제와 동일할 때 E2E 테스트가 진정한 의미를 갖는다고 생각합니다. 특히 번역처럼 고객들이 실시간으로 사용하는 기능들은 실제 고객 환경과 동일한 조건에서 테스트하는 것이 중요하기 때문에 Mocking만으로는 원하는 결과를 얻기 어렵습니다.

하지만 다음과 같은 기능들은 Mock API를 사용하는 것이 더 빠르고 적절한 테스트 방법이라고 판단했습니다.

이 과정에서 로그인과 같은 반복적인 사전 작업들은 global setup (opens in a new tab) 설정으로 일원화했습니다. 그 외에도 테스트 코드를 작성하면서 다음과 같은 규칙을 정했습니다.

테스트 코드를 작성하면서 비즈니스 로직에 대해 더 깊게 이해할 수 있었습니다.

GitHub Actions를 통한 테스트 자동화, Slackbot 연동

테스트 자동화는 GitHub Actions를 통해 진행했습니다. 다양한 브라우저 환경에서의 동작을 검증하기 위해 matrix 기능을 활용하여 Chromium, Firefox, WebKit(iOS Safari) 세 브라우저 환경에서 병렬 테스트를 진행했습니다. 번역 데이터를 기반으로 고객의 실제 사용 패턴과 유사한 테스트 환경을 구성하고, GitHub Actions의 cron을 활용하여 특정 시간마다 테스트를 실행하도록 자동화했습니다.

Slackbot

또한 테스트가 실패했을 때 실패한 Job의 링크를 Slackbot을 통해 전송하여 번역 오류에 즉각 대응할 수 있는 체계를 구축했습니다.

Slackbot

도입 결과

정량적 성과

E2E 테스트 도입 후 다음과 같은 정량적 성과가 나타났습니다.

버그 탐지 및 해결

테스트를 작성하면서 아래와 같이 여러 중요한 버그들을 발견했고, 지속적으로 발견되는 새로운 버그들도 함께 수정하고 있습니다.

백엔드 영역

NLP 영역

마치며

백엔드와 비교했을 때 프론트엔드 영역에서는 특히 일이 많고 바쁠수록 테스팅의 중요성이 간과되는 경우가 많습니다. 당연히 기능 자체가 있어야 그 기능에 대한 테스트도 있는 것이기 때문에 바쁜 조직에서 테스트 코드는 사치처럼 느껴질 수 있습니다.

하지만 테스트 코드가 있고, 그 테스트 코드가 오류를 발견하고 수정하는 과정을 반복할 때 비로소 프로덕트의 완성도가 높아진다고 생각합니다. 또한 테스트 코드를 체계적으로 작성해두면 버그 발견부터 수정, 배포까지의 전체 프로세스가 단축되어 개발 생산성을 크게 개선할 수 있습니다.

특히 E2E 테스트는 단순히 버그를 잡는 것을 넘어서, 팀 전체의 생산성을 높이는 도구로도 활용할 수 있다는 것을 경험했습니다. 초기 구축 비용은 있지만, 장기적으로 보면 그 투자 대비 효과는 충분히 가치 있다고 확신합니다.

© Sangmin Park .