안녕하세요. 아이들나라SRE 송호훈입니다.
서비스를 운영하다 보면 크고 작은 일들이 많이 발생합니다. 하지만 중요한 것은 일이 발생하는 것이 아니라 일이 발생했을 때 어떻게 대처해야 하는가 입니다. 그리고 대처를 하기 위해서는 일이 발생했는지 탐지하는 것부터가 시작인데요. 그래서 오늘은 장애를 탐지하는 이야기부터 해볼까 합니다.
장애를 탐지하는 방법
장애를 탐지하는 방법은 크게 2가지가 있습니다.
첫째, 잘 만들어진 모니터링 시스템을 통해 서비스 이상을 탐지하기
대부분의 회사들은 정교한 모니터링 구성을 통해 서비스를 모니터링 합니다. 이런 방법으로 모든 장애를 다 잡아낼 수 있다면 너무 행복하죠. 물론 현실은 그렇지 않습니다.
사용빈도가 낮은 기능에서 가끔 발생하는 문제, 서버까지 요청이 이루어지지 않아 탐지되지 않는 버그, 특정 액션을 연속할 때 발생하는 문제 등 참 쉽지 않습니다.
둘째, 누군가가 “너네 서비스 이상해”라고 말해주기
여기서 누군가는 구성원이 될 수도 있고, 최악의 경우는 고객이 될 수도 있습니다. 고마운 고객분들은 VOC를 통해 말을 해주시지만, 안타깝게도 조용히 서비스를 이탈하시는 고객분들도 있습니다.
아이들나라는 어린이 대상의 OTT 서비스입니다. 주된 액션은 VOD재생과 HTML5 컨텐츠 재생입니다. 모니터링 시스템을 통해 모니터링 하고 있지만 그래도 잡히지 않는 문제들이 있었고, 이러한 문제들은 VOC를 통해 접수/대응이 이루어지는 상황이었습니다.
그런데 말입니다. 내 아이를 내가 혼내더라도, 어디가서 맞고 오는건 용서할 수 없지 않겠습니까? 적어도 내 서비스의 문제는 고객이 알아차리기 전에 내가 먼저 알고 싶었습니다. 그럼 누군가는 충성고객이 되어 하루종일 서비스를 사용해주면서 이상하면 바로 알려주어야 했습니다.
근데 이게 가능할까?
일을 시작하기에 앞서 여러가지 고민이 됩니다.
•
이걸 만들어서 뭘 테스트하지?
•
누구랑 같이 해야하지?
•
어떻게 만들지?
•
만들면 잘 돌긴 할까?
고민하는 동안 장애가 발생했고, 일의 당위성을 얻었습니다.
그래서 빠르게 시도 해보고 되는 방향으로 고치기로 합니다. 이른바 Fail fast 전략입니다.
만들어서 뭘 테스트하지?
테스트 범위가 고민되었습니다. 물론 다 하면 좋지만, 신규기능이나 UI개편, 편성 변경 등 고려해야할 요소들이 많았습니다. 그래서 최소한의 기능으로 우선 되는 걸 만들어보자고 결정했습니다. 더 테스트하고 싶은게 있다면 구성원들이 직접 참여하기를 기대합니다.
그리고 무슨 버전을 테스트할지도 의견이 분분했습니다.
•
베타 버전
◦
장점
▪
기능 출시 전 테스트 가능하여 고객이 문제를 경험하기 전에 찾을 수 있다.
◦
단점
▪
지속적으로 변경되기 때문에 테스트가 다른 이유로 깨질 가능성이 크다.
▪
빌드할 때마다 새로운 베타버전을 설치해야한다.
•
스토어 버전
◦
장점 : 실제 사용자 환경과 동일하게 테스트할 수 있다.
◦
단점 : 고객이 장애를 경험할 가능성이 있다.
여러가지로 고민해본 결과 자동화 테스트의 목적이 “최소한의 필수 기능으로 재난 상황을 막는다”였기 때문에 스토어 버전을 타겟으로 잡았습니다.
누구랑 같이 해야하지?
이게 참 어려운 일이었습니다. 다들 자기 일하기 바쁜데 누가 나서서 일을 더 만들려고 할까… 다행히도 뜻이 맞는 구성원을 찾았고 슬슬 일을 벌이기 시작했습니다.
어떻게 만들지?
테스트 단말기 수급
단말기 선택에서도 여러 고민을 했지만, 결론은 실물 장비를 선택했습니다.
실물장비 | 시뮬레이터 | AWS 디바이스팜 | mTworks
(디바이스팜과 유사한 국내 서비스) | 삼성 디벨로퍼
remote test lab | |
장점 | • 실제 사용자와 동일한 환경으로 구성 | • 장비수급 및 발열 이슈 없음 | • 손쉽게 다양한 장비를 수급
• 동시에 테스트 수행 | • 손쉽게 다양한 장비를 수급
• 동시에 테스트 수행
• 실물장비를 원격으로 제공하여 실물 장비에서 테스트를 수행 | • 무료
• 수많은 삼성 갤럭시 장비를 테스트해볼 수 있음. |
단점 | • 장비 자체를 관리해주어야 함
◦ OS업데이트
◦ 보안관리
◦ 발열관리
• 테스트단말기를 연결해놓을 서버가 필요함 | • 아이들나라 앱은 플레이어 이슈로 시뮬레이터 지원이 되지 않아 대상에서 제외 | • 아이들나라 앱은 해외에서 영상재생이 제한되어 사용 불가 (디바이스팜은 us-west-2 리전 사용가능) | • 자체 테스트 언어를 사용해야 함.
• 실물 장비를 돌려쓰는 것이어서 다른사람이 쓰던 흔적이 많은 장비를 사용하게 됨
• 클린한 장비를 위해 예약을 하게되면 비용이 비싸짐. | • 매번 재설치를 해주어야 함.
• ios 장비가 없음.
• 크레딧 개념으로 사용시마다 크레딧이 차감 (크레딧을 수급할 방법이 매일 로그인을 해야하는 것밖에 없음) |
테스트 도구 조사
테스트 도구는 가장 유명한 Appium을 고려했습니다. 그러던 중 GPT 형님이 maestro라는 것도 있다고 알려주어서 maestro도 함께 검토를 해보았습니다. 가장 믿음직스러운 것은 appium이었지만 우린 Fail fast 전략이기 때문에 빠르게 해볼 수 있는 maestro로 우선 성과를 보고자 했습니다.
maestro | appium | |
장점 | • 러닝커브가 없음.
(노코드에 가깝게 yaml로 시나리오를 작성하면 실행해줌)
• 복잡한 액션이 간단한 커맨드로 구현되어 있어 테스트가 간결해짐 | • 코드로 조작하는 만큼 섬세한 작업이 가능함
• 프로젝트가 무르익어서 하고싶은 대부분의 작업을 구현할 수 있음 |
단점 | • 테스트가 간결한 만큼 제약사항이 많아서 고도화하기가 어려움
• 몇가지 버그가 있는데 안고쳐짐.
◦ 테스트 속도에 따라 요소 탐색이 실패하는 경우
◦ 테스트 수행시마다 apk를 생성하는데, 끝나고 안지워서 빌드서버 용량이 금방 참.
• ios는 시뮬레이터만 지원 (실물단말 테스트를 지원하지 않음) | • maestro에서는 한줄로 되는 것을 모두 메소드로 구현해야 함 |
테스트 작성
테스트코드 작성 패턴은 selenium에서 추천하는 POM(Page Object Model)을 따르기로 하였습니다.
POM 패턴의 장점은 각 페이지의 요소와 테스트 케이스의 로직을 분리하여 각 클래스가 본연의 기능에만 충실할 수 있도록 하는 것입니다.
테스트 코드는 실행 순서도 중요합니다.
예를 들면 로그인 테스트를 수행한 이후 권한이 있는 VOD 컨텐츠를 재생하는 등의 액션을 하는 경우입니다.
이런 경우 maestro의 경우 config.yaml을 이용하여 appium의 경우 testng.xml에 선언 순서를 이용하여 테스트 클래스의 실행순서를 제어할 수 있습니다.
flows:
- "flow_*"
executionOrder:
continueOnFailure: true
flowsOrder:
- flow_0_setup
- flow_0_login
- flow_0_gnb
- flow_0_mymenu
- flow_0_profile
- flow_2_reading
- flow_1_play
- flow_3_learning
- flow_0_logout
YAML
복사
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite">
<listeners>
<listener class-name="kr.co.inara.ReportListener"/>
</listeners>
<test name="PRE_STEP">
<classes>
<class name="kr.co.inara.core.AppUpdateChecker"/>
<class name="kr.co.inara.core.AuthTests">
<methods>
<include name="로그인"/>
</methods>
</class>
</classes>
</test>
<test name="CORE">
<classes>
<class name="kr.co.inara.core.GNBTests"/>
<class name="kr.co.inara.core.ProfileTests"/>
<class name="kr.co.inara.core.MyMenuTests"/>
</classes>
</test>
<test name="PLAY">
<classes>
<class name="kr.co.inara.play.PlayMainTests"/>
<class name="kr.co.inara.play.CharacterTests"/>
<class name="kr.co.inara.play.OriginalTests"/>
<class name="kr.co.inara.play.PlayClassTests"/>
<class name="kr.co.inara.play.QuizShowTests"/>
<class name="kr.co.inara.play.PlaygroundTests"/>
<class name="kr.co.inara.play.ParentsClassTests"/>
</classes>
</test>
<test name="LEARNING">
<classes>
<class name="kr.co.inara.learning.LearningMainTests"/>
<class name="kr.co.inara.learning.HomeTests"/>
<class name="kr.co.inara.learning.EnglishTests"/>
<class name="kr.co.inara.learning.MathAndCodingTests"/>
<class name="kr.co.inara.learning.KoreanAlphabetTests"/>
<class name="kr.co.inara.learning.NuriCourseTests"/>
<class name="kr.co.inara.learning.EggSchoolTests"/>
</classes>
</test>
<test name="READING">
<classes>
<class name="kr.co.inara.reading.ReadingMainTests"/>
<class name="kr.co.inara.reading.LibraryTests"/>
<class name="kr.co.inara.reading.PictureBookTests"/>
<class name="kr.co.inara.reading.ReadingBookTests"/>
<class name="kr.co.inara.reading.AudioBookTests"/>
<class name="kr.co.inara.reading.CompleteCollectionTests"/>
<class name="kr.co.inara.reading.TouchBookTests"/>
<class name="kr.co.inara.reading.ArBookTests"/>
<class name="kr.co.inara.reading.VideoClassTests"/>
</classes>
</test>
<test name="POST_STEP">
<classes>
<class name="kr.co.inara.core.AuthTests">
<methods>
<include name="로그아웃"/>
</methods>
</class>
</classes>
</test>
</suite>
XML
복사
테스트 리포트
테스트 수행에 대한 리포트는 테스트 설계 만큼이나 중요합니다. 테스트가 성공했는지 실패했는지, 실패했다면 왜 실패했는지 알아야 대응이 되겠죠?
아이들나라에서는 Slack으로 커뮤니케이션을 하기 때문에, Slack에 리포트를 배달하기로 했습니다. maestro는 기본적으로 xml으로 fail 리포트가 뽑히고, 실패한 케이스에 대해 스크린샷을 저장하여 실패 시점의 화면 상태를 저장합니다.
appium을 사용하신다면 TestNGITestListener 인터페이스의 onTestFailure(ItestResult) 메소드를 확장받아 구현하거나, 파이썬같은 경우 TestResult클래스 또는 hook함수를 통해 유사한 기능을 구현할 수 있습니다.
테스트 실패 시 이 리포트와 스크린샷을 가져와서 Slack에 전달하도록 구현했습니다. 또한, 스크린샷 만으로는 부족한 경우가 있는데요. 이 문제를 해결하기 위해, 테스트 수행 전 과정의 화면을 녹화하여 fail 리포트 시 같이 전달하고 있습니다.
만들면 잘 돌긴할까?
100%는 아니지만 90%정도는 쓸만합니다. 여기에서 10%를 뺀 이유는 단말기의 성능 또는 환경적인 이슈로 인해 실행이 느려지는 경우 요소를 찾지 못하여 종종 실패하는 케이스가 발생하기 때문입니다.
저희는 이렇게 UI 테스트 자동화를 구성하여 크리티컬한 이슈를 사전탐지하여 빠르게 조치할 수 있었습니다. 또 반복적인 테스트에서 발생하는 서비스가 불안정한 부분이나 특정한 플로우에서만 발생하는 버그도 발견하여 조치하고 있습니다. 앞으로도 이 자동화 시스템은 적극적으로 활용할 계획입니다.
+ 추가 기능
테스트 실패 케이스에서 스크린샷을 보는 것도 좋지만, 실패에 이르기까지의 과정을 레코딩하여 보면 더할 나위없이 좋습니다. 저희는 레코딩을 위해 scrcpy 를 사용하고 있습니다.
scrcpy는 레코딩 해상도를 지정할 수 있고, 또 실물기기의 화면을 기기가 물려있는 서버의 화면으로 전환할 수 있기 때문에 실물기기의 발열 문제도 잡을 수 있었습니다.
(물론 adb 자체적으로 레코딩을 지원하지만 aos api 버전이 낮은 경우 레코딩 제한이 최대 180초로 다소 짧습니다.)