안녕하세요. 카카오뱅크에서 안드로이드(Android) 모바일 앱을 개발하는 Groot입니다. 저는 오늘 현재 저희가 자체 개발하여 사내에서 사용 중인 한 도구를 소개하려고 합니다.
먼저, 제가 개발을 하고 있는 금융 도메인에 대해 이야기하겠습니다. 은행에서 모바일 앱을 개발할 때 사용되는 기술과 방법은 특별히 다르지 않지만, ‘금융 데이터’를 다루기 때문에 규제 준수를 위해 더 정교하고 안전한 환경에서 테스트를 진행해야 하는 의무가 있습니다. 예를 들어, 은행은 고객의 민감한 금융 정보를 다루기 때문에 운영 환경의 ‘실제 데이터’를 개발 과정에서 직접 사용하기가 어렵습니다. 이러한 이유로 원하는 테스트 데이터를 직접 가공해야 하는데, 그 또한 쉽지 않습니다.
이처럼 은행에서의 앱 개발과 테스트는 매우 제한된 환경에서 이루어질 수밖에 없습니다. 그래서 오늘 소개할 REST API 모킹 도구가 탄생했습니다! 이 도구는 클라이언트 개발자를 포함한 기획자, QA 담당자가 외부 환경에 구애받지 않고 필요한 데이터를 손쉽게 제어할 수 있도록 도와줍니다. 그럼 이제부터 도구가 만들어진 배경과 개발 과정, 그리고 제공하는 기능들에 대한 사용법을 중심으로 자세히 설명드리겠습니다.
이 값 API로 보내주실 분…? ✋
카카오뱅크 개발자들은 앱 개발 과정에서 수 많은 API를 연결하고 다양한 외부 기관과 상호작용하며 다양한 상황에 대해 테스트를 진행합니다. 간단한 서버 API 하나라고 해도, 그 내부는 여러 외부 기관과 다른 서버들의 데이터를 복합적으로 조합하여 응답을 만들어내는 경우가 많기 때문입니다.
예를 들어 고객의 계좌 잔액을 조회하는 단순한 API의 경우에도 실제로는 내부 데이터베이스부터 다른 금융 기관의 응답과 여러 보안 서버 등을 거쳐 복합적으로 가공된 데이터를 모아 결과를 제공합니다. 언뜻 단순해 보이는 요청이지만 살펴본 바와 같이 여러 시스템의 협력이 필요합니다.
이제 그 복잡한 연계 중 특정 기관이 오류를 반환하는 상황을 테스트 하는 경우를 상상해 보시죠. 만약 서버 내부에서 데이터를 제어할 수 있다면, 서버 개발자에게 요청하여 데이터를 조작하고 원하는 응답을 받을 수 있습니다. 하지만 그런 상황이 아닐 때는 문제가 좀 복잡해집니다. 이 경우, 하나의 상황을 테스트하기 위해 클라이언트, 서버, 코어뱅킹, 외부 연계시스템, QA 등 여러 단계를 거쳐야 합니다.
[그림 1]에서 보듯, 고객에게 보여지는 부분을 담당하는 클라이언트 개발자로서 가장 어려운 점은, 원하는 값을 받아오기 위해서는 뒷단에 있는 여러 개발자들과 많은 협의와 단계를 거쳐야 한다는 것입니다. 여러 팀 간의 협력이 필수적이며, 테스트 데이터를 만들고 제어하는 데 많은 시간이 소요될 수 있습니다. 때로는 담당자를 찾는 것도 쉽지 않습니다. 이러한 커뮤니케이션 비용을 줄이면서도 속도감 있는 개발과 테스트를 위해서는, 클라이언트 단에서 서버의 도움 없이도 필요에 따라 응답을 수정할 수 있는 기능이 필요하다고 생각하게 되었습니다.
이런 문제를 해결하기 위해, 저희 팀은 클라이언트 개발자가 여러 담당자를 거치지 않고도 스스로 문제를 해결할 수 있는 REST API 데이터 모킹 도구를 개발했습니다. 이를 통해 클라이언트 개발과 테스트 과정에서의 편의를 크게 높일 수 있었습니다. 그럼 저희가 어떤 과정을 거치면서 REST API 데이터 모킹 도구라는 대안을 찾았는지, 이어서 설명드리겠습니다.
서버 응답값에서 자유로워지기 위해 탄생한 도구 🛠️
해당 문제 상황에 대해 저희가 생각해 본 대안은 여러가지가 있었는데요. 크게 4가지로 정리해보면 다음과 같습니다.
🥲 대안 1. 모든 상태의 UI 테스트 코드 작성하기
클라이언트 단에서 우선 고려할 수 있는 가장 정확한 방법은 모든 상태에 대해 UI 테스트 코드를 작성하는 것입니다. 하지만 현실적으로 모든 상태에 대해서 테스트 코드를 작성하는 것은 쉽지 않은 방법입니다. 또한 CI/CD 과정에서 UI 테스트 코드가 많아질수록 테스트 시간이 길어지고 실패되는 케이스를 잡는 비용이 오히려 더 증가할 수 있습니다.
😵 대안 2. 클라이언트 단에서 내부 코드 조작하기
서버 개발자와 커뮤니케이션을 하지 않고 가장 쉽게 데이터를 변경해서 볼 수 있는 방법은 클라이언트 개발 시에는 해당 상황을 내부 코드를 조작함으로써 다양한 상황을 확인해 볼 수 있습니다. 예를 들어 코드의 조건문을 변경하여 특정 상황을 반대로 바꿔서 보거나 서버에서 받은 응답이 아닌 데이터를 직접 클라이언트 단에서 하드코딩하여 보는 방법을 사용할 수 있습니다.
하지만 이와 같은 방법 사용하다 잠깐 보려고 넣었던 코드가 MR에 같이 들어가 원치 않은 동작을 한다면 어떨까요? 물론 대부분의 경우 코드 리뷰 과정에서 발견되어 운영환경으로 배포 될 가능성은 거의 없습니다만 좋은 방법이라 할수는 없습니다.
😅 대안 3. Proxy Server를 이용한 서버 응답 변경하기
그렇다면 코드 조작을 하지 않고 서버의 응답을 마음대로 변경하여 받을 수 있는 방법이 있을까요? 대표적으로 Charles Proxy와 같은 HTTP 프록시/모니터링 도구의 Proxy Server의 기능을 활용하는 방법이 있습니다. 물론 PC 환경에서는 해당 방법도 좋은 대안이 될 수 있겠지만, 모바일 환경에서는 인증서 등의 초기 설정이 쉽지 않고 여러 가지 보안 장치가 연계 되어있어 별도로 우회할 수 있는 로직을 넣어주어야 하기 때문에 처음 사용하는 유저에게는 진입장벽이 있습니다. 특히 개발자가 아닌 기획자 또는 QA 직군이 해당 방법을 사용하기는 조금 어려운 측면이 있습니다.
✅ 대안 4. REST API 모킹 도구를 직접 만들어보자!
만약 대안 1~3번이 적합하지 않다면, 직접 만드는 방법밖에 없겠죠! Android 앱에서 API 요청/응답(Request/Response)을 볼 수 있는 Chucker라는 오픈소스를 참고하여 요청과 응답을 보여주고 편집할 수 있는 모킹 도구를 만들기로 했습니다. 모킹 도구는 API 응답값을 특정 값으로 간단히 변경할 수 있습니다. 이를 통해 특정 상황을 만들어 내고, 그 상황에서 UI가 정상적으로 그려지는지를 확인할 수 있습니다. 예를 들어, API 응답값을 수정하여 금액이 비약적으로 크거나 이름이 매우 긴 값을 테스트할 수 있습니다.
결과적으로 사내에서 누구나 쉽게 API 응답값을 모킹해서 사용할 수 있는 자체 도구를 만들게 됐습니다.
테스트나 개발 과정에서 실제 데이터를 대신할 가짜 데이터를 생성하거나 제공하는 것을 의미합니다. '모킹'은 가짜 데이터를 '모방'한다는 뜻에서 유래되었습니다. 실제 데이터가 없거나 이를 사용하는 것이 비효율적일 때, 데이터 모킹을 통해 효율적으로 작업을 진행할 수 있으며, 다음과 같은 상황에서 유용합니다.
- API 테스트: 외부 API가 아직 준비되지 않았거나 불안정한 경우, 모킹된 API 응답을 사용하여 테스트를 진행할 수 있습니다.
- 빠른 개발: 실제 데이터베이스 접근 없이 빠르게 테스트 환경을 구축할 수 있습니다.
- 에러 시나리오 테스트: 특정한 오류 조건을 인위적으로 만들어, 이를 처리하는 코드를 테스트할 수 있습니다.
- 민감한 데이터 보호: 실제 민감한 데이터를 사용하지 않고도 테스트를 수행할 수 있어 보안 문제를 방지할 수 있습니다.
본격적인 REST API 모킹 도구 개발 Start! 🚀
그럼 본격적으로 번개처럼 빠른 개발을 위해 탄생한 REST API 모킹 도구⚡️ 제우스(Zeus)의 개발 과정을 설명드리겠습니다.
⛳️ Step 1. 요구사항 분석과 계획 수립
제우스를 직접 개발하기로 마음 먹고, 인터뷰를 진행했는데요. 해당 도구에 어떤 기능을 담으면 좋을지 담당자들의 요구사항을 종합하여 간략히 정리해보면 다음과 같습니다.
👩🏽💻 서비스 기획자: "앱 내 어디서든 간편하게 접근할 수 있으면 좋을 것 같아요."
👨🏻💻 클라이언트 개발자: "프로덕션 앱에는 해당 기능의 흔적도 남아 있지 않게 만들어주세요."
이처럼 예비 사용자들과의 인터뷰를 통해 좀 더 구체화된 그림을 그려볼 수 있었는데요. 전반적인 시스템의 설계와 모킹 도구에 담을 기능들을 고민한 끝에, 최종적으로 다음과 같은 3가지 목표를 설정했습니다.
- ✔️ 일관된 데이터로 모킹 신뢰성 확보하기
- ✔️ 언제 어디서 누구든 접근 가능한 UX
- ✔️ 간단한 모킹 절차: API 목록 저장, 응답 변경/지연 기능
그럼 바로 이어서 위의 3가지 목표를 어떠한 방식으로 제우스에 적용하였는지 순서대로 하나씩 살펴보겠습니다.
⛳️ Step 2. 시스템 설계와 구현
2-1. 일관된 데이터로 모킹 신뢰성 확보하기
클라이언트 개발자가 평소와 같이 개발 업무를 수행할 때에도 별도 설정없이 어디서든 API를 모킹할 수 있고, 모킹된 데이터가 실제 서버로부터 받은 응답값처럼 동작하게 들어 모킹에 대한 신뢰성을 확보하고자 했습니다. 더 자세한 설명을 위해, 안드로이드 프로젝트 구조를 들여다 보며 설명드리겠습니다. 아래 [그림 2]는 일반적인 안드로이드 앱 개발 구조입니다.
안드로이드 앱 개발을 처음 접해본 분들에게는 조금 생소할 수도 있는데요. 위의 프로젝트 구조와 동작 방식을 잘 모르셔도, 사용자의 입력 및 이벤트에 해당하는 Click
에 가까워질 수록 상위 레이어에 해당하며, 멀어질 수록 Server
에 가까운 Core 레이어로 보시면 됩니다.
그런데 왜 Core 단계에서 모킹을 수행 했을까요? 반대로 생각하면 그렇게 설계한 이유가 보입니다. 상위 레이어에서 모킹을 수행하면, API 연동을 할 때 마다 고려해줘야 할 부분들이 생기기 때문인데요. 상위 레이어에 해당하는 ViewModel과 Repository 모듈의 경우 잦은 수정이 발생하는 레이어이므로, 서버 API 연동 작업을 해둬도 개발 과정에서 높은 확률로 수정, 삭제, 생성이 일어날 수 있습니다. 그렇기 때문에 일반적인 클라이언트 개발 단에서 거의 수정할 일이 없는, 매우 코어(Core)한 영역에서 모킹이 이루어지면 편리합니다. 이 경우, 클라이언트 개발자가 본인이 작업하는 범위와 영향도에서 벗어난 영역에서 모킹이 수행된 것이기에, 그 응답값을 보다 신뢰할 수 있을 것이라 생각했습니다.
다행히 OkHttp 라이브러리의 경우, OkHttp의 Interceptor라는 기능을 제공하고 해당 Interceptor에서 모든 요청과 응답을 제어 할 수 있습니다.
위의 [그림 3]처럼 Interceptor로 특정 API를 모킹하는 상황을 예로 설명드리겠습니다. 먼저 모킹 도구를 사용하기 이전에, 모킹 예정인 API 데이터를 간단히 맵(map) 형태의 구조체에 JSON 형식으로 저장해둡니다. 모바일과 같은 클라이언트 단에서 요청(Request)이 들어올 때 구조체에서 일치하는 패턴을 발견하면, 응답(Response)을 변경하고자 하는 값으로 바꿔서 들어온 곳으로 다시 전달하는 방식입니다.
아래 적힌 코드는 [그림 3] 속 수도 코드(Pseudo Code)를 코틀린으로 구현해 본 Interceptor를 통한 API 모킹 코드의 예시입니다. 이때 모킹하고자 하는 데이터를 changedResponseData
변수에 추가하면, 언제 어디서든 간단하게 API를 모킹할 수 있습니다.
object ApiResponseMockingInterceptor : Interceptor {
internal val changedResponseData = ConcurrentHashMap<String, InterceptorBody>()
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val key = request.toMockingKey() // "${path}?${query}"
val response = chain.proceed(request)
val changeResponse = changedResponseData[key]?.body
val responseCode = changedResponseData[key]?.code ?: response.code
// 저장된 Mocking Map에 응답이 있을 경우, 해당 데이터로 모킹
return if (!changeResponse.isNullOrEmpty()) {
response.newBuilder()
.code(responseCode)
.body(changeResponse.toResponseBody(response.body?.contentType()))
.build()
} else {
response
}
}
}
2-2. 언제 어디서 누구든 접근 가능한 UX
요구사항 수집 당시, 인터뷰 대상을 클라이언트 개발자로만 한정하지 않은 이유도 모킹 값을 필요로 하는 모든 직군이 제우스를 사용할 수 있게하기 위함이었습니다. 제우스는 개발자 뿐만 아니라 기획자와 QA 담당자도 API의 응답 값을 조정하며 원하는 값을 화면에 노출시켜 볼 수 있도록 하였습니다. 위 그림처럼 테스트 환경에서는 어느 화면에서든 제우스에 접근할 수 있도록 구현하였습니다.
[그림 4]는 제우스의 사용 여부를 확인할 수 있는 화면입니다. 테스트 앱에서 제우스를 실행하면, 화면 상단에 번개 모양의 버튼(⚡️)이 표시됩니다. 해당 버튼을 클릭하면 누구나 손쉽게 데이터를 조작할 수 있는 제우스의 API 모킹 화면에 진입할 수 있습니다. 제우스는 실제로 비개발 직군에서도 높은 사용률을 자랑합니다. 제우스는 화면에서 응답값을 변경했을 때 어떻게 보이는지 매우 직관적이고 간단하게 확인할 수 있어, 많은 분들이 이용해주고 계신 것 같습니다.
2-3. 간단한 모킹 절차
제우스는 별도의 프로그램 설치가 필요 없이 테스트 앱 내에서 바로 응답값을 변경할 수 있도록 하였습니다. 기존 호출된 API 목록과 응답값을 UI에 표시함으로써, 사용자가 원하는 값을 편리하게 수정할 수 있도록 구성하여 도구의 사용성을 향상시켰습니다. 앞서 보여드린 [그림 4]의 화면 상단에 있는 번개 모양 버튼(⚡️)을 클릭하면, [그림 5]처럼 API 목록 화면으로 이동합니다. 해당 목록에서 모킹하고자 하는 API를 선택한 후, 상단의 ‘RESPONSE’ 탭에서 테스트하고자 하는 응답값을 자유롭게 수정할 수 있습니다.
⛳️ Step3. 기능 및 테스트
아래 영상은 제우스를 사용하는 화면을 녹화한 것입니다. 하나씩 살펴보겠습니다.
3-1. 정상 동작 화면
[그림 6]은 이미지 검색 API를 통해 <가디언즈 오브 갤럭시>에 등장하는 Groot 이미지 목록을 검색한 화면을 예시로 구성한 것입니다. 정상적으로 작동할 경우, 여러 장의 이미지가 노출되어 화면을 제대로 그릴 수 있습니다. 그러나 서버에 이미지가 없거나 서버가 오류 응답을 주는 경우도 존재합니다. 이러한 오류 응답 상황을 어떻게 연출할 수 있는지 아래에서 설명드리겠습니다.
3-2. 오류 화면: 검색 결과, 서버 응답이 없을 경우
우선, [그림 7]에서 검색 결과가 없는 경우부터 설명드리겠습니다. 실제로 서버에는 앞서 보여드린 Groot 이미지 목록이 존재하지만, 제우스를 통해 API를 모킹하여 검색 결과 이미지가 존재하지 않는 상황을 연출할 수 있습니다. 또한, 서버 응답 오류 상황도 만들어볼 수 있습니다. 실제 서버가 정상적으로 응답값을 전달하고 있지만, 모킹을 통해 마치 서버에 오류가 발생한 것처럼 상황을 바꿀 수도 있습니다.
3-3. 화면 노출 문구/값 확인하기
클라이언트 개발을 하다 보면, 실제 서버에서 매우 긴 문구 또는 큰 금액의 값이 내려오는 경우를 종종 마주합니다. 이러한 예외적인 경우는 화면에 정상적으로 노출되는지를 사전에 반드시 확인해야 합니다. 이러한 경우, API 모킹 도구인 제우스를 활용하면 화면에 노출되는 값을 미리 확인할 수 있습니다. 만약 [그림 8]과 같은 확인 작업이 없다면, 긴 문구나 큰 금액을 보여줘야 하는 상황에서 화면 UI가 깨져 보이거나, 잘못된 값이 노출될 위험이 있습니다. 제우스를 통해 이를 사전에 대비하여 운영 환경에서 발생할 수 있는 화면 오류를 미리 점검할 수 있습니다.
3-4. 서버 응답 지연 테스트하기
비즈니스 로직에 따라 서버에서 응답이 오는 시간동안 별도의 로딩을 보여주기도 합니다. 하지만 일반적으로 테스트 서버는 운영 서버보다 트래픽이 적기 때문에 굉장히 빠른 속도로 응답을 줍니다. 이러한 경우 로딩 화면을 만들었지만, 시각적으로 확인하기도 전에 로딩이 완료되는 경우가 많습니다.
실제 상황은 어떨까요? 앱을 이용하는 사용자들의 환경은 매우 다양합니다. 제우스에서는 운영 환경을 가정하여 응답이 늦어지는 상황을 연출할 수 있습니다. 아래 [그림 9]의 예시는 실제 서버에서 100ms 내외로 응답이 있었지만, 클라이언트에서 인위적으로 5000ms로 설정하여 응답 시간을 늘린 경우입니다. 이를 통해 로딩 화면이나 설정된 타임아웃(Timeout) 등이 정상 작동하는지 확인할 수 있습니다.
번외 모음집
제가 제우스를 개발하면서 ‘이건 미리 알았다면 좋았을 텐데!‘라고 생각한 2가지 내용을 모아 보았습니다. 제우스와 같은 API모킹 도구를 직접 개발하려는 분들은 꼭 한 번 읽어보시기 바랍니다.
🗒️ 번외 1. OkHttp의 Interceptor 동작 프로세스
[그림 10]을 보시면, OkHttp의 Interceptor는 체인 형태로 동작합니다. 따라서 addInterceptor
를 추가하는 순서에 따라 나중에 등록된 Interceptor가 먼저 등록된 Interceptor에 영향을 미칠 수 있습니다. 이 점을 인지하시고 이미 사용 중인 Interceptor와의 순서를 고려하여 개발해야 합니다.
예를 들어, HttpLoggingInterceptor를 함께 사용한다고 가정해보겠습니다. 만약 Interceptor를 MockingInterceptor
> HttpLoggingInterceptor
순으로 등록하면, 안드로이드 스튜디오의 Logcat 창을 통해 모킹된 응답 결과가 출력됩니다. 반대로 HttpLoggingInterceptor
> MockingInterceptor
순으로 등록하면, Logcat에는 서버에서 수신한 데이터가 출력되고 애플리케이션에는 모킹된 결과를 얻을 수 있습니다.
🗒️ 번외2 - Production 소스코드에서 제외시키기
API의 요청과 응답을 모킹하는 기능은 악의적인 공격자가 가장 원할 기능 중 하나일 것입니다. 따라서 운영 환경에서는 해당 기능이 동작하지 않도록 막는 것이 필수적입니다.
바람직하지 않은 예시
if (BuildConfig.DEBUG) {
//.. 블라블라
}
일반적인 경우, 위에서 설명한 대로 Debug 빌드인지 Release 빌드인지를 기준으로 특정 코드가 동작하지 않도록 설정할 수 있습니다. 하지만 진정으로 악의적인 사용자는 디컴파일 후 BuildConfig.DEBUG
코드를 찾아보고 테스트용 코드를 활성화할 수 있습니다. 이처럼 개발 편의성으로 만들어둔 기능이 운영 환경에서 활성화될 위험이 있습니다.
이러한 보안 취약점을 초래할 수 있는 개발 기능의 경우, debug
폴더를 별도로 구성하여 release
빌드 시에 해당 소스 코드가 포함되지 않도록 해야 합니다. 정리하자면, 운영 환경에서는 보안적인 이유로 모킹 기능을 비활성화하는 것이 중요합니다. 앞서 말씀드린 별도 폴더 구성 외에도, 필요에 따라서는 Production 빌드에서 모킹 기능이 동작하지 않도록 적절한 보안 조치를 취해야 합니다.
마무리하며
이번 글에서는 OkHttp의 Interceptor를 활용하여 API 요청과 응답을 가로채고 원하는 값을 테스트하는 방법을 살펴보았습니다. 이를 통해 앱 개발 및 테스트 과정에서 제우스의 API 모킹 기능을 활용하여 특정 상황을 시뮬레이션하고 테스트할 수 있었으며, 클라이언트 개발자는 물론, 서버 개발자, 기획자, QA 담당자의 업무 생산성을 크게 향상시킬 수 있었습니다. 모두의 소중한 시간을 절약하면서도 빠르고 안전하게 앱 개발을 할 수 있도록 도와준 셈입니다. 😊
사실 제우스를 개발하고 소개할 당시에는 제가 Droid Knights 2024라는 국내 최대 규모 안드로이드 커뮤니티 행사에서 발표를 하고, 카카오뱅크 기술 블로그에 관련 글을 작성하게 되리라고는 생각하지 못했습니다. 하지만 자료를 준비하는 과정에서, 지금은 사내 구성원들이 잘 사용하고 있는 모킹 도구의 개발 과정을 돌아보며 저도 많은 것을 되돌아볼 수 있었습니다.
은행에서 클라이언트 개발자로 근무하며 모킹 도구를 통해 업무 효율화를 이룬 사례처럼, 여러분만의 업무 효율화를 위한 도구를 개발하는 데 제 경험이 좋은 영감이 되었기를 바랍니다. 제우스의 개발 여정을 읽어주셔서 감사합니다! ⚡