매출 손실을 줄여주는 외부링크 관제 Bot, 'URL Checker' 개발기

안녕하세요, 뱅크샐러드 Engineering Foundation의 Front End Team 소속 Web Engineer 이소영입니다.

뱅크샐러드는 사용자의 데이터를 기반으로 가장 최적의 금융 상품을 추천하기 위해 여러 금융사와 제휴하여 다양한 상품을 확보해나가고 있습니다.

웹팀은 추천된 금융 상품에 대한 자세한 내용을 바로 확인하고 가입할 수 있도록 제휴사의 상품 상세 페이지로 사용자를 안내합니다. 제휴사의 상품 상세 화면을 어떻게 관리하고 사용자에게 안내할지 많은 고민 끝에, 제휴사로부터 상품 상세 페이지에 대한 URL을 전달받아 직접 관리하기로 했습니다. 관리되는 화면들은 사용자가 동일한 서비스를 이용하고 있다고 느껴지도록 뱅크샐러드 앱 내에서 Webview로 보여주고 있습니다.

제휴사 페이지 장애는 곧 뱅크샐러드의 장애

기존에는 제휴사의 상품 상세 페이지에서 어떤 문제가 발생하더라도 뱅크샐러드 팀이 즉시 알 방법이 없었습니다. 연결된 상품 상세페이지에서 알 수 없는 장애가 발생하거나 전달받은 URL이 변경되어 찾을 수 없는 페이지가 노출될 경우, 실제로는 제휴사 페이지라 하더라도 사용자 입장에서는 뱅크샐러드 서비스의 장애로 인식합니다. 뱅크샐러드 팀은 해당 사이트의 장애 여부를 즉시 알 수 없었고 이에 적절히 대응할 수 없었습니다. 따라서 장애를 예방하거나 빠르게 파악할 방법이 필요했습니다.

그리하여 제휴사 페이지의 장애 여부를 빠르게 파악하여 매출 손실을 막고 사용자 경험을 보호하는 외부 링크 관제 Bot, ‘URL Checker’ 프로젝트가 기획되었습니다.

문제 정의

URL 정보가 변경되면 제휴 상품의 상세 페이지를 확인할 수 없는 장애가 발생하는데 이 장애를 대처하는 일에는 크게 두 가지의 어려움이 있습니다.

  • 제휴사 페이지에 직접 접근하기 전까지는 장애 상황을 인지할 수 없음.
  • 따라서 제휴사 페이지에 대한 장애를 뱅크샐러드 팀원 중 한 명이 일일이 확인해야 함.

제휴사 페이지 확인은 뱅크샐러드 서비스의 품질 확보를 위해 꼭 필요한 업무지만, 매시간 사람이 직접 수행하는 것은 매우 비효율적입니다. URL Checker가 제휴사 페이지를 확인하여 단순히 반복해야 하는 업무를 자동화함으로써 뱅크샐러드 팀이 다른 중요한 업무에 집중할 수 있도록 하는 것을 목표로 삼았습니다.

문제 해결 접근

문제 정의를 기반으로 이 문제를 어떻게 해결할지 기능별로 나눠서 고민했습니다.

  • 제휴사의 페이지에 실제로 접근하여 장애 상황인지 아닌지 파악해야 함.
  • 언제 장애가 발생할지 모르니 주기적으로 모니터링을 해야 함.
  • 장애가 발생할 경우 빠르게 대응하기 위해 관계자에게 알림을 줘야 함.

Puppeteer

제휴사의 페이지에 접근하기 위해 사람이 아닌 Headless Browser의 대표 라이브러리, Puppeteer를 사용했습니다.

Puppeteer를 통해 뱅크샐러드 팀이 페이지에 직접 접근하지 않아도 제휴사 페이지에 접근할 수 있었습니다. 그러나 접근까지는 했으나 어떤 상황을 장애라 판단할지 그 정의가 필요했습니다.
우선 제휴사 페이지가 정상 상태일 때의 HTML 소스 코드를 snapshot으로 저장해뒀습니다. 그런 다음 일정 시간 후에 제휴사 페이지에 다시 접근했을 때 해당 페이지의 HTML 소스 코드를 이전에 저장한 snapshot과 비교하여 두 snapshot이 다를 경우 장애라고 볼 수 있다는 가설을 세웠습니다.

첫 번째 시련과 Trouble Shooting

위 설계로 프로토타입을 제작하여 가설을 검증하는 도중 문제점이 발견되었습니다.

  • 페이지 내의 간단한 문구 수정도 장애 상황으로 판단
  • 페이지에 존재하는 단순 시간 값(timestamp) 변경에도 장애 상황으로 판단

이러한 문제점으로 ‘HTML 소스 코드가 달라질 경우 장애’라는 가설은 장애 상황을 판단하기에는 부적합하다고 생각했습니다.

프로토타입을 통한 검증 테스트에서 발생한 문제점을 해결하기 위해 어떻게 바뀔지 모르는 텍스트 비교에 예외 사항을 두며 대응하기보단 가설을 전면 수정하는 쪽으로 방향을 정했고, 장애 상황을 판단하는 기준을 이미지의 변경 여부로 세웠습니다.

DOM Tree구조를 비교하는 것도 고려해보았으나, 제휴사 페이지의 code는 제휴사 측에서 변경하는 부분이기 때문에 사용자에게 보여지는 부분에 대한 변화 없이 변경될 수 있는 영역이라고 생각했습니다. 이에, 장애 상황을 판단하는 기준은 가장 안전한 기준인 ‘페이지 스크린샷’으로 잡았습니다.

새로운 기준을 바탕으로 정상 상태의 이미지와 현재 시점의 캡처 이미지를 비교하여 특정 수치 이상 차이가 있으면 장애라는 가설을 재수립했습니다. 이번에도 마찬가지로 이 가설을 검증하기 위해 테스트를 진행했습니다.

URL Checker 전체 구성도

  1. URL Checker를 위한 백오피스를 바로 제작하기보단 빠른 가설 검증을 위해 운영팀에서 기존에 구글 시트를 사용하고 있다는 점을 고려하여 Google Sheets API 활용
  2. URL Checker가 주기적으로 제휴사 페이지를 순회하며 이미지를 캡처.(current.png)

    • AWS S3에 상품의 정보를 key로 폴더를 분류하고, 해당 상품의 이미지가 없을 경우 origin.png라는 이름으로 업로드
  3. 정상 상태의 이미지와 캡처한 이미지를 비교하여 기존 대비 변경된 정도를 파악 후 50% 이상 다를 경우 장애라고 판단.

    • 50%: 자동으로 움직이는 Carousel이 적용된 페이지의 경우 매번 current.png 이미지에 변경이 발생하는데, 이를 장애라고 판단하지 않을 보수적인 수치로 산정했습니다.

이미지 비교는 Resemble.js를 이용했으며, 측정 기준은 Resemble.js의 compare 수치입니다.

CronJob

제휴사 페이지를 주기적으로 모니터링하기 위해 CronJob을 만들었습니다. CronJob을 만들기 위해 GitHub Actions의 schedule 기능을 사용했습니다. 서버를 별도로 관리하지 않아도 된다는 장점이 있었고 별다른 설정 없이 빠르게 개발할 수 있다는 장점 때문에 선택하게 되었습니다.

Slack Notification

뱅크샐러드는 온라인 커뮤니케이션 도구로 Slack을 사용하고 있습니다. 장애 발생 시 바로 관계자에게 알림을 줘서 장애에 대해 빠르게 대응할 수 있도록 Slack incoming Webhooks를 이용하여 Slack App을 만들고 장애 알림 메세지를 보내도록 했습니다.

두 번째 시련과 Trouble Shooting

운영 시작 알림 메세지

야심 차게 정식운영을 시작한 URL Checker는 운영 직후 양치기 bot이 되기 시작했습니다.

URL Checker 에러

page 로드가 끝나기 전에 URL Checker가 캡처한 사진으로 인한 오류상황

원래 슬랙 메세지의 원본이미지 부분에는 ‘정상 상태’의 제휴사 페이지 사진이 들어가나, 본 글에서는 뱅크샐러드 페이지 사진으로 표현하였습니다.

URL 목록 중 33% 정도의 비율로, 인터넷 연결이 끊기거나 해당 사이트에서 접근을 차단하는 ERR_CONNECTION_CLOSED, 브라우저와 웹 페이지간 통신이 없을 때 발생하는 ERR_CONNECTION_TIMED_OUT 에러가 발생하여 장애 상황을 제대로 파악하지 못했으며, 페이지에서 리소스를 불러오는 도중에 화면을 캡처하여 원본 이미지와 다르다고 판단하였습니다.

페이지에서 리소스를 받아오는 중 화면을 캡처하는 이슈는 page.goto optionwaitUntil 값을 networkidle0으로 수정하여 해결되었지만 ERR_CONNECTION_CLOSED, ERR_CONNECTION_TIMED_OUT 이슈는 그 빈도만 줄어든 상태에서 꾸준히 발생했습니다.

세 번째 Trouble Shooting

세 번째 Trouble Shooting에서는 전략이 필요했습니다. 문제가 되는 URL을 관리하는 제휴사의 서버 환경을 정확히 알 수 없어 원인 파악에 어려움이 있었기 때문입니다.

오류의 원인은 제휴사 서버의 설정값에 의한것일 수도 있고, URL Checker가 운영되는 GitHub Actions 환경 문제일 수도 있습니다. 이 원인을 보다 확실하게 해결하기 위해서 우리에겐 Out Of Control 영역인 제휴사 서버가 원인일 수 있다는 점은 배제하고, 불확실한 요소를 줄여 디버깅 난이도를 낮추는 것이 먼저라고 생각해 URL Checker의 운영 환경을 먼저 변경해보기로 했습니다.

GitHub Actions의 runner를 기본 제공 서버에서 EC2로 변경하였습니다. 그러자 에러 발생 비율이 33%에서 3% 수준으로 줄어들었고 대부분의 URL에서 간헐적으로 오류가 발생하는 수준으로 개선되었으나 특정 URL에서는 계속해서 에러가 발생했습니다. 이 URL을 실제로 휴대폰 디바이스를 통해 접근하면 제대로 불러와 졌기에 ‘VM, EC2 등 실제 기기가 아닌 서버 환경에서의 접속을 차단한다.‘라는 가설을 세웠습니다. 이 이슈를 해결하기 위해서는 URL Checker가 단말기의 LTE를 통해 접속하여 URL Checker가 운영되도록 환경을 변경해야만 했습니다. 비용 대비 효용을 고민하게 되었고, 아직까지는 하나의 URL에 대해서만 발생하는 이슈이기 때문에 체크 대상에서 제외했습니다.

네 번째 Trouble Shooting

몇 차례의 Trouble Shooting에도 ERR_CONNECTION_CLOSED, ERR_CONNECTION_TIMED_OUT 오류가 간헐적으로 발생했습니다. 이 단계에 들어서니, 이전과는 완전히 다른 해결 방법이 필요하다고 생각했습니다. Trouble Shooting의 시작점은 현재 상황에 대한 진단이라고 생각하여, 그동안 사용했던 EC2 사용 현황을 살펴보았습니다.

EC2사용 상태

위 지표를 보면 CPU를 상당히 많이 사용했음을 알 수 있습니다. 간헐적으로 오류가 발생했던 원인으로 많은 작업을 수행하는 데에 있어서 서버 자원이 충분하지 못했다고 판단했습니다.

그리고 처음 GitHub Actions를 선택했던 이유는 별도의 서버 설정 없이 CronJob을 만들 수 있다는 이유였지만 Trouble Shooting 과정에서 GitHub Actions에 self-hosted runner가 필요하다고 판단하여 별도의 EC2를 운영했습니다. 하지만 이 역시 저희가 마주한 오류를 완벽하게 해결해주지는 못했습니다.

서버 자원 추가 할당과 함께 운영 환경을 한번 더 변경하기로 했고, 운영환경을 기존 GitHub Actions가 아닌 Kubernetes CronJob으로 과감히 옮기기로 결정했습니다.
그 이유는 현재 뱅크샐러드 팀에서는 이미 다수의 CronJob이 Kubernetes에서 관리되고 있고, Kubernetes 설정을 Infrastructure as Code로 직접 관리할 수 있기 때문에 더욱 편리할 것이라 생각했습니다.

기존에 사용했던 인스턴스인 t2 medium의 서버자원이 부족했다는 점을 고려해서, 메모리 할당을 넉넉히 requests: 8Gi, limits: 16Gi로 설정했습니다.

resources:
  limits:
    cpu: 2
    memory: 16Gi
  requests:
    cpu: 2
    memory: 8Gi

k8s 대쉬보드 화면 이 결과 현재 안정적으로 운영 중이며, 7일간의 모니터링 결과에서 URL Checker 자체의 에러가 단 한 건도(!) 발생하지 않았습니다.

Result

URL Checker안정적인 운영모습

현재 URL Checker는 모두가 잠든 시간에도 열심히 제휴사 페이지를 체크하고 있습니다.

사이트 장애 탐지 성공

제휴사 페이지가 들어가는 부분을 뱅크샐러드 페이지로 대신 표현하였습니다.

제휴사의 비정기적 점검이나 장애 상황 등을 매번 URL에 직접 접속하지 않고도 알 수 있게 되었습니다.

Next TODO

운영팀의 고통을 더 줄이고 다양한 상황에서 제휴사 페이지의 안정성을 보장하기 위해 Next Step을 준비하고 있습니다.

Playwright

현재 사용하는 puppeteer는 크롬 환경에서밖에 동작하지 않는다는 단점이 있습니다.

Playwright는 브라우저 엔진을 Webkit으로도 설정할 수 있어, iOS 사용자의 환경에서도 제휴사 페이지를 확인할 수 있다는 장점이 있습니다. 최종적으로는 Chromium과 Webkit 두 가지 환경에서 URL Checker가 운영되도록 변경할 예정입니다.

Feedback

URL Checker를 개발한 가장 큰 목적은 ‘운영팀의 노고를 줄인다.‘였습니다. 제휴사 상품 페이지에 대한 확인을 자동화함으로써 다음과 같은 결과를 얻을 수 있었습니다.

운영팀 피드백

제휴 상품 페이지의 동작 여부를 확인하는데 한 주간 소요되는 시간이 전체 응답자에 대해 기존 2~3시간에서 1시간 이내로 줄어들었고, 장애를 인지하는 시간또한 2~4일 이내에서 1일 이내로 단축되었습니다.
앞으로도 운영 팀과의 협업을 통해 운영 중에 발생할 수 있는 매출 리스크와 좋지 않은 고객 경험을 개선하고, 중요한 일에 집중할 수 있는 팀을 만들어 나가고자 합니다.

마무리

위처럼 외부 환경으로 발생할 수 있는 장애를 능동적으로 대응하기 위해 URL Checker를 기획하고 만들었습니다. 그 과정에서 뱅크샐러드가 가장 중요시한건 ‘문제 해결’에 집중하는 것이었습니다. 문제를 잘 정의하고, 단계를 나눠 접근하고, 또 그 과정에서 가설을 세워 검증하는 과정을 거치며 좀 더 알맞은 방법으로 문제를 해결할 수 있었습니다. 하나의 문제를 푸는데 다양한 방법이 있었고 기술적인 욕심을 부릴수도 있었지만 지금 상황을 고려해 더 나은 사용자 경험을 제공하고 안정성을 확보한다는 문제에 집중할 수 있었습니다.


세상의 다양한 문제를 해결하고 자동화 하는데 관심이 있으신 분, 그렇게 더 나은 세상을 함께 만들고 싶으신 분들 모두 💚뱅크샐러드💚에서 함께 이야기해요!. 🙂

보다 빠르게 뱅크샐러드에 도달하는 방법 🚀

지원하기