Engineering
QA팀의 장애 감지 자동화 플랫폼의 성장 스토리
2025년 12월 8일
원문에서 보기 ↗지속 가능한 자동화 플랫폼을 위한 아키텍처 설계와 고민들

이 이미지는 생성형 AI를 활용해 제작하였습니다.
시작하며: 놀유니버스 QA팀은 어떤 자동화를 하고 있을까요?
안녕하세요! 놀유니버스에서 QA를 담당하고 있는 랄프(하승우)라고 합니다. 반갑습니다!
들어가기에 앞서 놀유니버스 QA에서는 ‘품질’을 지키기 위해 어떤 자동화 플랫폼을 구축, 운영하고 있는지 소개부터 드리려고 합니다.
현재 운영 중인 자동화 영역은 크게 세 가지로 소개해 드릴 수 있을 것 같아요.
[NO. 1] UI 자동화 (라이브 장애 조기 감지)
가장 먼저 소개할 것은 실제 고객이 사용하는 라이브 환경을 지키는 UI 자동화입니다.
이 플랫폼은 24시간 365일 돌아가며, 사용자가 장애를 겪기 전 아주 작은 이상 징후라도 먼저 발견하여 알리는 ‘조기 경보 시스템(Early Warning System)’ 역할을 합니다. 실제로 다수의 장애를 조기에 감지하여 리포팅하고 있으며, 단순 오류 감지뿐만 아니라 자동 재시도 및 슬랙 알림까지 연결된 원스톱 대응 체계를 갖추고 있습니다.
이번 포스팅의 핵심 주제이기도 하죠!

UI Automation Incident Cycle

UI Automation Fail Alert
[NO. 2] API 자동화 (Bug Prevention: 배포 전 결함 차단)
두 번째는 라이브 배포 전 단계에서 결함을 사전에 막아내는 ‘Bug Prevention(버그 예방)’ 활동입니다.
QA환경에 신규 빌드가 배포될 때마다 CI/CD 파이프라인에 연결된 Postman(JavaScript) 기반의 API 자동화가 즉시 동작합니다. 이 자동화는 새로운 코드가 기존 핵심 기능(숙소, 레저, 항공 등)에 어떤 영향을 미칠지 분석하고, 주요 사용자 경로(User Path)를 검증하여 QA 환경의 안정성을 사전에 확보합니다. 라이브 서비스 감시가 ‘실전 대응’이라면, API 자동화는 ‘사전 예방’에 가깝습니다.
관련 내용은 Mr. 그루트가 작성해준 기술 블로그에 자세히 설명되어 있습니다. 꼭 한번 읽어보세요 :)
[NO. 3] 업무 효율화 자동화 (팀 생산성 극대화)
세 번째는 팀 내부의 생산성을 높여주는, QA팀의 든든한 ‘업무 서포터’ 역할을 하는 업무 효율화 자동화입니다.
업무 효율화 자동화는 Python과 Jenkins를 기반으로, QA 엔지니어들이 반복적인 수작업에서 벗어나 더 가치 있는 일에 집중할 수 있도록 돕는 시스템을 구축했습니다. 예를 들어, 매일 처리해야 하는 프로젝트 티켓 관리, 테스트에 필요한 대량의 데이터 생성, 복잡한 현황 대시보드 리포팅, 그리고 사람이 놓칠 수 있는 ‘업무 누락 영역 알림’ 같은 일들을 이 시스템이 대신 처리해 줍니다. 가장 중요한 것은 ‘편의성’과 ‘보안’입니다. 모든 자동화 스크립트는 Github으로 관리되며, 각각의 자동화 Job들은 젠킨스(Jenkins)에서 중앙 관리되어 QA 작업자 누구나 필요할 때 언제든지 파라미터만 입력해 편하게 실행할 수 있습니다. 또한, Jira나 DB 접근처럼 계정 정보가 필요한 민감한 작업들은 모두 젠킨스의 Credential 기능을 통해 철저하게 암호화하고 관리하여, 보안 걱정 없이 안전하게 자동화의 이점을 누릴 수 있도록 구성했습니다.
자 그럼, 앞서 소개한 세 가지 자동화 중에서 이 포스팅의 주인공인 [NO. 1] UI 자동화 플랫폼이 겪었던 파란만장한 성장 스토리를 공유해볼까 합니다.
놀유니버스 QA팀의 UI 자동화 플랫폼 여정, 시작해볼까요?
혹시… 200개가 넘는 젠킨스(Jenkins) Job을 하나하나 수동으로 수정해본 적 있으신가요? 😭
오늘은 UI 자동화 플랫폼이 어떻게 그 고통스러운 Phase 0(개별 관리)부터, Phase 1(Selenium) 시대를 거쳐, Phase 2(Playwright)로 진화하게 되었는지, 그야말로 ‘파란만장했던’ 여정과 기술적 고민들을 솔직하게 공유해볼까 합니다. (지금 생각해보면 창피한 부분도 있었네요..^^;)
자, 그럼 QA팀의 UI 자동화 성장 스토리 속으로 한번 빠져볼까요?
1. Phase 0: 고통에서 시작되다 (플랫폼의 필요성)
모든 것은 ‘이대로는 안 된다’는 절박함에서 시작됐어요. 플랫폼이 존재하기 이전(P0), QA팀의 UI 자동화 환경은 소수의 전문가에게 의존했습니다. Robot Framework라는 도구를 이용하여 하나의 테스트 시나리오가 곧 하나의 개별 프로젝트로 관리되었고, 담당자별로 파일을 따로 관리하고 있었죠… 이는 자동화 코드를 직접 작성하고 유지보수할 수 있는 특정 시니어 QA 엔지니어에게만 업무가 집중되는 심각한 ‘병목 현상’을 의미했습니다.
더 큰 문제는 ‘유지보수 재앙’이었습니다. 예를 들어, 서비스 전체에서 공통으로 사용하는 로그인 버튼의 ID가 변경되면?
네… 그 버튼을 사용하는 모든 개별 프로젝트를 모두 열어 코드를 수정해야 했습니다. 코드 낭비와 리소스 낭비가 엄청났죠.
서비스는 빠르게 성장하는데, 품질 검증의 속도가 발목을 잡는 상황이었습니다.
이 ‘유지보수 지옥’과 ‘높은 기술 허들’을 동시에 해결할 무언가가 절실히 필요했습니다.

Robot Framework 로그 발췌 (2022년도)
2. Phase 1: ‘누구나 쉽게!’ 그리고 새로운 문제점
Phase 0의 문제를 해결하기 위한 Phase 1의 핵심 목표는 명확했습니다.
“QA 엔지니어가 코드가 아닌 ‘단순 설정’으로 자동화를 생성할 수 있게 자동화 플랫폼을 만들자!”
이 목표를 달성하기 위해 당시 가장 성숙한 도구였던 Selenium 과 CI/CD의 표준인 Jenkins 를 결합하여 24/7 수행될 수 있도록 AWS EC2 환경에 플랫폼을 구성했습니다. (기존 사용자 로컬에서 자동화를 수행하던 때보다 많이 발전했죠!)
여기서 잠깐! Selenium이 뭔가요?
Selenium 같은 도구를 'UI 자동화 프레임워크'라고 부릅니다.
웹페이지 화면을 사람 대신 조작해 기능이 시각적으로 제대로 보이고
정상적으로 동작하는지 확인하는 도구입니다.
이 도구(Framework) 핵심 역할은 크게 두 가지입니다.
행동 재현 (Action)
사용자가 브라우저에서 하는 클릭, 키보드 입력, 스크롤, 페이지 이동 같은
모든 행동을 코드로 짜인 ‘테스트 스크립트’ 를 수행하죠
결과 검증 (Verification)
단순히 동작만 하는 게 아니라, "로그인 후 ‘주문하기’라는 문구가 정확히 뜨는가?",
"결제 버튼이 활성화되었는가?"처럼 화면의 텍스트, 버튼, 이미지 등 모든 요소(Element)가
기대한 상태로 있는지 확인하고 검증합니다.
QA 엔지니어들은 더 이상 복잡한 코드를 다루지 않고, 미리 정의된 젠킨스 Job에 “어떤 페이지에서(URL_PARAMETER), 어떤 요소(SELECTOR_ATTRIBUTE)를 찾아, 어떤 텍스트(TARGET_TEXT_PARAMETER)가 있는지”와 같은 파이프라인 파라미터만 입력하면 되도록 설계했습니다. 이 부분이 자동화 플랫폼의 핵심입니다! 이 설계 부분은 아래 Phase 2에서 좀 더 자세하게 설명 드리겠습니다.
이 접근은 폭발적인 성공을 거두었습니다. 기술 장벽이 낮아지자 여러 QA 담당자들이 자동화 시나리오 구현에 참여할 수 있었고, 그 결과 200여 개가 넘는 핵심 비즈니스 시나리오가 플랫폼 위에 빠르게 구축되었습니다. 이 자동화 자산들은 수많은 라이브 장애를 사용자보다 먼저 발견하는 든든한 ‘품질 안전망’ 역할을 했습니다.

Phase 1 UI Health Check Coverage, 총 219개의 검증 Job
Phase 1의 새로운 한계: 219개가 안겨준 고통
하지만 기쁨도 잠시, 219개라는 숫자는 곧 새로운 재앙이 되었습니다.
[고통 1: 너무 느린 속도와 불안정성]
Selenium 기반의 UI 헬스 체크는 안정적이었지만 너무 무거웠습니다. 219개의 시나리오가 한 사이클을 완료하는 데 오랜 시간이 소요되었습니다.
이것은 단순한 ‘느림’의 문제를 넘어, 새로운 기능과 시나리오가 추가될 때마다 전체 UI 헬스 체크에 필요한 총 구동 시간이 비례하여 증가하는 ‘확장성’의 문제였습니다. 시나리오 수가 늘어남에 따라 개별 테스트 잡(Job) 1개당 수행 시간이 계속 길어졌고, 이로 인해 전체 테스트 사이클이 무거워지는 것이 큰 부담이었습니다.
게다가 Selenium은 명시적 대기(wait) 처리에 의존하기 때문에, DOM 렌더링 타이밍이나 네트워크 상태에 따라 간헐적으로 테스트가 실패하는 ‘Flaky’ 현상(불규칙적 실패)이 높게 나타나는 점도 고질적인 문제였습니다.
[고통 2: 끔찍한 운영 효율]
이게 진짜 고통이었습니다. Phase 0의 ‘코드 중복’이 Phase 1에선 ‘쉘 스크립트 중복’으로 바뀌었을 뿐이었습니다. 219개의 시나리오를 실행하기 위해, 219개의 젠킨스 Job에 자동화 실행 로직이 담긴 Shell Script가 그대로 복제되어 있었습니다.
만약 이 공통 쉘 스크립트의 로직을 하나 수정해야 한다면? 네, 누군가가 219개의 젠킨스 Job 설정에 일일이 들어가 수동으로 ‘복붙’을 해야 했습니다. WebDriver 설치나 브라우저 버전 호환성 문제라도 터지면… 상상에 맡기겠습니다. 😭

Phase 1 — 시나리오 별 Job의 설정에서 Execute shell을 중복하여 관리
3. Phase 2: “왜 Playwright로의 전환이었나?”
Phase 1의 ‘속도’, ‘안정성’, ‘운영’이라는 세 가지 숙제를 해결하기 위해, 차세대 자동화 도구로 Playwright를 전략적으로 선택했습니다.
프레임워크를 변경하면서 속도와 안정을 챙김과 동시에, 그동안 비효율적이었던 요소들도 함께 개선하자로 목표를 잡았습니다.
그동안의 Phase 1의 도구, Selenium의 한계는 명확했습니다. 우리는 한정적인 자원 (EC2)에서 최고의 효율이 필요했거든요,
- 느린 속도 와 불안정성: 동기적 실행 방식과 명시적 대기 처리는 테스트 시간을 길게 만들고, 렌더링 타이밍 이슈로 불규칙한 실패(Flaky)를 유발했습니다.
- 복잡한 환경: WebDriver 설치와 브라우저 버전 호환성 문제는 CI/CD 파이프라인에서 반복적인 유지보수 비용을 발생시켰습니다.
- 비효율적 디버깅: 실패 원인을 찾기 위해 로그를 일일이 추적해야 했습니다.
Playwright는 이 모든 문제의 완벽한 해결책이었습니다.
- 압도적인 속도: 병렬 실행을 기본으로 지원합니다. workers 설정만으로 여러 테스트를 동시에 실행해, 전체 테스트 완료 시간을 크게 줄여줍니다.
- 강력한 안정성 : 요소 로딩을 자동 감지하는 ‘자동 대기(Auto-wait)’ 기능이 내장되어 Phase 1의 ‘Flaky’ 현상을 획기적으로 줄여줍니다.
- 간편한 환경: WebDriver가 필요 없고, Playwright 자체가 Chromium, WebKit, Firefox를 내장해 브라우저를 자동 관리합니다.
- 탁월한 디버깅: Trace Viewer, UI 모드, 자동 스크린샷/비디오 녹화 기능으로 실패 원인을 시각적으로 빠르게 파악할 수 있습니다.
Phase 2의 장애 감지 워크플로우: 그래서 어떻게 장애를 감지하나요?

UI Automation Job Execution Cycle
자, 그래서 Phase 1의 문제를 해결하기 위해 Playwright를 도입했는데, 실제 장애 감지 흐름은 어떻게 동작할까요?
장애가 발생해서 슬랙 알림이 오기까지의 여정을, 가상의 ‘상품 상세 화면’ 테스트가 성공/실패하는 과정을 예로 들어 따라가 보겠습니다.
(1) 시작: 젠킨스와 단 하나의 문지기
모든 것은 젠킨스 스케줄러에서 시작됩니다. “매 5분마다 ‘상품 상세 화면 UI 검증’ Job 실행” 명령이 내려지죠.
Phase 1과 달리, 200여개의 Job은 이제 단 하나의 쉘 스크립트(run_with_shared_env.sh)만 호출합니다.
이 스크립트는 공용 Python 가상 환경을 활성화시킨 뒤, 진짜 지휘자인 main.py를 호출합니다.
# --- [2] Python 가상 환경 활성화 ---
# Jenkins 서버에 미리 설정된 공용 Python 가상 환경의 경로
if [[ "$(uname)" == "Darwin" ]]; then
# macOS(iMac) 노드용 가상 환경 경로
SHARED_VENV_PATH="/var/jenkins_imac/shared-envs/playwright-venv"
else
# 기본 Linux(AWS) 노드용 가상 환경 경로
SHARED_VENV_PATH="/var/lib/jenkins/shared-envs/playwright-venv"
fi
if [ ! -f "$SHARED_VENV_PATH/bin/activate" ]; then
echo "에러: 공용 가상 환경을 찾을 수 없습니다. 경로: $SHARED_VENV_PATH"
exit 1
fi
# 위치한 디렉토리로 이동한 후 가상 환경을 활성화
cd "$(dirname "$0")"
source "$SHARED_VENV_PATH/bin/activate"
(run_with_shared_env.sh의 가상 환경 활성화 코드 일부)
(2) 성공: “구매하기” 버튼이 잘 있네! (PASS Case)
main.py는 젠킨스로부터 받은 파라미터(URL, 요소, 검증할 텍스트 등)를 챙겨, 실제 행동대장인 validator.py에게 검증을 지시합니다.
validator.py가 Playwright를 실행해 페이지에 접속하고, “구매하기” 버튼이 잘 있는지 확인합니다.
버튼이 잘 있다면? validator.py는 성공을 알리고, main.py는 exit(0)(성공)을 반환하며 조용히 검증을 마칩니다.
def run_validation(self):
"""
주어진 파라미터를 바탕으로 일반적인 UI 검증 수행
페이지 로딩, 텍스트 포함 여부 등을 검사
"""
print(f"웹페이지 검증 시작: {self.url}")
self.page.goto(self.url, wait_until="domcontentloaded")
# 파라미터 가져오기
locator = self._get_locator()
selector_method = self.params.get("selector_method", "CLASS_NAME").upper()
target_text = self.params.get("target_text")
# 페이지에 오류를 의미하는 '재시도' 버튼이 없는지 먼저 확인.
try:
expect(self.page.get_by_text("재시도")).not_to_be_visible(timeout=3000)
print("페이지 로딩 상태: 정상")
except AssertionError:
raise AssertionError("검증 실패: 페이지에 '재시도' 버튼이 표시되었습니다.")
# 본 검증 전, 사전에 필요한 클릭 단계 수행
self._perform_click_steps()
print("--------------------------------------------------")
print(f"검증 대상: '{self.params.get('value', '페이지 전체')}' | 목표 텍스트: '{target_text}'")
print("--------------------------------------------------")
try:
# 1단계/2단계로 나눠서 있으면 PASS하고 끝내버리기
try:
# 1단계: 스크롤 없이 짧은 시간(5초 이내) 내에 대상을 먼저 찾기
print("1단계 (스크롤 전)")
if target_text == "is_displayed" and selector_method != "ALL":
expect(locator).to_be_visible(timeout=5000)
elif selector_method == "ALL":
expect(self.page.locator('body')).to_contain_text(target_text, timeout=5000)
else:
expect(locator).to_contain_text(target_text, timeout=5000)
print("결과 : 성공")
print(f"확인 내용 : '{target_text}' 발견")
except Exception:
# 2단계: 1단계 실패 시, 스크롤 후 명시적 대기를 하고 다시 대상 찾기
print("결과 : 실패 (5초 내 미발견)")
print("2단계 (스크롤 후 + 5초 대기)")
print("페이지 맨 아래로 스크롤...")
self.page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
print("스크롤 후 1초 명시적 대기...")
self.page.wait_for_timeout(1000)
# is_displayed 로직 시작
if target_text == "is_displayed" and selector_method != "ALL":
print("검증 모드: 요소 노출 여부 확인 (is_displayed)")
expect(locator).to_be_visible(timeout=30000)
elif selector_method == "ALL":
print("검증 모드: 페이지 전체 텍스트 확인 (ALL)")
expect(self.page.locator('body')).to_contain_text(target_text, timeout=30000)
else:
expect(locator).to_contain_text(target_text, timeout=30000)
print("\n==================== [ 테스트 성공 ] ====================")
print(f"최종 확인: '{target_text}' 확인 완료")
(validator.py 주요 검증 로직 일부)
(3) 실패: 앗! “구매하기” 버튼이 없다! (FAIL Case)
하지만 “구매하기” 버튼이 없다면? validator.py가 에러를 발생시키며 1차 실패 처리를 합니다. 이제부터 Phase 2의 진짜 실력이 나옵니다.
(4) 복구 ①: “빠른 재시도”로 일시적 오류 걸러내기
main.py는 이 에러를 즉각 포착하고, “일시적인 렌더링 문제일 수 있어!”라고 판단합니다.
브라우저를 닫지 않은 상태(Stateful)로 validator.py에게 “딱 2번만 더 시도해 봐!”라고 명령합니다.
(5) 복구 ②: “셀프 힐링”으로 UI 변경 감지하기
“재시도 2회”에도 계속 실패하면, main.py는 “좋아, 플랜 B다!”를 외치며 마지막으로 validator.heal_with_string_similarity()를 호출합니다.
이때 validator.py는 thefuzz 라이브러리 를 사용해 페이지의 모든 텍스트와 “구매하기”의 유사도를 검사합니다.
- Case A: 셀프 힐링 성공! “바로 구매하기”(유사도 85점) 버튼을 찾았습니다! main.py는 즉시 “셀프 힐링 성공!” 알림을 슬랙으로 보내고, exit(0)(성공) 코드를 반환합니다. (장애는 아니지만, UI 변경으로 힐링된 상태를 알려주는 거죠.)
- Case B: 셀프 힐링 최종 실패! 80점이 넘는 요소를 못 찾았습니다. main.py는 validator.take_screenshot()로 스크린샷을 찍고, exit(1)(실패) 코드를 반환합니다.
def heal_with_string_similarity(self) -> bool:
"""
기본 검증 실패 시, 문자열 유사도를 이용해 대안 요소를 찾아 복구 시도
페이지 내 여러 요소들의 텍스트와 원래 찾으려던 텍스트를 비교하여 가장 비슷한 요소 찾기
Returns:
bool: 힐링에 성공하면 True, 실패하면 False를 반환
"""
print("\n--- [셀프 힐링 실행] ---")
target_text = self.params.get("target_text")
if not target_text or target_text == "is_displayed":
print("힐링 스킵: 비교할 target_text가 없거나 'is_displayed' 모드입니다.")
return False
# 1. 후보 요소 그룹 찾기 (텍스트를 포함할 가능성이 높은 태그들로 범위 지정)
candidate_selectors = 'div, span, p, a, button, h1, h2, h3, h4, li'
try:
candidates = self.page.locator(candidate_selectors).all()
print(f"총 {len(candidates)}개의 후보 요소에서 유사 텍스트를 검색")
except Exception as e:
print(f"오류: 힐링을 위한 후보 요소를 찾는 데 실패 {e}")
return False
best_score = 0
best_match_element_text = None
# 2. 각 후보 요소를 순회하며 원래 텍스트와의 유사도(0~100점)를 계산
for element in candidates:
try:
element_text = (element.inner_text() or "").strip()
if element_text: # 텍스트가 있는 요소만 비교
score = fuzz.ratio(target_text, element_text)
if score > best_score:
best_score = score
best_match_element_text = element_text
except Exception:
continue # 요소가 사라지는 등 예외는 무시
# 3. 임계값 판단
similarity_threshold = 80 # 80점 이상일 때만 성공으로 간주
print(f"분석 완료. 가장 유사한 텍스트: '{best_match_element_text}' (유사도: {best_score}점)")
if best_score >= similarity_threshold:
print(f"힐링 성공: 유사도 기준({similarity_threshold}점)을 통과했습니다.")
return True
else:
print(f"힐링 실패: 유사도 기준({similarity_threshold}점)을 넘는 요소를 찾지 못했습니다.")
return False

셀프 힐링 thefuzz 로직 및 Slack Alert
thefuzz 라이브러리의 똑똑한 전처리
위 코드에서 사용된 thefuzz 라이브러리는 단순한 문자열 비교를 넘어,
저희가 기대한 '셀프 힐링' 성공률을 높여주는 핵심 기능이 있습니다.
내부적으로 ‘강력한 토큰(Token) 기반 전처리’를 수행한다는 점입니다.
위에 예시처럼, 기대 텍스트가 "위치 및 교통"이었는데,
UI가 "위치/교통"으로 변경되었다고 가정해 보겠습니다.
일반적인 알고리즘이라면 두 문자열이 다르다고 판단하겠지만,
thefuzz는 비교 과정에서 "및", "/" 같은 특수 기호나 불필요한 조사 등을 먼저 제거합니다.
그 후 정제된 "위치", "교통"이라는 핵심 토큰(Token)들만 비교하기 때문에,
이 두 문자열은 100점의 유사도를 기록하며 '동일한 텍스트'로 간주됩니다.
이런 디테일한 전처리 과정 덕분에, 개발 과정에서 발생하는 사소한 UI 텍스트 변경
(예: 기호 추가, 단순 오타)으로 인해 불필요한 장애 알림이 울리는 것을 방지하고
셀프 힐링된 내용만 알려주면서, '셀프 힐링'의 성공률을 크게 높일 수 있었습니다.
(6) 알림: “진짜 장애”만 골라서 전파
Case B로 인해 exit(1)(실패) 코드가 run_with_shared_env.sh로 전달됩니다.
드디어 문지기가 움직일 때죠. “진짜 실패했군!”을 인지하고,
즉시 “ [홈 화면 UI 검증] 실패” 알림을 젠킨스 빌드 URL, 스크린샷, 오류 로그와 함께 슬랙으로 전송합니다.

실제 장애 상황의 Slack Alert
이 탄탄한 흐름 덕분에 ‘일시적 오류’는 걸러내고, ‘사소한 변경’은 감지하며, ‘진짜 장애’만 신속하게 알림 받을 수 있게 되었습니다.
Phase 2의 핵심: ‘누구나’ 만드는 파라미터 기반 자동화
Phase 1의 목표였던 “QA 엔지니어가 코드가 아닌 ‘단순 설정’으로 자동화를 생성” 하는 컨셉은 Phase 2에서 완성되었습니다. Phase 2 플랫폼의 핵심은 QA 엔지니어가 Playwright, Python 코드를 전혀 몰라도, 젠킨스(Jenkins)에 미리 정의된 ‘파라미터’ 양식만 채우면 강력한 UI 자동화를 즉시 생성할 수 있다는 점입니다.
QA 엔지니어 및 사용자는 젠킨스에서 새 Job을 생성(혹은 복사)한 뒤, Build with Parameters 설정에서 다음과 같은 ‘설정값’만 입력합니다.

QA 엔지니어가 할 일은 여기까지입니다. Build 버튼을 누르면 이 파라미터 값들이 Orchestrator(main.py) 로 전달되고, Worker(validator.py)가 이 값을 받아 실제 검증을 수행합니다.


Jenkins Job Parameters 및 Shell Script 예시
Phase 2 전환기의 기술적 고려사항
Phase 2로의 전환이 전반적인 속도와 안정성을 극적으로 개선했지만, Selenium 환경에서 Playwright 환경 으로 넘어오는 초기 단계에서 다음과 같은 새로운 유형의 이슈들이 발견되었습니다.
- Selenium 스크립트 마이그레이션 과정의 오류 : 서로 다른 대기 로직
가장 두드러진 문제였습니다. Selenium에서 잘 동작하던 스크립트를 거의 그대로 Playwright로 가져왔음에도, 특히 ‘클릭 후 페이지 이동’과 같이 화면 전환이 일어나는 영역에서 테스트 실패 케이스가 예상보다 많이 발생했습니다.
이는 Playwright의 버그라기보다는, 두 도구의 핵심 동작 로직인 ‘’대기’ 처리 로직이 다르기 때문이었습니다.
Selenium 은 명시적 대기(Explicit Wait)를 통해 “페이지 URL이 바뀔 때까지” 또는 “특정 요소가 나타날 때까지”를 스크립트가 직접 제어했지만, Playwright는 강력한 자동 대기(Auto-Waiting) 기능이 오히려 복잡한 SPA(Single Page Application) 환경의 화면 전환 완료 시점을 Selenium과 다르게 판단하여 실패처리 하는 경우가 있었습니다.
결국, Selenium 코드를 단순히 ‘번역’하는 것이 아니라, Playwright의 자동 대기 로직을 신뢰하되, 필요한 경우 page.waitForNavigation()나 page.waitForURL() 등 Playwright에 맞는 대기 방식을 명시적으로 추가하는 리팩토링 과정이 추가로 필요했습니다.

Phase 2 초기에 발견된 ‘대기 로직 이슈’ 실제 사례
4. 얻은 성과: 데이터로 증명하다
이 모든 Phase 2 아키텍처의 혁신은 ‘UI Health check의 수행시간’의 획기적인 단축으로 증명되었습니다.
말로만 하는 것보다 데이터로 보여드릴게요!

성공 Job 평균 수행 시간 비교 (Selenium Phase 1 vs Playwright Phase 2)
(실험 환경: 동일 사양의 EC2 t3.large 각각의 인스턴스에서 각 Job을 30회 반복 실행 한 결과의 평균값 이며, 소수점 이하는 절사했습니다.)
Playwright의 빠른 실행 엔진과 Phase 2의 효율적인 아키텍처 덕분에, 동일한 시나리오도 훨씬 빠르게 완료되었습니다.
성공 Job의 평균 수행 시간이 약 66% 이상 단축되었습니다.
이는 전체 200여 개의 시나리오가 한 사이클을 완료하는 시간이 극적으로 줄었음을 의미합니다.

실패 Job 평균 수행 시간 비교 (Selenium Phase 1 vs Playwright Phase 2)
(실험 환경: 동일 사양의 EC2 t3.large 각각의 인스턴스에서 각 Job을 30회 반복 실행 한 결과의 평균값 이며, 소수점 이하는 절사했습니다. 강제 실패를 위한 대상이 없는 요소로 설정하여 실험을 진행했습니다.)
더 중요한 것은 ‘실패’를 얼마나 빨리 감지하느냐입니다.
실제 장애가 발생했을 때, 실패를 인지하고 재시도 및 오류 확정 알림을 보내기까지의 평균 시간이 58% 이상 단축되었습니다.
이게 바로 Phase 2의 핵심 성과죠. 장애 상황을 더 빨리 공유하여 대응 시간을 확보할 수 있게 되었습니다.
이처럼 Phase 2 아키텍처는 데이터를 통해 Phase 1의 가장 큰 고통이었던 ‘느린 속도’와 ‘불안정성’이라는 문제를 성공적으로 해결했음을 증명했습니다.
단순히 빨라진 것을 넘어, 장애를 더 빨리 인지하고 대응할 수 있게 되어 ‘품질 안전망’으로서의 역할을 완벽하게 수행하게 된 것입니다.
이 모든 성과는 하루아침에 이루어진 것이 아니었습니다.
마치며: 더 넓은 사용자를 향해
Robot Framework 기반의 로컬 수동 관리 방식(Phase 0)에서 출발하여, AWS EC2와 젠킨스를 활용한 플랫폼(Phase 1)을 구축했고,
이제는 Playwright와 탄탄한 Phase 2 아키텍처를 통해 빠르고 안정적인 플랫폼에 도달했습니다.
이 여정을 통해 QA팀은 반복적인 장애 감지 업무에서 해방되어, 더 가치 있는 품질 전략을 수립하는 데 집중할 수 있게 되었습니다.
하지만 우리의 여정은 여기서 멈추지 않습니다. 현재 Phase 2 아키텍처의 커버리지 증대 및 지속 가능한 자동화 환경 구축과 함께 누구나 쉽게 사용할 수 있도록 ‘Starter kit 웹 포털’ 형태로 개발하고 있습니다.
- 커버리지 확장과 지속 가능한 자동화 환경 구축
현재 플랫폼의 강력한 성능을 바탕으로, 놀티켓(인터파크 티켓) 영역 및 파트너센터, 레저어드민 등 백오피스까지 UI 자동화 시나리오를 확장하여 더 촘촘한 품질 검증 망을 구축하고자 합니다.
하지만 단순한 시나리오의 양적 확장은 UI 변경 시 유지보수 비용의 급증으로 이어질 수 있습니다. 자동화의 진정한 가치는 지속 가능성에 있으며, 이를 위해서는 UI가 변경되어도 테스트가 쉽게 실패하지 않는 견고함이 필수적입니다.
이 문제의 근본적인 해결책은 ‘data-testid’와 같은 테스트 전용 식별자(Selector)를 표준화하는 것입니다. 문제는 이 표준화 작업이 QA팀 내부의 노력만으로는 불가능하다는 점입니다. 식별자의 표준화는 프론트엔드 개발자가 실제 애플리케이션 소스 코드에 직접 반영해야 하며, 디자인 시스템과도 연계되어야 합니다.
따라서, 향후 핵심 미션은 개발/디자인 조직과의 긴밀한 협업 체계를 구축하고, 테스트 식별자 표준을 수립하며, 개발 프로세스의 일부로 성공적으로 정착시키는 것을 목표로, UI 변경에도 흔들리지 않는 견고한 자동화 환경을 확보하고, 레거시 지면까지 안정적으로 커버하는 진정한 품질 검증 망을 완성하는 것이 최종 목표입니다.
- 비 R&D 직군을 위한 사용 환경 제공:
Phase 1의 목표가 ‘QA팀 내부의 사용 확대’였다면, V3의 목표는 ‘전사 구성원의 사용 확대 ’입니다. 젠킨스 파라미터에 익숙하지 않은 비 R&D 동료들(타 부서, 기획자, 운영자 등)도 스스로 필요한 웹페이지의 UI 자동화를 생성하고 활용할 수 있도록, 웹페이지 형식의 ‘UI 자동화 생성기’를 제공하는 것이 최종 목표입니다.

Mr.그루트와 개발중인 Low-code UI 자동화 Job 생성 웹페이지
기술을 통해 동료들이 반복적인 일에서 벗어나 더 가치 있는 일에 집중하도록 돕는 것, 그것이 놀유니버스 QA팀이 플랫폼을 고민하고 계속 발전시키는 이유입니다.
Phase 0의 고통에서 시작해 Phase 2의 안정화를 이루기까지의 여정은 ‘더 나은 품질’을 향한 QA팀의 치열한 고민이었습니다. 앞으로도 놀유니버스 QA팀은 자동화 플랫폼을 ‘누구나’ 쉽게 사용하는 도구로 발전시켜, 놀유니버스의 품질을 지키는 든든한 조력자가 되겠습니다.
긴 글 읽어주셔서 감사합니다.

누구나 마음 편히 놀 수 있는 세상을 함께 만들어갈 동료를 찾고 있어요 :)
