너 혹시 T(Tester)야? : 테스트 효율을 높이는 Charles 툴 활용기

Nov.20.2023 박성준

QA/Testing

이미지 출처 : [민보우 드로잉] (https://brunch.co.kr/@nofearbow/94)

들어가며

저는 2020년부터 2022년까지 약 3년간, 배달의민족 앱의 QA(품질 보증) 업무를 담당하였습니다.

배달의민족 앱을 테스트하려면 수많은 테스트 데이터가 필요한데 테스트 데이터를 모두 수동으로 만들 순 없습니다.
테스트 데이터를 효율적으로 쌓기 위해 Charles라는 프록시 툴을 도입하였습니다.

  • 프록시 : 서버와 클라이언트 사이에 중계기로서 대리로 통신을 수행하는 것을 가리킴 (출처 : 위키백과)

Charles 툴은 시스템과 인터넷 간의 모든 HTTP 및 SSL/HTTPS 트래픽을 보고 가로채고 분석할 수 있는 웹 디버깅 프록시 기능에 특화되어 있습니다.

요청, 응답을 확인하는 것만이 아니라 일반적으로는 재현할 수 없는 상황을 만들거나 요청 & 응답 값을 조작할 수 있고
이를 모바일 기기의 GUI로 확인할 수 있어서 클라이언트 테스트 및 디버깅에 유용합니다.

[Charles의 강점]

  • HTTP 요청/응답을 가로채고 다른 값을 응답
  • Status code를 Error code로 응답
  • 대기 시간을 포함하여 느린 인터넷 연결을 시뮬레이션하는 대역폭 조절

이 글에서는 Charles 툴을 테스트 활동에 사용하여 테스트 시간 감소에 도움을 받았고 효율적으로 테스트 데이터를 쌓는 등 적재적소에 툴을 활용했던 경험을 소개하고자 합니다.


Charles 툴에 대해

1. Charles 사용 배경

QA에서 앱 테스트를 수행할 땐 아래 내용을 고려하여 서버와 클라이언트의 로직에 대한 테스트가 이루어집니다.

  1. 클라이언트가 서버에 요청을 잘 보내고 있는지
  2. 서버에선 클라이언트의 요청을 처리하여 응답해 주는지
  3. 서버 응답대로 클라이언트가 화면을 그려주는지


위의 내용을 배달의민족의 가게로 풀어서 설명해 보면 아래와 같습니다.

  1. 사용자가 배달의민족 가게 목록에서 A 가게를 클릭하면 클라이언트에서 서버에 "A 가게의 데이터를 보여줘"라고 요청합니다.
  2. 서버에선 A 가게의 메뉴, 리뷰, 찜 카운트 등의 가게를 구성하고 있는 데이터를 정확하게 응답해 줍니다.
  3. 클라이언트에선 서버 응답 기반으로 GUI를 제공해서 사용자와 인터페이스를 할 수 있게 해줍니다.



이중 테스트에 가장 시간이 많이 필요한 건 어떤 항목일까요?

테스트 데이터를 준비해야 하는 2번 항목입니다.
가게 테스트에 필요한 테스트 데이터를 몇 가지만 예를 들어보면

  • 메뉴 데이터 / 리뷰 수 / 사장님 댓글 / 리뷰 별점 / 찜 카운트 / 배달팁 설정 등이 있겠네요

이처럼 가게를 구성하고 있는 경우의 수는 다양하고 이를 만족하기 위해선 많은 테스트 데이터가 필요합니다.

그림 1 – 배달의민족 가게 화면



만약 리뷰 수가 1,000개가 넘어갈 때 세 자리마다 콤마(,)가 찍히는지
봐야 하는 테스트가 필요한 상태라 가정해 보겠습니다.

리뷰를 무조건 작성할 수 있는 테스트 계정이 있다고 가정하고 리뷰 1개 작성할 때 1분이 걸린다고 접근해 봐도 1,000분이 걸리는 많은 시간이 소모되는 작업입니다.
(물론 QA 조직에서 이렇게 수동으로 데이터를 만들진 않습니다. 예시라 봐주세요 ^^)

어떻게 하면 효율적으로 테스트 데이터를 만들 수 있을까요
예전에 스타크래프트라는 게임이 있었고 이 게임엔 치트키라는 게 있었습니다.

웬 치트키 얘기냐고요?

가게 응답 값에서 리뷰 수를 제공해 주는 파라미터를 치트키처럼 원하는 대로 값을 바꿀 수 있다면 어떨까요?
실제론 서버에서 가게에 등록된 리뷰 수를 계산해서 응답을 전달하지만 이를 가로채서 원하는 값으로 바꾼 후 클라이언트에서 GUI를 잘 제공해 주는지만 테스트한다면 단순 비율로만 계산했을 때 서버, 클라이언트 모두 테스트하는 것 대비 절반의 시간으로 테스트를 진행할 수 있을 것입니다.
이런 치트키 같은 도구가 Charles와 같은 프록시 툴입니다.


2. Charles의 특징

프록시 기능과 모바일 기기 GUI 연동
API 테스트를 진행할 때 많이 사용하는 툴은 ‘Postman’, ‘PAW’ 등이 있습니다.
이들 툴이 API 요청, 응답에 대한 테스트에 특화되었다면 Charles는 시스템과 인터넷 간의 모든 HTTP 및 SSL/HTTPS 트래픽을 보고 가로채고 분석할 수 있는 웹 디버깅 프록시 기능에 특화되어 있는 차이가 있습니다.


Charles 툴 활용사례

그러면 Charles의 특징과 툴을 어떻게 활용하여 테스트를 했는지 공유해 보겠습니다.

1. 서버 응답 데이터 변조

배달의민족의 가게는 사용자가 ‘찜’을 할 수 있고 찜한 가게들만 별도로 확인할 수 있습니다.

찜의 카운트는 9,999까지는 서버의 데이터를 그대로 표시하지만 10,000 카운트가 넘어가게 되면 "1만+"라고 클라이언트에서 가공 처리를 해주어야 합니다.

"1만+"라고 클라이언트에서 표시해 주는지 확인을 위해선?

  • 1대의 디바이스 혹은 1개의 배민회원으론 가게에 찜 등록을 한 번만 할 수 있기 때문에 찜이 1만 개인 가게 테스트 데이터를 만들기 위해선 1만 대 디바이스 혹은 1만 개의 배민 회원 계정이 필요합니다.

수동으로 설정하긴 불가능한 테스트 데이터라 SQL을 활용하여 db를 조작하여 데이터를 만들 수밖에 없습니다. db 접근 권한이 없거나 SQL 관련 지식이 없으면 서버 개발자에게 데이터 설정을 요청해야 하는데 이로 인한 커뮤니케이션 리소스가 소모됩니다. Charles의 프록시 기능을 활용하여 서버 응답 값을 변조하여 로컬 환경에서 재현시키는 방법으로 간단하게 테스트를 할 수 있습니다.

그림 2 – Charles로 서버 응답 데이터 가로채고 대체하는 과정



배달의민족 가게 상세 API에서 서버 응답받은 찜 카운트 값을 변조하는 방법을 적어보겠습니다.

Charles로 API의 서버 응답 값 변조 방법

  1. 배달의민족 가게 상세 API 명세서에서 찜의 데이터를 노출시켜주는 파라미터와 응답 구조 확인
    * 파라미터와 응답 구조는 보안 문제 발생 감안하여 업로드하진 않겠습니다.
  2. JSON Editor tool을 사용해 실제 서버 응답을 가로채고 대체할 API 응답 값을 JSON 구조로 만들기
    * JSON Editor tool은 Visual Studio Code 추천합니다. 파싱 에러 발생 시 툴에 표시해 주기도 하고 무엇보다 무료라는 강점이 있습니다. Visual Studio Code 다운로드
  3. Charles 실행 : Sequence 목록에서 배달의민족 가게 상세 API > 마우스 오른쪽 클릭 > Map Local 선택
  4. Edit Mapping 창의 Local path에서 대체할 JSON 파일 업로드
  5. Charles와 연결된 모바일 기기로 배달의민족 가게 상세 화면 진입 시 대체한 응답 파일구조로 클라이언트에서 화면에 그려주는 것을 확인 가능

그림 3 – 가게의 찜 카운트 응답 값 전/후 변화 사진

2. 서버 API 에러 응답 상황에서 클라이언트 동작 확인

사용자들이 앱을 사용하며 API가 호출될 때 항상 정상적인 200 코드 응답만 받으며 잘 동작되는 것은 아닙니다.

  • 클라이언트의 요청에서 문제가 있는 경우 응답받는 400번대 코드
  • 서버 API에 문제가 있는 경우 응답받는 500번대 코드

위와 같이 에러 응답을 받는 상황이 있고 에러 응답을 받으면 클라이언트는 서버의 응답 데이터로 화면을 정상적으로 그려줄 수 없습니다.
사용자를 고려한 서비스라면 이런 예외적인 상황에서 사용자에게 에러 상태인 걸 가시화해서 보여주고 재시도, 화면 이탈 등의 액션 이벤트를 시도할 수 있게 해주는 유저 플로우가 필요합니다.

그림 4 – 배달의민족 배달 현황 화면의 API가 에러 응답을 받는 상황



이에 대한 테스트의 결과를 도출하기 위해선 아래와 같은 번거로운 절차가 필요합니다.

  1. 서버 개발자에게 테스트 서버에 장애를 만들어달라고 요청
  2. 에러 응답 상황 만들기
  3. 테스트 수행
  4. 테스트 완료 시 원복 요청
  5. 코드 수정 후 원복

이것만으로도 커뮤니케이션 리소스가 발생되는데 더욱 큰 문제는 테스트 서버를 사용하는 모든 유관부서 인원에게 영향을 미칠 수 있단 점입니다.
(10분 동안 100명이 테스트 서버를 사용 못 한다고 했을 때 낭비되는 시간은 1,000분이 될 것입니다.)

이렇게 에러 응답을 받는 상태에 대한 테스트도 charles의 프록시 기능을 활용하여 QA 인원 개인의 로컬 환경에서 테스트 서버에 전혀 영향을 주지 않고 테스트 가능합니다.


배달의민족 가게 목록 API에서 500 응답을 받는 상황을 로컬에서 만들어서 테스트하는 방법을 공유해 보겠습니다.

Charles로 API 200 응답 -> 500 응답으로 변경하는 방법

  1. Charles 실행 : Sequence 목록에서 사용하고자 하는 API를 선택 > 마우스 오른쪽 클릭 > Copy URL 선택
  2. Tools > Rewrite 메뉴 진입
  3. Enable Rewrite 체크
  4. [Add] 버튼 선택하여 Rewrite 항목 추가
  5. Name 입력 후, Location의 [Add] 버튼 선택하여 ‘Edit Location’ 창 불러온 후 아래 값 입력
    • Protocol : http / https 중 선택
    • Host : 가게 목록 API 입력 (복사 -> 붙여넣기 가능)
    • Path : API Path 입력 (‘Host’에 URL 붙여넣기 후, 키보드의 [Tab] 키 선택 시 자동으로 값 채워짐)
    • Query : 특정한 정보를 요청하여 Rewrite 대상을 한정할 경우 입력
      (‘Host’에 URL 붙여넣기 후, 키보드의 [Tab] 키 선택 시 자동으로 값 채워짐)
  6. Type의 [Add] 버튼 선택하여 ‘Rewrite Rule’ 창 불러온 후 아래 값 입력 후 저장
    • Type : Response Status
    • Match – Value : 200
    • Replace – Value : 500
  7. 모바일 기기로 Rewrite Rule을 설정한 API인 가게 목록 화면을 진입하여 API 요청 & 응답이 이루어지면 [절차 6]에서 대체한 500 Status 에러 응답을 받기 때문에 클라이언트 화면에서 에러 화면을 제공해 주는지, (다시 시도) 액션 이벤트가 잘 이루어지는지 등의 테스트를 서버 장애 상황이 아닌 경우에도 가능

그림 5 – 가게 목록 API가 200 응답일 때와 500 응답일 때 앱 화면 비교


그림 6 – 가게 목록 API가 200 응답일 때와 500 응답일 때 Charles 화면 비교

3. Timeout 상황 테스트

Timeout은 ‘프로그램이 특정한 시간 내에 성공적으로 수행되지 않아서 진행이 자동적으로 중단되는 것’을 말합니다.

만약 앱에 Timeout이 구현되지 않았다면?
엘리베이터나 만원 지하철 등, 네트워크가 느려질 수 있는 상황에서 API 호출하였을 때 끝없이 로딩이 발생될 수 있습니다. Timeout이 구현되었다면 일정 시간 이후 API 호출 시도가 자동적으로 중단됩니다.

Timeout 테스트는 일반적인 도심의 사무실 환경에선 테스트하기가 쉽지 않지만
Charles는 앱의 Timeout 상황을 재현하여 테스트할 수도 있습니다.

배달의민족의 가게목록에서 가게를 선택하는 시점에 timeout이 발생하는 테스트 상황을 만드는 내용을 영상과 함께 공유하겠습니다.

영상 1 – latency (ms)를 10000ms로 설정하고 배달의민족 앱에서 Timeout 상황 재현


Charles로 Timeout 상황 재현 방법

  1. Charles 실행 : Proxy > Throttle settings 선택
  2. Enable Throttling 체크
  3. 특정 Host에만 throttle 설정을 하고 싶으면 [Only for selected hosts] 체크하고 Host 정보를 입력 (체크를 하지 않으면 모든 요청에 throttle 설정이 적용)
  4. 상세 설정값들을 테스트에 적절한 값으로 입력 후 적용
    • throttle preset : 3G, 4G 등 일반적인 인터넷 연결 유형을 설정
    • Bandwidth : 전송할 수 있는 최대 데이터양을 정의합니다.
    • Utilisation : 한 번에 사용자가 접근할 수 있는 전체 대역폭의 백분율
    • Round-trip latency : 클라이언트와 원격 서버 간의 요청 지연 시간 (ms)
    • MTU : 현재 프리셋의 최대 전송 단위를 정의
    • stability : 연결이 완전히 실패할 가능성을 측정, 신뢰할 수 없는 네트워크 조건을 시뮬레이션 하는 데 사용
    • Unstability quality range : 연결이 ‘불안정한’상태 일 가능성을 측정하여 품질을 저하 시킴. 주기적으로 연결 품질이 떨어지는 모바일 네트워크와 같은 네트워크를 시뮬레이션 할 때 사용
  5. 앱에서 API 호출이 발생되면 throttle 설정이 적용되어 네트워크 지연 상태일 때의 클라이언트 동작을 확인할 수 있습니다.

Charles 꿀팁

Charles를 사용하며 막히는 부분을 해결했던 다양한 꿀팁들을 공유합니다.

  1. Charles 실행 시, 노트북(Mac)에서 인터넷이 되지 않는 문제
    • Proxy > macOS proxy 체크 해제 시 정상적으로 인터넷 사용 가능합니다.

  2. 인증서 만료 상태
    • Help > SSL Proxying > Reset Charles Root Certificate… 선택 > 팝업에서 Reset 선택 > 인증서 재 다운로드하면 신규 인증서 다운로드 가능합니다.

  3. Android 기기에서 "CA 인증서를 설치할 수 없음"이라는 에러 팝업 노출되며 인증서 설치 불가능한 경우
    • 휴대폰 설정 > 생체 인식 및 보안 > 기타 보안 설정 > 디바이스에 저장된 인증서 설치 > CA 인증서 메뉴 진입하여 해당 인증서를 설치하면 됩니다.

  4. OS ver 7.0 이상의 Android 기기에서 앱과 Charles의 연결이 되지 않는 경우
  5. 응답 값이 제대로 나오지 않는 문제
    • SSL Proxying settings에 포함되지 않은 Location들을 확인한 경우입니다.
    • Proxy > SSL Proxying settings… 메뉴 진입 후 요청과 응답 값에 대한 정보를 확인하기 위한 세팅이 필요합니다.
    • Include 영역의 [Add] 버튼 클릭하여 Location을 입력합니다.
      (예시 : 아래 이미지처럼 설정 후 charles에서 ‘baemin’이나 ‘smartbaedal’이란 키워드가 포함된 API들이 레코딩 될 경우, 응답 값이 깨지지 않습니다.)

      그림 7 – SSL Proxying settings

전하고 싶은 이야기

Charles로 클라이언트 테스트하는 방법을 몇 가지 케이스와 함께 설명해 보았는데 간단히 정리해 보겠습니다.

  • 서버 응답 값을 대체하여 로컬 환경에서 클라이언트 테스트가 가능하므로 클라이언트 테스트만 할 경우, 테스트 속도를 50% 이상 단축할 수 있습니다.
    • 어드민으로 만들어야 할 테스트 데이터들을 응답 값 변경으로 대체 가능합니다.
    • 서버에서 데이터를 응답해 주지 못하는 상황에서도 테스트 가능합니다.
    • 텍스트 길이, 가게 응답 수, 유효하지 않은 데이터 등으로 응답 값을 대체하여 서버에서 제한하는 스펙의 한계를 넘어서 테스트할 수 있습니다.
  • API 에러 응답 상황, Timeout 등 일반적으로 테스트할 수 없는 상황에 대한 유저 플로우 테스트가 가능합니다.
  • 요청 & 응답 값 등을 모바일 기기의 GUI와 함께 확인 가능하여 화면만 보고 테스트를 하는 것보다 더욱 정밀한 테스팅을 할 수 있습니다.
  • 클라이언트와 서버 개발팀이 분리된 기능 중심 조직이라면 각 팀의 산출물에 대한 테스트 일정을 별도로 진행하여 한결 유연한 관점으로 테스트 일정 접근이 가능합니다.
    • 배달의민족 푸드서비스 조직에선 클라이언트와 서버의 QA 인원이 별도로 있기에 QA 인력을 클라이언트와 서버에 분산 배치하여 각 조직의 개발 산출물을 별도의 일정으로 테스트를 진행하였습니다.
      이에 대한 배경은 같은 팀 임선진님이 쓴 가파르게 성장하는 서비스를 담당한, 한 품질담당자의 회고를 먼저 읽어보시면 이해가 빠를 것 같습니다.

서버와 클라이언트의 QA R&R이 분리되어 있었고 서버 로직에 대한 검증을 별도로 진행하는 동료들이 있었습니다. 따라서 클라이언트에 집중하여 테스트 전략을 수립할 필요가 있었고 Charels를 활용하여 앱 배포 주기 내에 많은 과제에 대한 QA 업무를 진행할 수 있었습니다.

다만 모든 테스트를 Charles를 통해 서버 응답을 대체하여 테스트를 하는 건 위험할 수 있습니다.
로컬에서 잘 동작돼도 서버에서 데이터를 잘 처리해서 제공해 주지 못한다면 사용자에게는 전혀 의미가 없는 결과이기 때문입니다.

어디까지나 Charles는 테스팅의 보조 수단으로 활용이 되어야 하며,
서버 코드 수정 없이 클라이언트만 코드 수정이 있는 상황에서 Charles를 활용한다면 더욱 빠르고 효율적인 테스팅을 할 수 있을 것입니다.

참고Charles documentation


부록 : Charles 설치 방법

  1. Charles 공식 홈에서 OS에 맞는 설치 파일을 다운로드 하여 설치합니다.

    라이선스를 구매하지 않는다면 30일간 ‘free trial’ 버전을 사용할 수 있고 라이선스 비용은 사용할 인원에 따라 다른데 최저 50 ~ 최대 700달러의 비용이 필요합니다.

  2. Charles 실행 > Proxy > Proxy settings… 메뉴 진입하여 [Proxies] 탭의 Port: "8888" 입력합니다.

    그림 8 – 포트 입력
  3. Proxy > Recording settings… 메뉴 진입 후 [Include], [Exclude] 탭에 정보를 등록합니다.

    • [Include] : ‘Recording’ 대상에 포함되는 정보
    • [Exclude] : ‘Recording’ 대상에 포함되지 않는 정보

그림 9 – Recording settings
  1. 모바일 기기의 와이파이 설정 화면에서 각 항목에 아래와 같이 값을 입력합니다.
    (OS ver에 따라 항목 명칭은 조금씩 상이할 수 있습니다.)

    • 프록시 : 수동
    • 프록시 호스트 이름(서버) : ‘Charles’가 설치된 PC의 IP 주소와 동일한 IP 주소 입력
    • 프록시 포트(포트) : 8888 입력

그림 10 – 모바일 기기 와이파이 설정 화면
  1. 모바일 기기의 웹 브라우저 주소창에 charlesproxy.com/getssl 입력하여 링크 이동 시 자동으로 인증서가 다운로드 되며 OS에 맞는 방법으로 인증서를 설치합니다.
    • (Android)
      • 파일 관리자 앱으로 다운로드한 인증서 설치
    • (iOS)
      • 설정 > 일반 > 정보 > 인증서 신뢰 설정 > 설치한 ‘Charles’ 인증서 토글 ON
      • 설정 > 일반 > 프로파일 및 기기 관리 > ‘Charles Proxy’ 프로파일 설치
  2. 인증서 설치까지 완료된 후, 모바일 기기로 API 호출 동작이 일어나는 이벤트 발생 시 레코딩되어 Charles로 요청, 응답 값 확인이 가능합니다.

    그림 11 – Charles로 레코딩되는 API의 요청, 응답값을 확인 가능