우리는 항상 지켜보고 있다 👀
frontend , vrt , slack , backstopJS
안녕하세요. Portal개발그룹 서비스개발담당 FE개발1팀의 진창엽입니다.
신입 입사 후 처음으로 받았던 작은 개발 과제였던 “프론트엔드에서의 장애 징후 감지” 라는 주제로 프로젝트를 진행해 본 이야기를 공유해보려고 합니다.
아마도 제목에서 짐작하셨겠지만, 웹페이지를 항상 ‘지켜보는 서비스’를 만드는 것으로 고민에 대한 답을 내 보았습니다. 이 글은 단계별로 가장 타당하다고 생각한 기술을 적용하게 된 고민은 무엇인지, 프로젝트를 진행하며 배운 점은 또 무엇인지에 관한 회고록입니다.
왜 웹페이지를 지켜보게 되었나요?
2020년 1월에 입사한 서비스개발담당의 신입 사원들에게는 과제가 주어졌었습니다. 바로 11번가에 기여할 수 있는 이전에 없던 새로운 서비스를 구현해 보는 것이었는데요, 주제는 자유롭게 선택이 가능했습니다. 주제 중 가장 어려운 주제가 자유 주제이니만큼, 어떤 분야를 선택하면 좋을지 고민하던 차에 작은 장애가 있었습니다.
갑작스레 이미지 아이콘들이 깨지는 현상이 생기는 장애였습니다. 여러 팀이 원인 파악과 해결에 분주한 상황이었는데요, 이런 상황을 방지해 볼 방법은 없을까 하는 데서 출발해 선정하게 된 주제가 Frontend에서의 장애 징후 감지 였습니다.
Frontend 측면에서 장애 징후를 감지하기 위해서 일반적인 장애 유형을 분석해본 결과, 화면 단에서 미리 잡아낼 수 있는 장애 징후들은 CDN 연결 실패로 인한 이미지 깨짐, 잘못된 스타일시트(CSS) 배포로 인한 백화 현상 등이 있었습니다. 즉, 예기치 못한 (원치 않은) 화면의 변화로 요약해 볼 수 있었는데요, 이런 유형의 문제는 사람이 계속 보고 있지 않는다면 잡아내기가 어렵겠다는 생각이 들어 좌절하던 순간에 문득, 굳이 사람이 꼭 봐야 할까? 라는 의문이 들게 되었습니다. 이내 이런 변화를 모니터링하는데 도움을 주는 서비스를 만들면 유용하겠다는 결론을 내리고 구체적인 구현 방안을 찾게 되었습니다.
지켜보기 위한 준비
문제를 인식하고 이를 어떤 방향으로 풀어볼지 정했으니 실제로 웹페이지를 지켜보는 서비스를 구현해 낼 방법에 대해 고민이 필요했습니다. 가장 먼저 어떤 환경을 모니터링 할지를 정해야 했습니다. UI 변경이 실제 고객들에게 노출되기 전에 마지막으로 반영되는 단계에 구상해본 서비스를 적용하고 싶었는데, 이를 위해서는 11번가 모바일 웹 개발 환경에 대한 이해가 먼저 필요했습니다.
간략히 이야기하면, 11번가 Frontend는 주로 HTML + CSS 작업을 통해 디자인에 맞도록 개발된 마크업(Markup) 산출물을 담당하는 UI개발과, 그 산출물을 실데이터와 연결하고, 모던 웹 프레임워크(Modern Web Framework) 형태로 만들어내는 JS개발로 분리되어 각각 관리되고 있습니다. 마찬가지로 CSS와 JS를 각각 분리하여 배포하는 방식으로 모바일 웹 개발 환경이 꾸려져 있습니다. 일반적인 배포 프로세스와 마찬가지로 UI 산출물이 배포되고 URL 로 접근 가능한 서버를 상용/개발 등으로 나누어 운영 중입니다. 이 서버 중 UI 변경이 마지막으로 반영되는 단계의 서버를 타깃으로 모니터링 하도록 하기로 했습니다.
환경에 맞도록 무엇을 볼지 정했으니 어떻게 볼지 생각해 볼 차례입니다. 다행히 시각적 요소들의 변경을 감지하는 테스트 기법에는 이미 널리 사용되고 있는 Visual Regression Test 가 있었습니다.
Visual Regression Test 가 무엇인가요?
Visual Regression Test (이하 VRT) 는 단어 뜻 그대로 나타내자면 ‘시각적 회귀 테스트’입니다. 회귀 테스트란 최근의 코드 변경이 기존 기능에 나쁜 영향을 미치지 않았음을 확인하는 소프트웨어 테스트의 한 유형인데요, 코드 변경에 따른 변화를 시각적으로 볼 수 있도록 구성한 테스트가 시각적 회귀 테스트입니다.
그렇다면 왜 이런 기법을 Frontend 테스트에 사용해야 할까요? 많은 경우에 테스트를 진행할 때에 입력값과 출력값을 검증하곤 합니다. 하지만 Frontend에서는 입력은 사용자의 동작, 그에 따른 출력은 화면의 변화로 나타나기 때문에 코드로 검증하기가 힘든 경우가 많습니다. 특정 시점의 결과물 코드의 스냅샷(snapshot)을 찍어두고 코드의 변경이 생겼을 때 그 변화를 비교하는 방식의 테스트를 통해 코드만으로 검증하는 방식이 있긴 하지만, 아래처럼 코드상 문제가 없지만 스타일상의 오류가 발생한 같은 케이스는 잡아내기 어렵다는 문제가 있습니다.
시각적인 테스트는 이와 다르게 변경이 생긴 경우 그 변화를 한 눈에 파악할 수 있습니다.
VRT 도구들 과 BackstopJS
VRT 를 시행할 수 있는 툴 들에는 유료 서비스에서부터 오픈소스 툴까지 다양한 도구들이 있습니다. 프로젝트를 위해 검토해본 도구들을 정리해보면, 유료 서비스와 오픈소스로 나뉘고, 각각의 뚜렷한 특징들을 가지고 있었습니다.
유료 서비스
유료 서비스들은 완성도 높고, 훌륭한 기능들을 갖추고 있었습니다. 참고하여 프로젝트 개발에 영감을 받을 수는 있었지만, 이 제품들을 소스 레벨에서 프로젝트에 녹여내기는 불가능했습니다. 대표적으로 다음과 같은 서비스들이 있습니다.
- Percy : 가장 유명한 시각적 테스트 도구 중 하나입니다. 대시보드를 갖추고 있습니다. 그러나 프로덕트 형태여서 원하는 식으로 programmatic 한 접근이 어렵습니다.
- Chromatic : Storybook의 maintainer 들에 의해 만들어졌습니다. Percy와 유사하게 팀 단위로 협업이 가능한 대시보드가 있습니다.
- Applitools : AI를 통해 의미 있는 변화를 감지하는 시각적 테스트 플랫폼입니다.
오픈소스
수많은 유료 서비스들과 같이 오픈소스들도 많이 존재합니다. 프로젝트에 채택한 BackstopJS 도 오픈소스입니다.
- BackstopJS : 크롬 headless 브라우저를 통해 스크린샷을 캡처하고, puppeteer를 활용해 다양한 상황을 스크립트로 설정해 줄 수 있습니다. 이전 테스트 스크린샷을 다음 테스트의 기준으로 삼을 수 있습니다.
- Cypress Visual Regression : 프론트엔드 테스트 도구 Cypress의 VRT 모듈입니다.
이처럼 많은 도구 중 최종적으로 선택하게 된 것은 BackstopJS 였습니다. 가장 간단하고, 유연하게 프로젝트에 활용 가능했을 뿐 아니라 특정 요소를 무시하고 테스트하는 기능, 가상 브라우저를 스크립트로 제어해 원하는 시점의 (가령 사용자가 쿠폰 다운로드 팝업을 띄운 경우) 스크린샷을 찍을 수 있도록 하는 기능 등 많은 편의를 제공했습니다.
ART 프로젝트
그동안 조사하고 검토했던 지식을 바탕으로 프로젝트를 설계해 볼 시간입니다. 테스트 데이터가 의미를 가지기 위해서는 실시간으로 상황을 알려줄 수 있어야 합니다. 알림 받은 담당자들은 어떤 영역에서 어떤 변경이 일어났는지 쉽게 확인이 가능해야 합니다. 확인이 끝난 이후 이번의 테스트 결과가 다음 테스트에 반영될 수 있어야 합니다. 분석한 요구사항으로 팀원들과 논의를 거쳐 프로젝트 구조를 잡아 나갔습니다.
그래서 탄생한 프로젝트는..
ART입니다. ART는 Auto (Visual) Regression Test의 약자로 Visual Regression Test를 자동으로 수행한다는 의미입니다.
주요 기능
- 실시간으로 Slack 알림을 통해 원하는 페이지의 이상을 파악할 수 있습니다.
- Slack 채널에서 어느 페이지에 어떤 변경 점이 생겼는지 확인하고, 대시보드에 접근할 수 있습니다.
- 대시보드는 사내 인증 서비스와 연동되어 구성원이라면 누구나 사용 할 수 있습니다.
- 최근 감지된 페이지의 변경 점을 전후로 나누어 확인 가능합니다.
- 페이지의 변경 점이 의도된 경우, 이번 스크린샷을 다음 테스트의 기준으로 삼습니다.
- 페이지의 변경 점이 예기치 않은 경우, 이상 상황을 파악해 수정합니다. 이 경우 테스트의 기준은 변하지 않습니다.
- 페이지가 그동안 변경된 내용을 확인할 수 있습니다.
Slack과 연동한 실시간 알림 시스템
ART의 사용 시나리오는 간단합니다. 페이지를 지켜보고 있다가 무언가 변화가 생기면 알려주는 것입니다.
개발자가 PR을 올리거나, 특정 시간이 되면 주기적으로 테스트를 진행해 Slack Webhook을 통해 채널에 알림을 줍니다. 테스트를 담당하고 Slack 알림을 전송하는 서버는 Test Instance 라고 부릅니다. 이 서버를 통해 실시간으로 이슈가 있음을 파악 가능합니다. 테스트 인스턴스는 express 서버 기반으로, 진행한 테스트에서 생성된 결과물을 스크린샷과 메타 데이터 둘로 나눕니다. 스크린샷은 S3에, 메타 데이터는 API 서버에 전달해 AWS DB (RDS) 에 저장하도록 합니다.
전송된 알림은 아래와 같이 대시보드로 이동 가능한 링크와 변경된 이미지를 포함합니다.
테스트를 관리하고 페이지 상태를 확인하는 대시보드
Slack에서 알림을 받아 이슈를 인지한 담당자는 대시보드를 통해 페이지 상태를 관리할 수 있습니다. 대시보드는 ART API 서버와 Vue 로 작업해 빌드한 대시보드 클라이언트로 구성되어 있습니다. API 서버는 NestJS 기반이며 인증 및 DB와의 연동을 담당합니다. 테스트 인스턴스에서 전달한 테스트의 메타데이터를 DB에 저장하고, 대시보드 클라이언트에서 요청하는 테스트 정보를 조회해 API 형태로 제공합니다.
대시보드에서는 변경된 페이지를 더 자세히 확인해 볼 수 있습니다. 만약 의도된 변경이라면 승인하기를 통해 다음 테스트를 이 스크린샷을 기준으로 삼도록 할 수 있습니다.
페이지 변경 내역 확인
프로젝트를 진행하며 쌓여가는 페이지의 스크린샷들을 활용하여 각 페이지의 History를 확인 할 수 있습니다.
겪은 이슈?
출발했던 아이디어는 간단했지만, 프로젝트를 진행하는 작업이 쉽지만은 않았습니다. 테스트 인스턴스, API 서버, 대시보드 클라이언트로 구성된 만큼 각각의 영역에서 다양한 경험을 해 볼 기회가 있었는데요, 여기서는 테스트 인스턴스에서 VRT를 진행할 때 겪었던 일들을 다뤄보겠습니다. 예상 가능했던 문제, 의외로 숨어있던 문제 등 다양한 이슈들이 있었습니다.
동적 영역 제거
어려울 것이라고 예측했던 부분은 페이지에서 동적인 부분을 제외한 후 테스트 해야 한다는 점이었습니다. 동적인 부분이란 렌더링 시마다 랜덤으로 나타나는 배너, 혹은 시간이 흐르며 자동으로 슬라이드가 넘어가는 인터렉션을 가진 영역 (ex. 실시간 쇼핑 검색어) 및 서버 응답에 따라 다르게 리스팅되는 상품 영역 등을 의미합니다. 사실 브라우저 대부분의 영역은 동적으로 변화합니다. 이런 영역들을 적절히 제거해주고 숨겨주지 않으면 의미 없는 변화에도 테스트 및 알림이 발생하는 문제가 있습니다.
이를 해결하는 방법에는 해당하는 요소들의 CSS 선택자(Selector)를 찾아 숨기거나, 제거하는 방법이 있습니다. 아래는 테스트를 시행하기 위해 사용하는 설정 파일의 일부입니다. hideSelectors
는 그 선택자에 해당하는 요소에 visibility: hidden
속성을 부여한 효과와 동일하게 레이아웃의 변화 없이 영역을 숨겨줍니다. removeSelectors
는 display: none
을 해당하는 요소에 부여한 것과 동일하게 작용해 영역을 제거해줍니다.
{
"name": "ui-home",
"hideSelectors": [[
".c-gnb__search-input"
]],
"removeSelectors": [[
".c-layer-app",
".dim"
]]
}
테스트의 신뢰도 문제
무언가 개발하다 보면 직접 경험해 보기 전엔 파악하기 힘든 문제들이 있습니다. 테스트 신뢰도 문제도 이번 프로젝트에서 그런 부류의 문제였습니다. 신뢰도를 떨어뜨리는 데는 크게 두 가지 현상이 있었습니다. 일부 리소스의 로딩이 끝나지 않았을 때 스크린샷을 찍는 경우와 특정한 요소가 렌더링 될 때까지 대기하도록 하는 경우에 발생할 수 있는 Timeout 문제입니다.
발생한 문제들을 분석해보니, 하나의 서버에서 한꺼번에 많은 브라우저를 띄우고 네트워크 요청을 보내 급격히 느려진 네트워크 환경이 그 원인이었습니다. 본질적인 문제 해결은 테스트 인스턴스를 Scale out 하기 쉽도록 분산 환경을 구축해 여러 대의 서버를 운영하는 것이지만, 주어진 시간 내에 해결하기는 어렵겠다는 판단을 내려 다른 방법을 찾게 되었습니다.
좋은 방법이 없을까 고민하다 네트워크에서 패킷의 무한 순환을 방지하는 Time to Live (TTL) 개념에서 실마리를 얻었습니다. 테스트에 TTL 값을 3으로 지정하고, 테스트 1회 수행 후 문제가 발생한 경우엔 TTL 값을 감소시키고 테스트를 재시도합니다. TTL 값이 0 이 되면 실제로 에러 상황으로 판단해 알림을 보내도록 했습니다. 이렇게 하면 문제가 발생한 테스트 케이스만을 개별적으로 다시 검증할 수 있어 개선된 네트워크 환경을 갖게 되고 정확성을 높일 수 있었습니다.
TTL 방식 테스트를 적용하기 이전에 결과에 이상이 있는 빈도는 리소스 로딩의 경우 10~15%, Timeout 케이스는 약 10% 정도였습니다. 결과를 표로 정리해보면 아래와 같습니다.
적용 전 | 적용 후 | |
---|---|---|
에러 케이스별 발생률 | Timeout : 10% Loading(Best): 10% Loading(Worst): 15% |
Timeout : 0.1% Loading(Best): 0.1% Loading(Worst): 0.3375% |
평균 값 | 약 11.6% | 약 0.179% |
마치며
이렇게 이미지가 잠깐 안 나온다는 해프닝에서 출발해 ART 프로젝트가 완성되었습니다. 정리하면 Visual Regression Test 기법을 활용한 실시간 모니터링 시스템 구축이었습니다. Slack Webhook을 활용한 알림 기능이 추가되었다는 점, 다양한 기능을 가지고 있고 앞으로 발전 시켜 나갈 Dashboard의 기반을 다졌다는 점이 특징이라고 볼 수 있겠습니다. 프로젝트를 진행하면서 11번가 고객에게 불편을 끼칠 수 있는 상황을 더 정확하고 빠르게 감지할 수 있도록 발전시켜 나가도록 하겠습니다. 비슷한 아이디어로 프로젝트 설계를 고민 중이신 분들께 도움이 될 수 있다면 더 기쁠 것 같습니다. 긴 글 읽어 주셔서 감사합니다. 🙇
이미지 출처
- Visual Regression Test: NELKOSUNSET, 2018.12.12
- 코드 만으로는 불충분한 경우: LINE DevDay, 2020.11.26
- BackstopJS 의 마스코트 안경 원숭이 : garris/BackstopJS